synth
The DSP bench Helsinki Life's sounds are computed on. It renders mono f32 tracks out of arithmetic - oscillator stacks, filtered noise, envelopes, pitch names - and encodes them into the ZAUD payload a `.soundClip` stores. No network, no host file, no service: it runs headless an…
synth
The DSP bench Helsinki Life's sounds are computed on. It renders mono f32
tracks out of arithmetic - oscillator stacks, filtered noise, envelopes,
pitch names - and encodes them into the ZAUD payload a .soundClip stores.
No network, no host file, no service: it runs headless and costs only CPU.
Consumed by game.sound.bank, which holds one recipe per sound in the game.
Tracks wrap, and that is the point
Synth.track(seconds, rate) is a fixed-length buffer, and every generator
writes wrapped: a note starting near the end continues at the head. A
track rendered this way is one period of an infinite periodic signal, so
its last frame runs into its first with no step - whatever frequencies it
holds and however long the tails are. That is what makes the looping beds
seamless, and clipRef:loopSeam() reads the result back.
What it offers
| Call | What it makes |
|---|---|
Synth.track(seconds, rate?) | An empty track. RATE_SFX is 48 kHz, RATE_BED 24 kHz. |
Synth.tone(tr, start, dur, freq, amp, opts) | One note. opts.wave picks a partial stack; glide, vibrato and detune are there too. |
Synth.noise(tr, start, dur, amp, opts) | A noise burst through a low-pass, high-pass or swept band-pass. |
Synth.chord(tr, start, dur, freqs, amp, opts) | The same note shape at several frequencies. |
Synth.hz("F#3") | A pitch name as a frequency. |
Synth.fromPcm(pcm, srcRate, channels, dstRate) | Audio from elsewhere, folded to mono and resampled through a pre-filter. |
Synth.loopify(tr, loopSeconds, fadeSeconds) | A seamless loop cut out of a longer take by equal-power crossfade. |
Synth.blend / gain / normalize / peak / rms | Mixing and finishing. |
Synth.encode(tr, opts?) | The ZAUD payload; pcm by default. |
wave names: sine, tri, square, saw, reed, celesta, pluck,
bell, gong, horn. A partial carrying a decay above 1 fades faster
than the note body, which is what makes a struck bell sound struck.
local Synth = require("game.sound.synth")
local tr = Synth.track(1.0, Synth.RATE_SFX)
Synth.tone(tr, 0, 0.9, Synth.hz("A5"), 0.5, { wave = "gong", decay = 3.2 })
Synth.normalize(tr, 0.88)
local zaud = Synth.encode(tr)
Scoped to this part · feeds back into the world's score.