Skip to content

Repository files navigation

Code Synthesizer

数式ベースの独自 DSL で音を定義し、編集内容を演奏中に反映できる Windows x86_64 向け Rust 製 VST3 シンセサイザーです。

README で定義していた最初の MVP は実装済みです。

  • VST3 Instrument / Stereo Output
  • 64 voice のポリフォニック SynthEngine
  • MIDI Note On/Off、CC、Pitch Bend、Pressure、Sustain、Program Change
  • MathSynth から引き継いだ Monaco / WebView2 UI と Editor / Play モード
  • Lexer / Parser / Validator / Cranelift JIT backend を備えた DSL
  • 非 RT スレッドでのコンパイルと Audio block 境界での hot reload
  • phase lock 付き live 波形、プリセット、プレビュー鍵盤
  • p.* ユーザーパラメータ、MIDI CC link、配置可能な knob / slider / toggle、VST3 automation
  • fn note / optional fn filter、user function、true stereo、3 domain persistent state、transactional RingBuf
  • ソース、パラメータ値、Play レイアウト、画面モードを含む plugin state 保存・復元

ギャラリー

必要環境

  • Windows x86_64
  • 64-bit VST3 対応 DAW
  • Rust 1.94 以上
  • Node.js ^20.19.0 または >=22.12.0
  • npm
  • Microsoft Edge WebView2 Runtime (システムのWebViewでOk つまりLinux非対応)

このリポジトリでは Rust 1.97.1、Node.js 24.6.0、npm 11.5.1 で検証しています。

ビルド

通常はリポジトリ直下で次だけを実行します。初回は UI 依存関係も自動で導入され、release VST3 バンドルまで生成されます。

.\build.cmd

PowerShell から直接実行する場合:

.\build.ps1                 # release bundle
.\build.ps1 -Dev            # debug bundle
.\build.ps1 -Test           # fmt + test + clippy + release bundle
.\build.ps1 -Install        # ユーザーVST3フォルダーへもコピー
.\build.ps1 -Smoke          # 5sのUIテスト

生成先:

target/bundled/Code Synthesizer.vst3/
  Contents/
    x86_64-win/Code Synthesizer.vst3
    Resources/moduleinfo.json

-Install を使わない場合は、生成されたバンドルディレクトリを次へコピーします。

$vst3Dir = "$env:LOCALAPPDATA\Programs\Common\VST3"
New-Item -ItemType Directory -Force $vst3Dir | Out-Null
Copy-Item -Recurse -Force "target\bundled\Code Synthesizer.vst3" $vst3Dir

こんなふうに。

その後、DAW で VST3 を再スキャンし、Instrument トラックへ Code Synthesizer を追加してください。

Version と Release

version はルート Cargo.toml[workspace.package] にある version だけを更新します。この値が全 crate、VST3 metadata、UI のタイトルと見出しに自動反映されます。

main または master へ push した際、GitHub Actions が直前の Cargo.toml と version を比較します。version が変わった場合だけ全テストと release buildを実行し、v{version} tag、GitHub Release、Windows x64 VST3 zipを生成します。通常のコード変更や、version以外の Cargo.toml 変更ではReleaseは作成されません。

使い方

プラグイン画面の Editor モードで DSL を編集すると、260 ms の debounce 後に自動コンパイルされます。成功したプログラムだけが Audio block 境界で DSP へ渡されるため、編集中に構文エラーがあっても直前の正常な音は継続します。Monaco には DSL 補完、snippet、hover、引数ヒント、コンパイラ marker、候補名の quick fix が入っています。

p.attack_ms = param(8.33, 1, 200, 0.1)
p.release_s = param(1.5, 0.1, 6, 0.01)
p.gain = param(1, 0, 1.5, 0.01, 7)

fn note(in, p) -> out {
    attack = min(in.t * 1000 / p.attack_ms, 1)
    release = exp(-9 * in.l / p.release_s)
    out.wave = p.gain * in.s * in.vol * in.mexpr * attack * release
        * sin(TAU * in.freq * in.t)
    out.pan = in.midi_pan
    out.l_limit = p.release_s
}

p.name = param(default, min, max, step, cc_link?) は最初の fn より前に宣言します。最初の4値は必須、MIDI CC番号だけ省略可能です。Play modeとDAWにはautomation可能なparameterとして宣言順で公開されます。

Play の Guide では記法、意味の作り方、配置、automationを確認できます。Arrange 中はドラッグで移動、右下ハンドルでリサイズします。位置・大きさ・表示形式はプロジェクトに保存されます。右クリックでは reset、値の copy/paste、knob / slider / toggle の切替を行えます。

Parameter Guide はparameter、通常関数、qualified bundle、persistent scalar、true stereo、RingBuf、post-mix filterをコメントで解説する実行可能なサンプルです。v2ではengineがMIDI gain/panを暗黙適用しないため、in.volin.mexprin.midi_pan はDSLで明示的に使用します。

Factory Presetsは32種類です。Basic、Lead、Pluck、Bass、Keys、Pad、Ensemble、Percussionなどの分類を内包したProgram menuから選択できます。基本音作り向けのBasic SynthSuperSawに加え、Wavefold LeadGlass PluckResonant BellsLo-Fi KeysDeep SpaceTape EchoPhase MotionMetal Drumを収録しています。新しい音色ではKarplus-Strong、modal resonator、filter、multitap delay、reverb、waveshaperなどの標準DSPを実用例として使っています。

