Modulator Bank — manual

Eight independent CV sources running alongside the tracks — LFOs, envelopes, random walks, chaos, and a struck-spring resonator — plus two bank-wide modes (JustF rate-link and the Geode rhythmic engine).

1. Overview
2. The 10 Shapes
3. Rate Domain, Mode, Invert, Offset
4. JustF (rate link)
5. Geode (6-voice engine)
6. The Modulator Page UI
7. Routing the output
8. Teletype ops (MO.* / G.*)
9. Setup summary

Introduction

The Modulator bank is a set of 8 independent CV sources that run alongside the tracks. Each slot generates a continuous control signal — an LFO, an envelope, a random walk, a chaotic attractor, or a struck-spring resonator — and routes it to CV outputs, the internal modulation bus, or MIDI. The bank is not a track: it has no steps and no pattern. It is a panel of motion you assign wherever you need it.

On top of the per-slot shapes sit two bank-wide modes that retune the whole set at once: JustF (a Just Friends / ochd-style rate link, M1 master + harmonic spread) and Geode (a 6-voice Just-Friends rhythmic engine). They are mutually exclusive and toggle from the same page.

Everything here is grounded in the firmware as built — defaults, ranges, enum members, key bindings, and label strings come from the model (Modulator.h, GeodeConfig.h), the engine (ModulatorEngine.h, GeodeEngine.h), and the UI (ModulatorPage.cpp).

Prerequisites

Part 1 · Overview

The bank holds 8 modulator slots (M1..M8, CONFIG_MODULATOR_COUNT = 8). Each slot is one Modulator: a shape plus its parameters, evaluated every engine tick by ModulatorEngine into a single output in 0..127 (64 = 0V at the CV stage). That value is then routed.

A slot is always running — there are no steps to advance. What moves the output is the shape's own motion and the slot's gate behavior (free-run vs triggered/held).

Three things define a slot:

Two bank-wide modes. JustF (Shift+RATE) makes M1 a master rate and spreads M2..M8 harmonically; M2 hosts the global INTONE. Geode (Shift+SHAPE) turns the bank into a 6-voice rhythmic engine. They cannot both be active.

Part 2 · The 10 Shapes

#EnumLabelFamily
0SineSineLFO
1TriangleTriangleLFO
2SawUpSaw UpLFO
3SawDownSaw DownLFO
4SquareSquareLFO
5RandomRandomsample-and-hold
6ADSRADSRenvelope
7ChaosLorenzLorenzchaos
8ChaosLatoocarfianLatoocarfianchaos
9SpringSpringphysical resonator

The shape is changed by focusing SHAPE (F1) and turning the encoder.

LFO shapes (Sine / Triangle / Saw Up / Saw Down / Square)

A bipolar waveform scaled by depth and shifted by offset, with a phase control.

ParamFieldDefaultRangeEffect
RATE_rate0.05 Hz (Free)Free 0.01–16 Hz; Tempo 1/64..16 barsSpeed; domain-keyed
DEPTH_depth250–127Amplitude (127 = full swing)
PHASE_phase0°0–359°Phase offset
OFFSET_offset0−64..+63Baseline shift; sets rest level via floorValue()

Random

A sample-and-hold of a random value, slewed between samples. MODE decides when it samples (FREE = clocked, TRIG = on gate rising, HOLD = track-and-hold while gate high).

ParamFieldDefaultRangeEffect
RATE_rate0.05 HzFree/TempoSample clock
DEPTH_depth250–127Amplitude
SLEW_smooth100 ms0–5000 msGlide between samples
OFFSET_offset0−64..+63Baseline shift

ADSR

A gated attack/decay/sustain/release envelope. Unipolar: rests at the floor, rises toward the amplitude ceiling. Two pages.

Page 1 — SHAPE / ATTACK / DECAY / SUSTAIN / RELEASE:

ParamFieldDefaultRangeEffect
ATTACK_attack100 ms0–2000 msIdle → peak
DECAY_decay100 ms0–2000 msPeak → sustain
SUSTAIN_sustain1000–127Held level
RELEASE_release200 ms0–2000 msSustain → idle after gate falls

Page 2 — DEPTH (F3) / INVERT (F4) / OFFSET (F5):

ParamFieldDefaultRangeEffect
DEPTH (amplitude)_amplitude1270–127Envelope peak
INVERT_invertOFFon/offDucks from the top instead of rising
OFFSET (rest V)_offset0−64..+63Rest voltage; shown as volts

State machine: Idle → Attack → Decay → Sustain → Release → Idle, driven by the gate source; output mapped through unipolarOutput(env, amplitude, floor, invert).

Chaos (Lorenz / Latoocarfian)

A chaotic attractor sampled into CV. Two pages.

Param (p1)FieldRangeEffect
RATE_rateFree/TempoIntegration / step rate
DEPTH_depth0–127Amplitude
P1_attack (reused)0–2000 → 0..1Lorenz ρ 10..50 / Latoocarfian a,b 0.5..3.0
P2_decay (reused)0–2000 → 0..1Lorenz β 0.5..4.0 / Latoocarfian c,d 0.5..3.0

Page 2: SLEW (_smooth, 0–5000 ms) on F1 and OFFSET on F5. P1/P2 reuse the ADSR attack/decay storage, curve-mapped to the attractor constants.

Spring

A 3-mode inharmonic resonator (a struck "spring"). Two pages. Spring relabels the reused fields.

Label (p1)Reused fieldEffect
STRIKE_rateStrike (hit) clock
TENS_attackTension → resonant frequency ω₀ (0.3 × 40^(attack/2000) Hz)
RING_decayRing-out → damping ζ (0.0015 × 800^(1 − decay/2000))
CLANG_smoothClang → pickup amplitude weighting (smooth / 5000)

Page 2: PICKUP (_phase 0–4: Position / Velocity / Kinetic / Potential / Total), DEPTH (strike force 0.7 × depth/127), OFFSET. Frequency ratios [1.0, 2.76, 5.40], semi-implicit integration, tanh soft-saturation. See docs/spring-modulator-spec.md.

Part 3 · Rate Domain, Mode, Invert, Offset

These four are shape-orthogonal — they apply across families.

Rate domain (Free vs Tempo)

Rate is domain-keyed. Re-pressing RATE when it is focused cycles the domain (on shapes that show RATE on F2).

Mode (gate behavior)

ModeLabelBehavior
RunFREEFree-running; gate ignored
TrigTRIGReset phase to offset on gate rising (offset 0 = hard sync)
GateHOLDAdvance only while gate high; reset on rising

For Random, FREE/TRIG/HOLD select clocked S&H / gate-triggered S&H / track-and-hold.

Invert, Offset / floor, Gate source

Part 4 · JustF (Just Friends rate link)

JustF turns the bank into an ochd / Just Friends rate cluster: one master rate (M1) with the rest spread harmonically. It is not a shape — it is a runtime engine mode (_justfActive, _intone).

For ADSR followers, attack/decay/release spread by the same ratio per index; sustain is copied from M1; amplitude stays per-slot.

