Skip to content

Audio path

Every Teensy Audio object from oscillator to SGTL5000, with mixer input numbers and the gains the firmware actually writes. Source: audioinit.h, toern.ino, toern_filter.ino, toern_synths.ino.

Engine constants

NameValueWhere
AUDIO_SAMPLE_RATE_EXACT44 117 Hz (Teensy default)All streams
TOERN_AUDIO_MEMORY_BLOCKS96 (~24 KB RAM2)AudioMemory()
MIX_BUS_HEADROOM1/3 ≈ 0.333 × GAIN 1–4 or 5–8 × ALLmixer1 / mixer2 every input
MIX_END_SAMPLES_GAIN0.50mixer_end in 0 and 1
MIX_END_SYNTH_GAIN0.60 × GAIN SYN × ALLmixer_end in 2
GAIN_40.80mixersynth_end in 0, 2, 3
GAIN_1 / GAIN_2 / GAIN_30.4 / 0.8 / 0.4Defined, unused
Default channel volume16 / 16 → amp gain 1.0amp1–8, 11, 13, 14

AudioMixer4 defaults every unwritten input to 1.0. The firmware only lists gains it actually sets.


Sample voices 1–8

One lane per channel. Bitcrush is before the state-variable filter. Voices 3 and 4 have no reverb object — they jump from the filter mixer into mixer1.

StageObjectGain / range
Playersound1–8 (AudioPlayArrayResmp)setAmplitude(vel/127 × neo-slider) 0–1. Velocity is also sent into the sampler note event.
Envelopeenvelope1–8ADSR from PARAM sliders (0–32). Mute / note-off calls noteOff().
Bitcrushbitcrusher1–8Slider 0 = bypass (16-bit @ sample rate). Slider 32 = 80% of old max ≈ 4-bit / ~9.6 kHz (not 1-bit / 1 kHz).
Channel ampamp1–8map(channelVol, 0..16, 0..1). At BITC > 0 the amp is ducked toward 0.6 as crush rises (crushCompGain).
Filterfilter1–8 SVFHCUT: LP path, freq 281 Hz–10 kHz. LCUT > 0: switch mixer to HP, freq 40 Hz–2.5 kHz. RES 0.7–5.0.
Filter mixerfiltermixerNDefault in0=1, in1=0, in2=0 (LP). Smooth 32-step crossfade. LCUT uses in2=1.
Reverb sendfreeverb1,2,5–8Room 0–1 from RVRB; damping 0.01–0.42. RVRB 0 bypasses and clears delay tanks. No freeverb on 3 or 4.
Wet / dryfreeverbmixerNin0 wet 0 → 0.54; in3 dry 1.0 → 0.88 (send, not a hard crossfade). Off: wet 0, dry 1.
Bank mixmixer1 (ch1–4), mixer2 (ch5–8)Every input MIX_BUS_HEADROOM = 1/3 × VOL → GAIN (1–4 or 5–8) × ALL. Sized for ~3 full-level hits per bank at 10/10.

mixer1 inputs: 0 = ch1 verb mix, 1 = ch2 verb mix, 2 = ch3 dry filter, 3 = ch4 dry filter. mixer2: 0–3 = ch5–8 verb mixes.


Channel 11 — 3-voice poly

Three oscillators × three voices, pan-split into L/R mixers, then collapsed to mono for the shared bitcrush / filter / reverb chain. Leftover waveform11_1/2 objects still exist but are muted (gain 0) so they cannot leak into reverb while stopped.

StageGain notes
OscillatorsAlways amplitude 1.0. Mix loudness is the 0.33 pan/volume law, not the leftover waveform11_* objects.
Preset osc volumesPer-instrument volume[osc] 10–100 (BASS 100/65/35, etc.).
Ampamp11 = channelVol 0–16 → 0–1, same crush-comp as samples.
ReverbNo freeverbmixer. Wet is stacked on dry at synthmixer11. RVRB 0 zeros in0 and bypasses tanks.

