K

Helpers

Pure math, curve, easing, mixing, and state helpers.

These inline and need no construction. They import from @msh/device-sdk/device/assembly and are real-time safe, so they run in process. The curve set is also importable in paint.ts from @msh/device-sdk/device/assembly/paint, so DSP and canvas draw identical curves.

Math

FunctionReturns
dbToGain(db)Decibels to linear gain.
gainToDb(g)Linear gain to decibels.
tanhFast(x)Cheap tanh approximation for soft saturation.
softClip(x)Cubic soft clip (linear near zero, saturating toward ±2/3).
hardClip(x)Hard clip to [-1, 1].
evalPowerCurve(progress, curve)Vital-style power-curve shaper, curve in [-1, 1].

Curve, interpolation, easing

FunctionReturns
clamp01(x)Clamp to [0, 1].
clampf(x, lo, hi)Clamp to [lo, hi].
lerp(a, b, t)Linear interpolation (t unclamped).
unlerp(a, b, v)Where v sits in [a, b] as a 0..1 fraction.
smoothstep(t)Hermite smoothstep 3t²−2t³ (t clamped).
smootherstep(t)Perlin smootherstep 6t⁵−15t⁴+10t³ (t clamped).
shapePower(t, power)Power-curve shape (parity with the on-screen CurveEditor).
powerCurve(t, y0, y1, power)Interpolate y0y1 along the power-curve shape.
bias(t, b)Schlick bias: b in (0,1), 0.5 linear; pushes mass toward 0 or 1.
gain(t, g)Schlick gain: g in (0,1), 0.5 linear; S-curve or inverse-S.
easeInExpo(t) / easeOutExpo(t)Exponential ease-in / ease-out.
easeInOutSine(t) / easeInOutCubic(t)Sine / cubic ease-in-out.
catmullRom(y0, y1, y2, y3, t)Catmull-Rom cubic between y1 and y2 (the sweet spot for variable-rate buffer reads).
hermite(p0, m0, p1, m1, t)Cubic Hermite between p0, p1 with tangents m0, m1.
tanhApprox(x)Padé tanh, pure and cheap (for canvas paint, where tanhFast isn't available).
evalCurvePoints(xs, ys, powers, count, x)Evaluate a piecewise power curve from sorted control points.

CurveLut

Bake a curve once, then sample it O(1) per call with no pow() in the hot loop. Backed by a StaticArray<f32> of evenly spaced samples over x in [0, 1].

let lut: CurveLut;
prepare(sampleRate: f64, maxBlock: i32): void {
  lut = new CurveLut(256);
  lut.bakePower(0.5);
}
// in process: const y = lut.sample(x);
MethodDoes
new CurveLut(size)Allocate a LUT of size samples (min 2).
bakePower(power)Bake a single power curve (y rises 0→1).
bakeFn(fn)Bake from an arbitrary f(x), x in [0, 1].
bakePoints(xs, ys, powers, count)Bake a piecewise curve from control points.
sample(x)Sample at x in [0, 1], clamped and linearly interpolated.
data() / size()The backing array / its length.

Mixing

FunctionReturns
crossfade(dry, wet, amt)Linear crossfade: amt 0 → dry, 1 → wet.
mixParam(id?, name?)A ready-made 0..1 modulatable "Mix" param (percent knob).

Pair them: crossfade(dry, wet, ctx.modulatedParam(mixIdx, i)).

Persistent state

Pack and unpack m.state bytes without manual offset math. StateWriter writes a version tag first; StateReader reads it back so older saves keep loading. See Parameters for declaring state.

getState(): Uint8Array {
  const w = new StateWriter(1, 64);   // version 1, 64-byte worst case
  w.i32(this.steps);
  w.f32(this.rate);
  return w.bytes();
}
setState(bytes: Uint8Array): void {
  const r = new StateReader(bytes);
  this.steps = r.i32();
  if (r.version >= 1) this.rate = r.f32();
}
StateWriterDoes
new StateWriter(version, capacity)Allocate a buffer (writes the version tag).
u8(v) / i32(v) / f32(v) / f64(v)Append a field.
bytes()The bytes written so far (return from getState).
StateReaderDoes
new StateReader(bytes)Read the version tag (exposed as version).
u8() / i32() / f32() / f64()Pull the next field, in write order.
remaining()Bytes left unread (guard newer fields with it).

Notes

  • Use the Mathf namespace (Mathf.sqrt, Mathf.pow, Mathf.sin, ...) in hot loops. It takes and returns f32 directly, with no f64 round-trip.
  • Size fixed buffers as StaticArray<T> allocated in prepare. Release builds strip array bounds checks, so keep loop bounds derived from the sizes you allocated.

For audio DSP blocks see DSP blocks; for oscillators, envelopes, and meters see Synth blocks.