Initial release: 8-bit sound synthesizer with LLM/MCP integration

Soundgen is a Rust workspace for generating 8-bit/chiptune sound effects,
UI sounds, and ambient textures for game audio assets. It features JSON-first
sound specs, an MCP server for LLM tool-use, an egui GUI editor, and a
runtime library with NES-authentic DAC emulation.

Features:
- 6 voice types: pulse, triangle, noise, DPCM, wavetable, FM
- Effects: ADSR envelope, frequency sweep, biquad filter, vibrato
- 13 built-in presets (SFX/UI/ambient) as JSON data files
- MCP server: list_presets, generate_sfx, render_sound tools
- Pattern-based sequencer (JSON song format)
- egui GUI: virtual keyboard, preset browser, channel editor, undo/redo
- Runtime: NES nonlinear DAC + SoundBank for game embedding
- 90 tests, 0 warnings

Crates:
- soundgen-core: synthesis engine (no I/O)
- soundgen-fmt: SoundSpec JSON schema + PresetRegistry
- soundgen-io: WAV writer + audio playback
- soundgen-seq: sequencer (patterns, songs)
- soundgen-cli: gen/render/render-song/list-presets
- soundgen-mcp: MCP server for LLM integration
- soundgen-gui: egui editor
- soundgen-runtime: NES DAC + SoundBank
This commit is contained in:
Emil
2026-06-21 22:07:05 +03:00
commit c7d6c40683
74 changed files with 13345 additions and 0 deletions
+237
View File
@@ -0,0 +1,237 @@
# 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 <name> [--param k=v]... --out <path>`, `gen --from <spec.json> --out <path>`, `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<f32>` в тестах без звуковой карты
- **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/`, расширяемые без перекомпиляции