tono-core 1.8.0

The pure, headless audio engine behind tono: synthesis-graph DSL, DSP, deterministic renderer, instruments, songs, and analysis — no I/O, no transport.
Documentation

Hear it

Recognizable classics rebuilt from scratch — the ▶ links play right on GitHub:

Sound Play The trick
retro-coin B5 grace note into a held E6 — the interval is the sound
jump-8bit exponential square sweep, gone at sustain 0
waka per-note pitch slides alternating up/down — the chomp drawn into the notes
nokia-tune 13 notes of Gran Vals on the Karplus-Strong pluck
deep-note supersaw tracks gliding from a scattered cluster onto a five-octave D chord
river-flows-in-you a complete piano piece — 800 notes on the sampled grand
band-demo four instruments, one groove, mixed on the stereo bus

More in docs/examples/audio/ — game-ready BGM loops and ambient beds, all deterministic renders.

What it is

A sound in tono is a symbolic graph (oscillators → envelopes → filters → effects → mix); rendering it is a pure function of (graph, seed, sample_rate) → byte-identical audio. The same document sounds identical offline, streamed in real time, or played as an instrument — so audio becomes something you can test, diff, cache, and ship without asset files.

  • Instruments & songs — a polyphonic sequencer, an 11-preset factory bank, a GM drum kit, catalog voices (piano, bass, guitar, strings…), and a fluent Song API that compiles to a plain document.
  • Game runtime — an embeddable Engine/Mixer with live DSP buses (reverb/EQ/compressor inserts + sends), voice management (polyphony caps, priority stealing), and interactive music (beat-quantized section switches, intensity layers, stingers on the downbeat).
  • Zero-asset SFX — a Patch renders infinite per-instance variations from named parameters (hardness, size, …). Impact sounds that scale with collision force, no sample library.
  • An ear for critique — LUFS/peak/spectral stats plus spectrogram + waveform images per render, so "does it sound right?" becomes numbers and pictures.
  • SoundFont sampler — point the sampler voice at any free GM bank for real recorded instruments, still deterministic.

Quick start

cargo install tono       # the `tono render` CLI
cargo add tono-core      # …or the engine as a library (games, tools)

Render a document and look at what came back:

tono render docs/examples/blip.json -o out/
#   out/blip.wav          the audio            (--format wav|flac|ogg)
#   out/blip.png          the spectrogram      ← look at this
#   out/blip_wave.png     the waveform         ← and this
#   out/blip.stats.json   peak / RMS / LUFS / spectral / transient analysis

That loop — emit a doc, render, read the images + stats, refine — is all a human or an agent needs to author sound by inspection.

Sampled instruments need a free General MIDI SoundFont once (FluidR3 GM, GeneralUser GS): wave: "sampler", sf2: "/path/to/gm.sf2", sf2_preset: 0.

One engine, five faces

Every face renders the same SoundDoc byte-identically — pick the surface that fits your workflow. New to the codebase? Start with the architecture & getting-started guide.

Face What it is Entry point
CLI render → audio + spectrogram + stats tono render f.json -o out/
Rust library the engine embedded in a game or tool cargo add tono-core
Python bindings live engine + deterministic numpy renders make python
Pattern station Tauri app: FL-style step grid, live audio, undo make desktop
Playground hear Rust snippets through the speakers make play EXAMPLE=band

Use it from Rust

use tono_core::dsl::SoundDoc;
use tono_core::render;

let doc: SoundDoc = serde_json::from_str(r#"{
    "name": "blip", "duration": 0.3, "engine": 4,
    "root": { "type": "mul", "inputs": [
        { "type": "sine", "freq": 880 },
        { "type": "env", "a": 0.002, "d": 0.08, "s": 0.0, "r": 0.05 } ] }
}"#)?;

let samples: Vec<f32> = render::render(&doc); // byte-identical every run

Game audio, in a few lines each

use tono_core::runtime::{Engine, Mixer, Priority};

// Voice management: a budget + priorities, and a bullet-hell scene stays clean.
let mut engine = Engine::new(48_000);
engine.set_max_voices(32);
engine.play_looping_prioritized(music, Priority::CRITICAL); // never stolen
engine.play_prioritized(explosion, Priority::HIGH);
engine.play(footstep);                                      // stolen first

// Live DSP buses: inserts and sends, reusing the streaming effect kernels.
let mut mixer = Mixer::new_at(48_000);
let sfx = mixer.bus("sfx");
let reverb = mixer.fx_bus("reverb", vec![reverb_node])?;
mixer.set_send(sfx, reverb, 0.9);
use tono_core::adaptive::{AdaptiveMusic, Quantize};

// Interactive music: react on the beat, like a film score.
let mut music = AdaptiveMusic::new(48_000);
music.set_tempo(120.0, 4);
music.add_section("explore", &explore_doc);
let battle = music.add_section("battle", &battle_doc);
music.transition_to(battle, Quantize::Bar);  // swaps on the next bar
music.set_intensity(0.9);                    // stems swell with the action
music.stinger_at(&boss_hit, Quantize::Bar);  // lands on the downbeat

Use it from Python

The same engine as a Python extension (crates/tono-py) — live procedural audio for Pygame / Ren'Py / Arcade, or deterministic numpy renders for anything:

import tono

engine = tono.Engine(48000)            # owns the output stream + render thread
engine.drumkit().note_on(36, 1.0)      # kick — the audio thread never touches Python
engine.instrument("warm_lead").note_on("C4", 0.9)

impact = engine.load_patch(open("impact.patch.json").read())
impact.trigger(hardness=0.8, size=0.3) # zero baked WAVs

# …or pull deterministic numpy arrays (testable in CI):
buf = tono.Patch(open("impact.patch.json").read()).render(hardness=0.7)

Build it with make python (maturin; abi3 wheels for 3.9+ build in CI).

Determinism

The real-time streaming path is byte-identical to an offline bounce (verified by a fuzzer in CI), and byte-changing kernel upgrades are gated behind a document engine revision, so old sounds never change. A golden corpus pins representative renders in CI. Render the same document twice, get the same bytes — which is what makes audio testable, diffable, and cacheable.

Learn more

A personal note

Every line of code in tono was written by AI. I didn't write a single line myself — my part was direction: deciding what to build, and holding the project to the same practices and standards I use in the projects where I do still write the code.

If tono helps you in any way — as a game-audio tool, a reference, or just a kick-start on your own project — that makes me genuinely happy. The tokens are already spent; the least they can do is be useful to you too.

⚠️ Notice — versions below 2.0.0

I intend to follow SemVer, but be realistic about what this project is: AI-generated code. Diffs are large, and every release below 2.0.0 may contain breaking changes despite my best intentions.

Once the tool has proven itself, I will cut a 2.0.0 release — that is the point where I start reviewing the code in detail and contributing to it directly. 2.0.0 will be tagged by me, by hand — it is the one release an AI agent is not allowed to cut. Until then, assume that anything below 2.0.0 has not been fully reviewed by me and may contain bugs or security issues I haven't caught.

Use at your own risk.

License

MIT — permissive, no warranty.