On the page. Shift+RATE toggles JustF (only when M1's rate domain is Free). On M2 the F2 cell relabels to INTONE (%+.2f) with a tiny derived-Hz readout beneath it; on M3..M8 the rate cell shows the read-only derived rate. Leaving the page bakes JustF: the derived rates (and ADSR times) are written into each follower, then JustF turns off. Tempo-domain slots are untouched.

Part 5 · Geode (6-voice rhythmic engine)

Geode reskins the bank into a Just-Friends-style rhythmic / stochastic clock engine (GeodeConfig + GeodeEngine). Separate subsystem, mutually exclusive with JustF. Shift+SHAPE toggles it; turning it on parks M2's depth to 0 so the run signal starts neutral.

The five globals (M1 page 2)

Stored 0..16383; intone and curve are bipolar (8192 = centre).

LabelFieldDefaultRangeEffect
TIME_time81920–16383Envelope base time, log-mapped, capped (≈ up to 4 s; MaxTimeNorm = 0.55)
INTONE_intone8192 (centre)0–16383 → −1..+1Voice time spread
RAMP_ramp81920–16383 → 0..1Attack/decay balance
CURVE_curve8192 (centre)STEP/LOG/LIN/EXPEnvelope shape
MODE_mode00–2TRANS / SUST / CYCLE

Per-voice params (M3..M8)

LabelFieldDefaultRangeEffect
DIVS_divs[v]11–64Clock divisor
REPEAT_repeats[v]0−1..255Burst count; −1 = INF
TUNE_tuneNum:_tuneDen(v+1):1num/denINTONE-ratio override (dormant — see note)
TIMEderived——The voice's gesture time in ms (read-out)
Tune is provisioned but dormant in this cut. The numerator/denominator fields are stored, defaulted (voice v → (v+1):1), and serialized, but not yet wired into editing or live engine evaluation. Setting 0/0 resets to the default ratio.

Part 6 · The Modulator Page UI

The header reads MOD n - MODULATOR (or ROUTING / GEODE). Track buttons T1..T8 select the slot. The layout is a left rolling scope and a right param list.

Sine LFO modulator page
Sine LFO: scope-left, divider, PhaseFlux param list (SHAPE/RATE/DEPTH/PHASE/OFFSET), RATE focused. CV1 assigned.

F-keys (per shape)

No step grid. The Modulator page does not handle step keys (S1..S16); they are ignored. Only the selected slot's track LED lights.

Encoder, re-press, Shift, Page+

ADSR modulator page 1
ADSR page 1: ATTACK/DECAY/SUSTAIN/RELEASE, DECAY focused. Pg 1/2 in the header.
ADSR modulator page 2
ADSR page 2: DEPTH (amplitude) / INVERT / OFFSET (rest voltage) in the F3–F5 slots; F1/F2 empty.
Random modulator page
Random: SHAPE focused; F4 is SLEW. Sampling is governed by MODE (FREE/TRIG/HOLD).
Chaos Lorenz modulator page
Chaos (Lorenz) page 1: RATE/DEPTH plus the two attractor params P1/P2, P1 focused.
Spring modulator page
Spring page 1: reused fields relabeled STRIKE / TENS / RING / CLANG, TENS focused.
JustF INTONE on M2
JustF active, M2: the RATE cell becomes INTONE (global spread); the derived Hz reads small beneath it.
JustF follower M5
JustF follower (M5): the RATE cell shows the read-only derived rate (6.00Hz), spread from M1 + INTONE.
Geode globals on M1 page 2
Geode globals (M1 page 2): TIME / INTONE / RAMP / CURVE / MODE. Header reads GEODE.
Geode voice page
Geode voice (M5 = voice 3): VOICE / DIVS / REPEAT / TUNE / TIME. Tune defaults to 3:1.

Part 7 · Routing the output

Press Right from the last param page to open the routing overlay (header reads ROUTING). The overlay is a membership grid — a modulator can drive multiple destinations at once.

F-keyDoes
F1 RUNFocus the modulator run mode (One / Loop / Cycle); encoder edits
F2 GATEFocus the gate source; encoder cycles the curated source list
F3 CLEARRemove the cursored destination
F4 EVENTFor a MIDI destination, cycle the continuous event (CC / Bend / Pressure)
F5 CC NUMFor a CC event, edit the CC number (0–127)

Routing edits are staged; the context menu (Shift+Page) offers CANCEL (revert) and COMMIT (apply CV / MIDI assignments).

Modulator routing overlay
Routing overlay: CV 1–8 / MIDI 1–16 grid (CV1, CV3, MIDI3, MIDI10 assigned), cursor on MIDI 3 (CC 74). RUN/GATE shown bottom-right.

Beyond CV and MIDI, a modulator routes into Routing::Target — the routing engine's full target set: tempo, swing, mute/fill, per-track octave / transpose / probability biases, per-sequence first/last/divisor/scale, Tuesday / Stochastic / Fractal targets, the chaos and wavefolder effects, DiscreteMap, indexed modulation, and the BusCv 1–4 sinks.

Part 8 · Teletype ops (MO.* / G.*)

The bank is fully scriptable from Teletype 2. Modulator slots are addressed 1-based (1..8); Geode is single-instance. Ops live in TeletypeNativeOps.cpp; names in TT2OpNames.cpp.

MO.* — modulator slot ops

OpArgsDoesAlias
MOslotread the slot's current output—
MO.Pslot, addr, [val]get/set any param by address (incl. gate source, addr 14)—
MO.SHAPEslot, [val]get/set ShapeMO.S
MO.RATEslot, [val]get/set RateMO.R
MO.DEPTHslot, [val]get/set DepthMO.D
MO.MODEslot, [val]get/set Mode (FREE/TRIG/HOLD)MO.M
MO.OFFslot, [val]get/set OffsetMO.O
MO.TRIGslottrigger the slot (fire its envelope / gate)MO.T

The MO.P addresses are the Modulator::Param table: Shape(0), Rate(1), Depth(2), Mode(3), Offset(4), Attack(5), Decay(6), Sustain(7), Release(8), Amplitude(9), Smooth(10), Phase(11), Invert(12), RateDomain(13), GateSource(14).

E.* and LFO.* are shape-locked sugar over the same slots — each write forces a shape + mode. E.* force ADSR + Trig: E (slot, amplitude), E.A, E.D, E.O, E.T (trigger). LFO.* force Sine + Run: LFO.R (centi-Hz, Free), LFO.C (divisor → Tempo), LFO.A (depth), LFO.O (offset). LFO.W / LFO.F / LFO.S are reserved, not yet wired.

G.* — Geode ops

OpArgsDoesAlias
G.TIME[ms]get/set TIMEG.T
G.TONE[intone]get/set INTONEG.I
G.RAMP[ramp]get/set RAMPG.RA
G.CURV[curve]get/set CURVEG.C
G.MODE[mode]get/set MODE (0=Trans/1=Sust/2=Cycle)G.M
G.RUN[state]get/set run state (0 = stop)G.N
G.VAL—read the live mix outputG.L
G.TUNEvoice, num, denset a voice's tune ratio (voice 0 = all)—
G.Vvoice, divs, repstrigger voice(s) (voice 0 = all)—
G.Stime, intone, runatomic TIME/TONE/RUN setter (no trigger)—

G.O, G.BAR, G.R are intentionally unregistered — no live GeodeConfig field; run goes through MO 2, bars through the transport.

How the ops map to the params

Part 9 · Setup summary

Per-slot params (the 15-field dictionary): Shape, Rate, Depth, Mode, Offset, Attack, Decay, Sustain, Release, Amplitude, Smooth, Phase, Invert, RateDomain, GateSource. Shapes reinterpret the labels (Spring → STRIKE/TENS/RING/CLANG/PICKUP; Chaos → P1/P2).

Defaults at clear(): Shape Sine, Rate 0.05 Hz (Free), Depth 25, Mode FREE, Offset 0, Attack 900 / Decay 1240, Sustain 100, Release 200, Amplitude 127, Smooth 100, Phase 0, Invert off, gate source GateOut1.

Bank modeToggleM1M2M3..M8
JustFShift+RATEmaster rateINTONE hostderived rates (read-only)
GeodeShift+SHAPEclock + globalsrun source6 voices

Each slot → 0..127 (64 = 0V) → routed to CV outputs / bus CV / MIDI via the routing overlay, or driven from Teletype MO.* / G.*.