# Soundgen 8-bit sound synthesizer in Rust for game audio assets, with LLM integration via MCP. ## Features - **6 voice types**: pulse (NES duty cycles), triangle, noise (LFSR), DPCM samples, wavetable (Game Boy wave), FM (2-operator) - **Effects**: ADSR envelope, frequency sweep (linear/exponential), biquad filter (lowpass/highpass), vibrato - **JSON-first**: every sound is a `SoundSpec` JSON object — LLMs generate JSON, CLI/MCP render to WAV - **Presets as data**: 13 built-in presets in `presets/{sfx,ui,ambient}/` — extend without recompilation - **MCP server**: LLMs can call `list_presets`, `generate_sfx`, `render_sound` as tools - **Sequencer**: pattern-based song playback (JSON format) - **GUI**: egui editor with virtual keyboard, preset browser, channel controls, sequencer - **Runtime library**: NES-authentic nonlinear DAC + SoundBank for game embedding - **No allocations in audio hot path**: `tick() -> f32`, `&mut self` ## Quick Start ```bash # List available presets cargo run --bin soundgen -- list-presets # Generate a sound from a preset cargo run --bin soundgen -- gen jump --out assets/jump.wav # Generate with parameter override cargo run --bin soundgen -- gen explosion --out assets/explosion.wav --param volume=0.95 # Render from a custom JSON spec cargo run --bin soundgen -- render presets/sfx/laser.json --out laser.wav # Render a song (sequencer) cargo run --bin soundgen -- render-song song.json --out music.wav # Launch the GUI editor cargo run -p soundgen-gui ``` ## MCP Server (for LLM integration) Run the MCP server on stdio: ```bash cargo run -p soundgen-mcp -- --presets-dir presets ``` Configure in Claude Desktop / MCP client: ```json { "mcpServers": { "soundgen": { "command": "/path/to/soundgen-mcp", "args": ["--presets-dir", "/path/to/presets"] } } } ``` LLM workflow: 1. `list_presets` → see available sounds 2. `generate_sfx { preset: "jump", out_path: "assets/jump.wav" }` → WAV created 3. `render_sound { spec: {...}, out_path: "assets/custom.wav" }` → custom sound ## SoundSpec JSON Format ```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 } ] } ``` Channel types: `pulse`, `triangle`, `noise` ## Song JSON Format (Sequencer) ```json { "bpm": 120, "rows_per_beat": 4, "tracks": [ { "type": "pulse", "duty": 50, "volume": 0.4 } ], "patterns": [ { "rows": [ { "notes": [{"frequency": 440}] }, {"notes": [null]} ] } ], "pattern_order": [0] } ``` ## Architecture Cargo workspace: | Crate | Purpose | |---|---| | `soundgen-core` | Synthesis engine (generators, effects, mixer). No I/O. | | `soundgen-fmt` | `SoundSpec` JSON schema + `PresetRegistry` | | `soundgen-io` | WAV writer (`hound`) + playback (subprocess) | | `soundgen-seq` | Sequencer: patterns, songs | | `soundgen-cli` | `gen`, `render`, `render-song`, `list-presets` | | `soundgen-mcp` | MCP server for LLM tool-use | | `soundgen-gui` | egui GUI editor | | `soundgen-runtime` | NES-authentic DAC + `SoundBank` for game embedding | ## Runtime Library (for game integration) ```rust use soundgen_runtime::SoundBank; // Load all presets at init time let bank = SoundBank::load_dir(std::path::Path::new("presets"))?; // Play by name (zero-allocation, returns pre-rendered buffer) let (samples, sample_rate) = bank.get("jump").unwrap(); // Pitch-shifted variant let (pitched, sr) = bank.get_pitched("jump", 1.5)?; ``` ## Built-in Presets **SFX**: jump, explosion, coin, laser, hit, powerup **UI**: click, hover, confirm, error **Ambient**: wind, rain, drone ## License MIT