Files
Soundgen/crates/soundgen-fmt/examples/render_song.rs
T
Emil c7d6c40683 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
2026-06-21 22:07:05 +03:00

113 lines
3.9 KiB
Rust

//! Example: render a song (JSON) to WAV using the sequencer.
//!
//! Song JSON format:
//! ```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]
//! }
//! ```
use soundgen_io::write_wav;
use soundgen_seq::{render_song, Song};
fn main() {
// Build a simple chiptune melody programmatically
let song_json = r#"{
"bpm": 130,
"rows_per_beat": 4,
"sample_rate": 44100,
"tracks": [
{
"type": "pulse", "duty": 50,
"volume": 0.35, "pan": -0.2,
"envelope": { "attack": 0.005, "decay": 0.03, "sustain": 0.6, "release": 0.05 }
},
{
"type": "triangle",
"volume": 0.4, "pan": 0.3,
"envelope": { "attack": 0.01, "decay": 0.05, "sustain": 0.5, "release": 0.08 }
},
{
"type": "noise", "mode": "white", "frequency": 8000,
"volume": 0.15, "pan": 0.0,
"envelope": { "attack": 0.0, "decay": 0.02, "sustain": 0.0, "release": 0.02 }
}
],
"patterns": [
{
"rows": [
{ "notes": [
{"frequency": 523.25, "velocity": 1.0},
{"frequency": 261.63, "velocity": 1.0},
{"frequency": 8000, "velocity": 1.0}
]},
{ "notes": [null, null, null] },
{ "notes": [
{"frequency": 659.25, "velocity": 1.0},
{"frequency": 261.63, "velocity": 1.0},
null
]},
{ "notes": [null, null, {"frequency": 8000, "velocity": 1.0}] },
{ "notes": [
{"frequency": 783.99, "velocity": 1.0},
{"frequency": 329.63, "velocity": 1.0},
null
]},
{ "notes": [null, null, null] },
{ "notes": [
{"frequency": 1046.50, "velocity": 1.0},
{"frequency": 329.63, "velocity": 1.0},
{"frequency": 8000, "velocity": 1.0}
]},
{ "notes": [null, null, null] },
{ "notes": [
{"frequency": 659.25, "velocity": 1.0},
{"frequency": 196.00, "velocity": 1.0},
null
]},
{ "notes": [null, null, {"frequency": 8000, "velocity": 1.0}] },
{ "notes": [
{"frequency": 587.33, "velocity": 1.0},
{"frequency": 196.00, "velocity": 1.0},
null
]},
{ "notes": [null, null, null] },
{ "notes": [
{"frequency": 523.25, "velocity": 1.0},
{"frequency": 261.63, "velocity": 1.0},
{"frequency": 8000, "velocity": 1.0}
]},
{ "notes": [null, null, null] },
{ "notes": [null, null, null] },
{ "notes": [null, null, null] }
]
}
],
"pattern_order": [0, 0]
}"#;
let song: Song = serde_json::from_str(song_json).expect("parse song");
let samples = render_song(&song);
let out = std::path::Path::new("render_song.wav");
write_wav(out, &samples, song.sample_rate).expect("write WAV");
eprintln!(
"Wrote {} ({:.1}s, {} patterns, {} tracks)",
out.display(),
song.duration(),
song.pattern_order.len(),
song.tracks.len()
);
}