Channels 13–14 — mono synths

No reverb (filter page 0 uses CENT in that slot). Two oscillators into a mixer, then a single envelope as VCA.


Summing buses

MixerInSourceGain
mixer10ch1 verb mix1/3 × GAIN 1–4 × ALL
1ch2 verb mix1/3 × GAIN 1–4 × ALL
2ch3 filter (no verb)1/3 × GAIN 1–4 × ALL
3ch4 filter (no verb)1/3 × GAIN 1–4 × ALL
mixer20ch5 verb mix1/3 × GAIN 5–8 × ALL
1ch6 verb mix1/3 × GAIN 5–8 × ALL
2ch7 verb mix1/3 × GAIN 5–8 × ALL
3ch8 verb mix1/3 × GAIN 5–8 × ALL
mixersynth_end0ch110.80
1—unset (default 1.0, nothing patched)
2ch130.80
3ch140.80
mixer_end0mixer10.50
1mixer20.50
2mixersynth_end0.60 × GAIN SYN × ALL
3audioInputAmp (monitor / pass-through)0 by default; see Input

Worst-case samples: 3 hot hits on a bank → mixer bus ≈ 1.0 at GAIN 1.0x/1.0x; both banks into mixer_end at 0.50 + 0.50 = 1.0 before synth (0.60) and monitor. VOL → GAIN 0.0x–2.0x scales 1–4, 5–8, SYN, and ALL (1.0x = this table). Four simultaneous full-level hits on one bank can clip — drop a GAIN knob or MIX_BUS_HEADROOM to 0.25 if that is a real pattern.

peakOutput listens to mixer_end for the LED-strip meter. It does not change the mix.


Preview (sample browser)

VOL → PREV is 0–50 → mixer gain 0–0.5 (RAM path 25% hotter so it matches SD). Independent of MAIN / LOUT / channelVol.


Input, record, monitor

Modemixer_end in3Notes
Idle play0Input not in the mix.
REC monitor0 … (1/3 × 0.50) = 0 … 0.167Mic: map MIC 0–63. Line: map L-IN 0–15. Mic + SPKR on → 0 (no feedback).
Preview cursor on row 11.0Direct pass-through; codec mic/line gain still applies.

Codec: inputSelect MIC or LINEIN. micGain 0–63 (0 on line). lineInLevel 0–15.


Stereo out and codec

VOL → 2-CH writes mixer_stereoL/R. Hardware: R is I2S DAC 0, L is DAC 1.

2-CHL in0 mainL in1 prevL in2 mixer1R in0 mainR in1 prevR in2 mixer2
OFF (0)110110
M+P (1)100010
L+R (2)001001

L+R taps mixer1 (voices 1–4) to left and mixer2 (5–8) to right, and mutes mixer_end on both sides — synths 11/13/14 and input monitor drop out of that mode. Stereo in 3 is always 0. VOL → GAIN 1–4 / 5–8 / ALL still apply because they live on mixer1/2; SYN does not in L+R.

CodecControlRange
Headphonesgtl5000.volume(GLOB.vol / 10)VOL MAIN 1–10 → 0.1–1.0
Line outlineOutLevel13–31 (SGTL: higher = quieter). Default 30.
HFCGraphic EQ treble only0 = DAP off. 10–250 step 10, then 256 → treble gain 0 … −1.0. Auto-volume / surround / bass enhance off.

What is not in the graph

  • Rows 9, 10, 12 — no voice objects.
  • Channels 3, 4, 13, 14 — no freeverb.
  • envelope11 — patched into mixer_waveform11 in2, but that input is held at 0. Poly envelopes are Senvelope1/2.
  • USB audio output is enabled. The two output mixers also feed usb1; the computer receives the same two-channel routing as the headphone and line outputs.
Clipping checklist: four sample hits on one bank; ch11 RVRB stacked on dry at 0.20 + 0.20; BITC + RVRB (crush before the tanks). Channel mute releases envelopes so tails do not sit in the pool.