Preset panelのNameへ名前を入力してSaveすると、現在のコード、parameter値、Play layoutをCustom分類へ保存できます。同名の保存は更新になり、CustomのLoadでは保存時の値と配置を復元します。Custom libraryはWebViewのlocal storageへ保存されます。

下部の鍵盤とPCキーはプレビュー用で、通常の DAW MIDI 入力も同じ SynthEngine へ送られます。Wave は発音中に audio callback の実出力を表示し、無音時はコンパイル済み式の静的プレビューへ切り替わります。モニターは余剰サンプルから連続フレームを切り出すため、右端で波形を循環させません。(循環させてひどい目にあった)

DSL リファレンス

programには fn note(in, p) -> out がちょうど1つ必要で、fn filter(in, p) -> out は省略できます。通常関数は fn name(in) -> out と定義し、result = name(input_bundle) と呼びます。forward referenceは可能、recursionはcompile errorです。

localはstatement順に再代入できます。比較はf32の0/1を返します。#// は行コメントです。2s500ms250us の時間suffixと k/m/u/g のSI suffixを利用できます。

persistent scalarは初期値必須です。voice はVoice slot、note(channel,note)global はplugin instanceで共有します。RingBufはsample単位でtransactionalに動作します。

f32 voice phase = 0
f32 note energy = 0
f32 global master = 1
RingBuf<f32, 180ms> global delay

完全一致するstorage schemaはhot reload後も維持され、sample rate変更時は全stateをresetします。ローカル、演算、storage、RingBuf容量には言語上の固定上限を設けず、負荷が大きい場合は Warning を表示します。parameterだけはVST automation slotとの同期のため最大32個です。

主要入力:

名前 内容
in.t / in.l / in.s Note On秒、Note Off秒、Velocity
in.freq / in.note / in.ch 周波数、MIDI note、MIDI channel
in.bend / in.bend_st Pitch Bend -1..1、半音換算値
in.mw / in.vol / in.midi_pan / in.mexpr CC 1 / 7 / 10 / 11
in.sustain / pressure fields Sustain / Channel / Poly Pressure
in.program / in.cc(n) Program Change、任意 CC
in.sr / transport fields Sample rate / DAW transport
in.voice / in.rand Voice index / Voice固有乱数

出力:

名前 内容
out.wave / out.pan mono Voice波形 / pan
out.wave_l / out.wave_r true stereo Voice波形
out.l_limit Note Off後にVoiceを終了する秒数。必須

定数は TAUPIEPHI、演算子は + - * / % ^ を使用できます。

関数:

sin cos tan asin acos atan atan2 sinh cosh
exp sqrt cbrt abs tanh ln log log2 log10
floor ceil round fract sign
min max pow mod clamp mix step smoothstep select
mtof ftom dbtoa atodb cent_ratio semitone_ratio
saw square pulse triangle noise

RingBufのpeek / peek_linear / len / duration、Biquad係数、Window、Filter、Delay、Physical Modeling、Modulation、Distortion、Dynamics、Smoothing、Stereo、Reverbを標準搭載しています。全signature、単位、bundle field、state domainはDSL Standard Libraryを参照してください。state付きDSPはcall siteごとにAudio thread外でmemoryを準備し、noteではVoice単位、filterではglobalに動作します。

構成

DAW
 └─ VST3 Adapter
     ├─ Processor: Audio / Event / SynthEngine
     ├─ Controller: Parameters / Automation / State
     └─ View: WebView2 / Monaco / IPC
                    │
                    └─ DSL Compiler
                         │ lock-free queue
                         ▼
SynthEngine ─ Voice Engine ─ Cranelift JIT Runtime
crates/
  synth-core/   固定 voice、MIDI state、stereo DSP、program/parameter/waveform共有
  synth-dsl/    lexer、parser、validation、Cranelift IR/JIT backend
  synth-vst3/   VST3 component/controller/view/factory
  synth-ui/     WebView2、IPC、preset、waveform preview
ui/             Monaco ベース UI
presets/        DSL プリセット
xtask/          VST3 バンドル生成

VST3 ホストには Single Component として公開し、同じインスタンス内で Processor と Controller の状態を共有します。内部の責務と Audio/UI thread の境界は分離しています。

RT-safe 方針

Audio callback 内では次を行いません。

  • heap allocation / deallocation
  • Mutex lock
  • filesystem、WebView、JSON、DSL parsing
  • program の破棄や重い初期化

VoiceとNote-domain slotは固定容量です。UI thread がCraneliftでネイティブコード化し、persistent stateを準備した ProgramInstance をbounded lock-free queueへpublishします。Audio threadはblock先頭でpointerを交換し、1 sampleでは生成済みnote/filter関数と固定state descriptorだけを使用します。旧programと実行メモリは別queueへ返し、UI thread側で破棄します。p.* の値とoscilloscope ring bufferはatomicな固定容量storageで共有します。

えとせとら

npm run build --prefix ui
npm audit --prefix ui --audit-level=high
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo xtask bundle --release

WebView2 だけを DAW 外で確認する場合:

cargo run -p synth-ui --example ui-smoke

VST3 の UI lifecycle、WebView2 生成、asset 配信、Monaco 起動結果は次へ記録されます。

%LOCALAPPDATA%\Code Synthesizer\ui.log

CODE_SYNTH_UI_DEVTOOLS=1 を設定してから DAW を起動すると、エディター表示時に WebView2 DevTools も開きます。

wiwiwi

standalone Web版と、semantic analyzerを直接利用するさらに高度なEditor refactoringは今後の拡張候補です。DSLとDSPコアはUI/VST3から独立しているため、Web版でも同じcompiler/runtimeを再利用できます。

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages