sine(turns: number) → number
Phase in TURNS (1.0 = one full cycle). Any real; wrapped here.
| arg | type | description |
|---|
| turns | number | |
seed(s: number) → void
| arg | type | description |
|---|
| s | number | |
buf(frames: number) → buffer
Buffers
| arg | type | description |
|---|
| frames | number | |
frames(seconds: number, rate: number) → number
| arg | type | description |
|---|
| seconds | number | |
| rate | number | |
add(dst: buffer, i: number, v: number) → void
| arg | type | description |
|---|
| dst | buffer | |
| i | number | |
| v | number | |
addWrapped(dst: buffer, frames: number, offset: number, src: buffer, srcFrames: number, amp: number) → void
Add `src` into `dst` at `offset`, WRAPPING past the end. This is what lets a
struck note fall near the end of a loop and have its tail arrive at the head
of the same loop, which is the only way a decaying note survives looping.
| arg | type | description |
|---|
| dst | buffer | |
| frames | number | |
| offset | number | |
| src | buffer | |
| srcFrames | number | |
| amp | number | |
env(frames: number, rate: number, points: { { number } }) → buffer
Envelopes. `points` is {{seconds, value}, ...} in ascending time,
linearly interpolated, held at the last value.
| arg | type | description |
|---|
| frames | number | |
| rate | number | |
| points | { { number } } | |
decayEnv(frames: number, rate: number, attackMs: number, tau: number) → buffer
An exponential decay from 1 to ~0 over `tau` seconds, with a short attack so
nothing starts on a step (a step is a click).
| arg | type | description |
|---|
| frames | number | |
| rate | number | |
| attackMs | number | |
| tau | number | |
addTone(dst: buffer, frames: number, rate: number, opts: ToneOpts) → void
A pitched tone: a harmonic stack that may glide, with optional vibrato.
| arg | type | description |
|---|
| dst | buffer | |
| frames | number | |
| rate | number | |
| opts | ToneOpts | |
addLoopPartial(dst: buffer, frames: number, cycles: number, amp: number, phase: number, ampCycles: number?, ampDepth: number?) → void
Loop-coherent partial: `cycles` is a WHOLE number of cycles across the whole
buffer, so the result joins to itself exactly. This is the drone's building
block, and the reason the bed does not click at the seam.
| arg | type | description |
|---|
| dst | buffer | |
| frames | number | |
| cycles | number | |
| amp | number | |
| phase | number | |
| ampCycles | number? | |
| ampDepth | number? | |
addNoise(dst: buffer, frames: number, amp: number, env: buffer?) → void
| arg | type | description |
|---|
| dst | buffer | |
| frames | number | |
| amp | number | |
| env | buffer? | |
addPluck(dst: buffer, frames: number, rate: number, freq: number, amp: number, damp: number, env: buffer?) → void
Karplus-Strong: a struck string. `damp` under 1 shortens the decay; a low
`damp` is a muted, woody strike, a high one rings like wire.
| arg | type | description |
|---|
| dst | buffer | |
| frames | number | |
| rate | number | |
| freq | number | |
| amp | number | |
| damp | number | |
| env | buffer? | |
mulEnv(b: buffer, frames: number, env: buffer) → void
Multiply a signal by an envelope, in place.
| arg | type | description |
|---|
| b | buffer | |
| frames | number | |
| env | buffer | |
mix(dst: buffer, dstFrames: number, src: buffer, srcFrames: number, offset: number, amp: number) → void
Add one buffer into another at a sample offset, without wrapping.
| arg | type | description |
|---|
| dst | buffer | |
| dstFrames | number | |
| src | buffer | |
| srcFrames | number | |
| offset | number | |
| amp | number | |
lowpassSweep(b: buffer, frames: number, rate: number, c0: number, c1: number, poles: number?) → void
Filters. In place, mono.
A lowpass whose cutoff travels from `c0` to `c1` across the clip. This is
what a whoomph is: broadband at the strike, closing to a rumble.
| arg | type | description |
|---|
| b | buffer | |
| frames | number | |
| rate | number | |
| c0 | number | |
| c1 | number | |
| poles | number? | |
lowpass(b: buffer, frames: number, rate: number, cutoff: number, poles: number?) → void
| arg | type | description |
|---|
| b | buffer | |
| frames | number | |
| rate | number | |
| cutoff | number | |
| poles | number? | |
highpass(b: buffer, frames: number, rate: number, cutoff: number) → void
| arg | type | description |
|---|
| b | buffer | |
| frames | number | |
| rate | number | |
| cutoff | number | |
bandpass(b: buffer, frames: number, rate: number, f0: number, f1: number, q: number) → void
Chamberlin state-variable bandpass with a swept centre frequency. `q` is
resonance: 1 is broad, 12 is a whistle.
| arg | type | description |
|---|
| b | buffer | |
| frames | number | |
| rate | number | |
| f0 | number | |
| f1 | number | |
| q | number | |
lowpassCircular(b: buffer, frames: number, rate: number, cutoff: number) → void
The circular forms: run the filter once to converge its state on the wrap
point, then again for the output. What comes out is continuous ACROSS the
loop join, which a one-pass filter never is.
| arg | type | description |
|---|
| b | buffer | |
| frames | number | |
| rate | number | |
| cutoff | number | |
bandpassCircular(b: buffer, frames: number, rate: number, fc: number, q: number) → void
| arg | type | description |
|---|
| b | buffer | |
| frames | number | |
| rate | number | |
| fc | number | |
| q | number | |
reverb(b: buffer, frames: number, rate: number, decay: number, mix: number) → void
A small Schroeder reverb — four combs into two allpasses. The fen is flat,
open and wet, so this is a short, dark tail rather than a hall.
| arg | type | description |
|---|
| b | buffer | |
| frames | number | |
| rate | number | |
| decay | number | |
| mix | number | |
resample(src: buffer, srcFrames: number, step: number) → void
Read `src` at `step` input samples per output sample, linearly interpolated.
A step of 2 halves the sample rate; a step of 1.72 halves it AND drops the
pitch by a minor third, which is how one voice becomes two.
| arg | type | description |
|---|
| src | buffer | |
| srcFrames | number | |
| step | number | |
gain(b: buffer, frames: number, g: number) → void
| arg | type | description |
|---|
| b | buffer | |
| frames | number | |
| g | number | |
peak(b: buffer, frames: number) → number
| arg | type | description |
|---|
| b | buffer | |
| frames | number | |
rms(b: buffer, frames: number) → number
| arg | type | description |
|---|
| b | buffer | |
| frames | number | |
normalize(b: buffer, frames: number, target: number) → void
| arg | type | description |
|---|
| b | buffer | |
| frames | number | |
| target | number | |
softClip(b: buffer, frames: number) → void
Soft clip, so a layered bake never hands the encoder a sample past unity.
| arg | type | description |
|---|
| b | buffer | |
| frames | number | |
fadeEdges(b: buffer, frames: number, rate: number, headMs: number, tailMs: number) → void
Fade the head and tail of a ONE-SHOT so it neither clicks in nor out. Never
use this on a loop: a loop must join at full amplitude.
| arg | type | description |
|---|
| b | buffer | |
| frames | number | |
| rate | number | |
| headMs | number | |
| tailMs | number | |
removeDc(b: buffer, frames: number) → void
Remove any DC the filters left, which otherwise eats headroom silently.
| arg | type | description |
|---|
| b | buffer | |
| frames | number | |
seamError(b: buffer, frames: number) → number
How big the step ACROSS the loop join is, measured in the units the signal
itself steps in: the wrap step divided by the rms of every interior
sample-to-sample step. A continuous waveform still steps at the join — by
one sample of its own slope — so the honest question is not "is the step
zero" but "is it the size of every other step". Around 1 is seamless; a
click reads in the tens or hundreds.
| arg | type | description |
|---|
| b | buffer | |
| frames | number | |