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
52 lines
1.5 KiB
Rust
52 lines
1.5 KiB
Rust
//! Example: generate SFX sounds from JSON presets and write to WAV.
|
|
|
|
use soundgen_fmt::{render_spec, PresetCategory, PresetRegistry};
|
|
use soundgen_io::write_wav;
|
|
use std::path::Path;
|
|
|
|
fn main() {
|
|
let presets_dir = Path::new("presets");
|
|
|
|
let registry = PresetRegistry::load_dir(presets_dir).unwrap_or_else(|e| {
|
|
eprintln!(
|
|
"Warning: could not load presets from {}: {}",
|
|
presets_dir.display(),
|
|
e
|
|
);
|
|
PresetRegistry::new()
|
|
});
|
|
|
|
if registry.is_empty() {
|
|
eprintln!("No presets found. Run from the project root directory.");
|
|
std::process::exit(1);
|
|
}
|
|
|
|
// Generate all SFX presets
|
|
let sfx = registry.list(Some(PresetCategory::Sfx));
|
|
let ui = registry.list(Some(PresetCategory::Ui));
|
|
|
|
let out_dir = Path::new("sfx_output");
|
|
std::fs::create_dir_all(out_dir).expect("create output dir");
|
|
|
|
for entry in sfx.iter().chain(ui.iter()) {
|
|
let samples = render_spec(&entry.spec);
|
|
let filename = format!("{}.wav", entry.name);
|
|
let out_path = out_dir.join(&filename);
|
|
write_wav(&out_path, &samples, entry.spec.sample_rate)
|
|
.unwrap_or_else(|e| eprintln!("Failed to write {}: {}", out_path.display(), e));
|
|
eprintln!(
|
|
" {} → {} ({} samples, {:.2}s)",
|
|
entry.name,
|
|
out_path.display(),
|
|
samples.len() / 2,
|
|
entry.spec.duration
|
|
);
|
|
}
|
|
|
|
eprintln!(
|
|
"\nGenerated {} sounds to {}",
|
|
sfx.len() + ui.len(),
|
|
out_dir.display()
|
|
);
|
|
}
|