# Soundgen — 8-bit синтезатор на Rust + LLM-интеграция ## Статус: Фазы 1-4 завершены | Параметр | Значение | |---|---| | Язык | Rust | | Игра | Rust + Vulkan (custom engine) | | Генераторы | Свои, с нуля | | GUI | egui (фаза 3) | | MCP | Да, через `rmcp` (официальный Rust MCP SDK) | | Звуки | SFX, UI sounds, Ambient | | Генерация | Build time → WAV-ассеты | | Лицензия | MIT | ## Архитектура Cargo workspace ``` soundgen/ ├── Cargo.toml # [workspace], shared deps ├── LICENSE # MIT ├── README.md │ ├── crates/ │ ├── soundgen-core/ # чистый движок синтеза, без I/O │ │ └── src/ │ │ ├── lib.rs # реэкспорты │ │ ├── generator.rs # trait Generator, Voice, Param │ │ ├── voice/ │ │ │ ├── pulse.rs # PulseChannel (duty 12.5/25/50/75%) │ │ │ ├── triangle.rs # TriangleChannel (32-step) │ │ │ ├── noise.rs # NoiseChannel (LFSR white/periodic) │ │ │ ├── dpcm.rs # (фаза 2) │ │ │ ├── wavetable.rs # (фаза 2, GB wave-style) │ │ │ └── fm.rs # (фаза 2, 4-op FM) │ │ ├── effect/ │ │ │ ├── envelope.rs # ADSR │ │ │ ├── sweep.rs # freq sweep (linear/exponential) │ │ │ ├── vibrato.rs # LFO (фаза 2) │ │ │ └── filter.rs # biquad lowpass/highpass (свой) │ │ ├── automation.rs # FrequencyAutomation, Arpeggiator │ │ └── mixer.rs # Mixer: N голосов → стерео │ │ │ ├── soundgen-fmt/ # декларативный формат + пресеты │ │ └── src/ │ │ ├── lib.rs │ │ ├── spec.rs # SoundSpec (serde JSON) │ │ └── preset.rs # PresetRegistry, built-in presets │ │ │ ├── soundgen-io/ # I/O слой │ │ └── src/ │ │ ├── lib.rs │ │ ├── wav.rs # hound: WAV writer (16/24-bit) │ │ └── realtime.rs # cpal (фаза 3) │ │ │ ├── soundgen-seq/ # секвенсер (фаза 2) │ │ └── src/ │ │ ├── pattern.rs # Pattern, Row, Note │ │ ├── song.rs # Song, Track │ │ └── sequencer.rs # Sequencer │ │ │ ├── soundgen-cli/ # CLI binary │ │ └── src/ │ │ ├── main.rs │ │ └── commands/ │ │ ├── gen.rs # gen --preset jump --out ... │ │ ├── render.rs # render song.json --out ... │ │ └── list.rs # list-presets │ │ │ ├── soundgen-mcp/ # MCP server для LLM (фаза 2) │ │ └── src/ │ │ ├── lib.rs │ │ ├── server.rs # rmcp ServerHandler impl │ │ └── tools.rs # generate_sfx, list_presets, render │ │ │ └── soundgen-gui/ # egui GUI (фаза 3) │ └── src/ │ ├── main.rs │ ├── keyboard.rs # виртуальная клавиатура │ ├── channel_panel.rs # регуляторы каналов │ └── preset_browser.rs # браузер пресетов │ ├── presets/ # встроенная библиотека (JSON) │ ├── sfx/ │ │ ├── jump.json │ │ ├── explosion.json │ │ ├── coin.json │ │ ├── laser.json │ │ ├── hit.json │ │ └── powerup.json │ ├── ui/ │ │ ├── click.json │ │ ├── hover.json │ │ ├── confirm.json │ │ └── error.json │ └── ambient/ # (фаза 2) │ ├── wind.json │ ├── rain.json │ └── drone.json │ ├── examples/ │ ├── play_melody.rs # фаза 1: pulse+triangle+noise → WAV │ ├── generate_sfx.rs # фаза 1: SFX из кода │ └── render_song.rs # фаза 2: song.json → WAV │ └── tests/ └── integration.rs # рендер WAV, проверка RMS/длительности ``` ## Декларативный формат (JSON) — LLM-friendly Пример `presets/sfx/jump.json`: ```json { "name": "jump", "duration": 0.3, "sample_rate": 44100, "channels": [ { "type": "pulse", "duty": 50, "frequency": { "start": 200, "end": 800, "curve": "exponential" }, "envelope": { "attack": 0.01, "decay": 0.15, "sustain": 0.0, "release": 0.14 }, "volume": 0.7 } ] } ``` Пример `presets/sfx/explosion.json`: ```json { "name": "explosion", "duration": 0.8, "channels": [ { "type": "noise", "mode": "white", "filter": { "type": "lowpass", "cutoff": { "start": 2000, "end": 200, "curve": "exponential" } }, "envelope": { "attack": 0.005, "decay": 0.7, "sustain": 0.0, "release": 0.095 }, "volume": 0.9 } ] } ``` LLM генерирует такой JSON → CLI/MCP рендерит → WAV-ассет готов. ## MCP server — tools для LLM Сервер на `rmcp` (stdio transport), expose 3 tool'а: | Tool | Параметры | Возвращает | |---|---|---| | `list_presets` | `category?: "sfx"\|"ui"\|"ambient"` | JSON-список пресетов с описаниями | | `generate_sfx` | `preset: string`, `params?: object`, `out_path: string` | Путь к WAV + метаданные (длительность, размер) | | `render_sound` | `spec: object` (полный SoundSpec JSON), `out_path: string` | Путь к WAV + метаданные | Workflow LLM при разработке игры: 1. LLM вызывает `list_presets` → видит доступные звуки 2. LLM вызывает `generate_sfx { preset: "jump", out_path: "assets/sfx/jump.wav" }` → WAV создан 3. Для кастомного звука: LLM пишет JSON-spec и вызывает `render_sound { spec: {...}, out_path: "assets/sfx/laser.wav" }` 4. LLM пишет Rust-код игры, загружающий WAV через `hound`/`rodio` ## Зависимости ```toml # workspace [workspace.dependencies] hound = "0.5" # WAV I/O serde = { version = "1", features = ["derive"] } serde_json = "1" # soundgen-io (фаза 3) cpal = "0.15" # soundgen-mcp (фаза 2) rmcp = "1.7" tokio = { version = "1", features = ["full"] } # soundgen-cli clap = { version = "4", features = ["derive"] } # soundgen-gui (фаза 3) eframe = "0.27" egui = "0.27" ``` ## Фазы разработки ### Фаза 1 — Core + CLI + Пресеты (MVP) Цель: работающая библиотека, CLI, и набор готовых пресетов для SFX/UI. - **soundgen-core**: `Generator`/`Voice`/`Param` трейты, `PulseChannel`, `TriangleChannel`, `NoiseChannel`, `Envelope` (ADSR), `Sweep` (linear/exponential), `FrequencyAutomation`, `Mixer`, простые biquad filters (lowpass/highpass — свой код) - **soundgen-fmt**: `SoundSpec` (serde), `PresetRegistry` (загрузка JSON из `presets/`) - **soundgen-io**: `wav::write(path, samples, sr, bit_depth)` через `hound` - **soundgen-cli**: `gen --preset [--param k=v]... --out `, `gen --from --out `, `list-presets [--category sfx|ui|ambient]` - **Пресеты**: jump, explosion, coin, laser, hit, powerup (SFX); click, hover, confirm, error (UI) - **Examples**: `play_melody`, `generate_sfx` - **Тесты**: unit на duty cycle/частоту/envelope; интеграционный — рендер WAV, проверка длительности и RMS ### Фаза 2 — Extended synthesis + Sequencer + MCP Цель: расширенный синтез, секвенсер для мелодий, MCP-сервер для LLM. - **soundgen-core**: `DpcmChannel`, `WavetableChannel`, `FmChannel`, `Vibrato` (LFO) - **soundgen-seq**: `Pattern`, `Song`, `Sequencer` (tempo, rows, note triggers) - **soundgen-mcp**: rmcp-сервер, tools: `list_presets`, `generate_sfx`, `render_sound` - **Ambient пресеты**: wind (filtered noise + slow LFO), rain (noise + highpass), drone (low freq sustained) - **CLI**: `render song.json --out music.wav` - **Example**: `render_song` ### Фаза 3 — GUI (egui) Цель: интерактивный редактор для человека. - egui app: виртуальная клавиатура (мышью/клавишами), панель каналов (duty, freq, envelope), браузер пресетов, pattern editor - cpal realtime playback (ring buffer) - Сохранение/загрузка проектов (JSON) ### Фаза 4 — Advanced (опционально) - NES-authentic: нелинейный DAC, DPCM corruption, hardware-accurate mixing - VST плагин через `nih-plug` - Runtime library для прямой интеграции в Rust+Vulkan игру ## Ключевые принципы - **Чистый core без I/O** — рендеринг в `Vec` в тестах без звуковой карты - **Sample-accurate tick** — `Voice::tick() -> f32`, `Mixer` собирает буфер - **Без allocations в hot path** — генераторы работают с `&mut self`, без `Vec` в audio thread - **JSON-first для LLM** — каждый звук описывается JSON, LLM генерирует JSON естественно - **Пресеты как data, не code** — JSON-файлы в `presets/`, расширяемые без перекомпиляции