Emil 3e2376ac26 Add feedback loop: SQLite-backed rating system + MCP tools + GUI Training tab
New crate: soundgen-feedback
- FeedbackDB with SQLite (rusqlite, bundled)
- CRUD: add, update_rating, get_all, get_unrated, top_rated, delete
- search_similar: keyword-based similarity for few-shot reference
- export_jsonl: export rated examples for fine-tuning
- 9 tests

MCP server: 5 tools (was 3)
- generate_batch: generate multiple sounds by name, store in DB
- get_reference_sounds: search DB for highly-rated similar sounds
- render_sound: now returns reference examples from DB in response
- --db flag to specify feedback database path

GUI: Training tab
- Open/create feedback DB (file dialog)
- Generate batch dialog (enter names, output dir)
- Sound list with star rating (1-5), feedback text, play button
- Export dataset as JSONL
- Stats display (total/rated/avg rating)

OpenCode config updated with --db feedback.db path
2026-06-21 22:40:07 +03:00

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

# 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:

cargo run -p soundgen-mcp -- --presets-dir presets

Configure in Claude Desktop / MCP client:

{
  "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

{
  "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)

{
  "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)

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

S
Description
Rust sound generator with MCP integration and AI training functionallity
Readme MIT
157 KiB
Languages
Rust 100%