builtins

Stereo

Stereo signal operations. Akkado tracks channel count (Mono vs Stereo) on every

Stereo signal operations. Akkado tracks channel count (Mono vs Stereo) on every signal; mono DSP ops applied to a stereo signal auto-lift — a single instruction runs twice at dispatch with independent per-channel state. No more manually duplicating a chain for L and R.

stereo

Create a stereo signal from mono or two separate signals.

ParamTypeDescription
LsignalMono signal (duplicated to both channels), or left channel if R given
RsignalRight channel (optional)
// Duplicate a mono signal into stereo
osc("saw", 220) |> stereo() |> out(%)

// Build a stereo image from two oscillators
stereo(osc("saw", 218), osc("saw", 222)) |> out(%)

mono

Sum-to-mono downmix of a stereo signal: out = (L + R) * 0.5.

ParamTypeDescription
sigstereoStereo signal to downmix

Standard sum-to-mono convention — correlated content stays at 0 dB; uncorrelated content sums at roughly -3 dB RMS.

// Downmix a stereo reverb tail before sidechain detection
wet = src |> stereo() |> freeverb(%, 0.8, 0.5)
env = wet |> mono() |> env_follower(%)

Calling mono() on a mono signal is a compile-time error — call it only on stereo signals. (When passed a function instead of a signal, mono() falls back to the monophonic voice-manager form: mono(instrument).)


left, right

Channel extraction from a stereo signal.

s = stereo(osc("sin", 220), osc("sin", 330))
left(s)   // → Mono
right(s)  // → Mono

pan

Panning with equal-power (constant-power) law.

SignatureBehaviour
pan(mono, pos)Position a mono signal in the stereo field. Emits PAN.
pan(stereo, pos)Balance an already-stereo signal. Emits PAN_STEREO.

Position is -1 (hard left) … 0 (centre, -3 dB) … +1 (hard right).

// Mono → stereo pan
osc("saw", 220) |> pan(%, lfo(0.25)) |> out(%)

// Stereo balance on a stereo bus
stereo(osc("saw", 218), osc("saw", 222))
  |> pan(%, 0.3)
  |> out(%)

The stereo form is DAW-style balance, not re-panning each channel. At pos = -1, the right channel is silenced and the left passes through at unity; at pos = 0, both channels are scaled by cos(π/4) ≈ 0.707.


width

Stereo width control using mid/side processing internally.

// Narrow to mono (width=0), original (1), wide (>1)
stereo_sig |> width(%, 1.4) |> out(%)

ms_encode, ms_decode

Mid/side processing — encode stereo to M/S, process, decode back.

// M/S processing: boost sides (wider stereo) while preserving centre
stereo_sig
  |> ms_encode(%)
  |> % * stereo(1.0, 2.0)   // scale mid 1.0, side 2.0
  |> ms_decode(%)
  |> out(%)

pingpong

True stereo ping-pong delay where echoes cross between L and R.

ParamTypeDescription
sigstereoStereo input
timesignalDelay time (seconds)
fbsignalFeedback (0..1)
widthsignalPan width (0 = centre, 1 = full ping-pong). Optional.
osc("saw", 110)
  |> stereo()
  |> pingpong(%, 0.375, 0.6)
  |> out(%)

Auto-lift

Any mono DSP op (lp, hp, delay, freeverb, saturate, …) applied to a stereo signal auto-lifts: the compiler emits one instruction flagged STEREO_INPUT, and the VM runs the op twice at dispatch — once per channel — with independent per-channel DSP state.

// One instruction per effect; VM runs each op twice at dispatch.
osc("saw", 220)
  |> stereo()
  |> lp(%, 500, 0.7)          // stereo lowpass, independent filter state
  |> delay(%, 0.25, 0.5)      // stereo delay lines per channel
  |> freeverb(%, 0.85, 0.5)   // stereo reverb
  |> out(%)

Scalar / control-rate parameters (cutoff, Q, feedback, …) are shared between the L and R passes — if you want independent parameters per channel, split the chain manually with stereo(lp(left(s), cutL), lp(right(s), cutR)).