synth
The DSP bench the fen's audio is cut on. Mono f32 `buffer`s in, mono f32 `buffer`s out — the shape `audio.encodePcm` and `audio.decode` both speak. Nothing here touches an asset, an entity or the mixer.
sound.synth
The DSP bench the fen's audio is cut on. Mono f32 buffers in, mono f32
buffers out — the shape audio.encodePcm and audio.decode both speak.
Nothing here touches an asset, an entity or the mixer.
Two jobs.
Synthesis
An envelope over a waveform. osc, noise, lowpass, highpass,
bandpass, ad, decay, gain, normalize, saturate, removeDc,
fadeIn, fadeOut, blend. Every UI answer, the toll on a lost yowe and
the swell under a tide step is made of these. It costs nothing and it never
fails for credit or network reasons, which is why the sounds the interface
depends on are all made this way.
rng(seed) is deterministic, so a bake is the same every time it runs — a
clip that differs between two machines is a clip whose checksum differs for
no reason anybody can see.
Seaming a loop
A generated music track ends where it ends. Looping it puts a step in the waveform, and a step in a waveform is a click, every cycle.
loopSeam(src, sr, loopSeconds, crossSeconds, start?) folds the material that
follows the loop point back over the head under an equal-power crossfade.
The output's last sample is the source's, and its first is what the source
itself played next — so the join is a join the source already made.
bestLoop(...) tries several offsets and keeps the quietest join, because a
loop point that lands on a drum hit reproduces that hit every cycle in a place
it does not belong, and which offset that happens at is a property of the
material rather than something a caller can know.
seam(b, sr) measures it. The honest question is not "is the step zero" — no
real signal has that — but "is the step bigger than the steps the material
makes anyway", and ratio answers it in one number.
Measured on this game's beds, cutting 22 seconds out of a 45-second generated score:
| seam ratio | |
|---|---|
| a plain cut | 39.1 — an audible click every cycle |
loopSeam at the same offset | 1.29 |
bestLoop across ten offsets | 0.12 |
Everything the game loops is baked through bestLoop and refused above a
ratio of 3.
Rate and channels
resample (low-passes first when coming down, so nothing folds back into the
band as a whistle), monoFromPcm, decodeMono.
Measurement
stats(b, sr) — peak, rms, dc, frames, seconds. An rms of zero is the
failure that otherwise ships silently: a clip that decodes, reports a
duration, and is silence.
Scoped to this part · feeds back into the world's score.