The examples/909.ttl circuit models the Roland TR-909 drum machine as nine independent voice sub-circuits inside a single Valis circuit. It demonstrates the val:NoteGate element, the Envelope gate-override input, val:AsymClip waveshaping, and the val:section parameter grouping used by multi-instrument circuits.
The Valis engine broadcasts a single MIDI state (gate, noteNumber, velocity) to every element in a circuit. In a synthesiser, that is the right design — one note drives the whole circuit. In a drum machine, nine separate instruments must respond to nine different note numbers simultaneously.
The solution is val:NoteGate, a source element that filters the global gate by note number:
:bdGate a val:NoteGate ; val:note 36 .
NoteGate has two control outputs:
gate — 1.0 when args.gate && args.noteNumber == note, otherwise 0.0velocity — the velocity at the time the note triggered, held until the next triggerA control arc connects NoteGate.gate to Envelope.gate, which is a new optional input port on the existing Envelope element:
:aBdGateAmpEnv a val:ControlArc ;
val:from [ val:node :bdGate ; val:port "gate" ] ;
val:to [ val:node :bdAmpEnv ; val:port "gate" ] .
The Envelope gate port has a default of -1.0, which acts as a sentinel: -1 means "use the host MIDI gate" and 0 or 1 means "use this value". Existing circuits that do not connect anything to gate continue to work without change.
| MIDI note | Instrument | Circuit blocks |
|---|---|---|
| 36 | Bass Drum | Triangle oscillator + pitch sweep, AsymClip, VCA |
| 37 | Rim Shot | Noise through highpass filter, short VCA |
| 38 | Snare Drum | Oscillator + band-passed noise mixed, VCA |
| 39 | Hand Clap | Noise through bandpass filter, VCA |
| 41 | Low Tom | TwinTBridge + pitch envelope sweep, amplitude decay |
| 43 | Mid Tom | TwinTBridge + pitch envelope sweep, amplitude decay |
| 50 | High Tom | TwinTBridge + pitch envelope sweep, amplitude decay |
| 42 | Closed HH | Six square oscillators, highpass, very short decay |
| 46 | Open HH | Six square oscillators, highpass, longer decay |
The 909 BD uses a triangle-wave VCO. A fast pitch envelope sweeps from about 210 Hz at the attack down to the body frequency (about 55 Hz) in about 20 ms, producing the characteristic "thump". An val:AsymClip element models the asymmetric diode waveshaping in the original's output stage — the positive half of the triangle is clipped sooner than the negative, altering the waveform character:
NoteGate → pitchEnv.gate → Scale(55-210 Hz) → Oscillator.frequency
NoteGate → ampEnv.gate → VCA.cv
Oscillator(triangle) → AsymClip → VCA → Level
The body pitch is the val:min of the Scale element, exposed as the "Tune" parameter. The TR-808 used a bridged-T resonant network instead; that design is in examples/808-bd.ttl.
Two sine oscillators at 180 Hz and 200 Hz are mixed before the tone VCA. Their 20 Hz beat produces a characteristic pitch wobble during the decay. The noise path uses white noise through a bandpass filter at 2 kHz, with its own shorter envelope (the "Snappy" control). The two paths have independent envelopes:
NoteGate → toneEnv.gate → toneVCA.cv
NoteGate → noiseEnv.gate → noiseVCA.cv
Oscillator(180 Hz, sine) ──┐
Oscillator(200 Hz, sine) ──┘→ oscMixer → toneVCA ──┐
Noise → StateVariable(BP 2 kHz) → noiseVCA ─────────┘→ Mixer → output
The "Snappy" parameter controls noiseEnv.decay; shorter gives a dry, cracking snare. The "Tone" parameter controls the BPF cutoff.
Low, Mid and High Toms use the same architecture as the BD, with a TwinTBridge resonator and a pitch sweep, at higher body frequencies (70, 110, 160 Hz respectively). The pitch decay time is slightly faster (15-20 ms) than the BD, and the amplitude decay is shorter.
White noise through a OnePole highpass filter at 1.2 kHz gives the dry click of a stick on the rim. The amplitude decay is 20 ms.
White noise through a resonant StateVariable bandpass at 1.2 kHz (Q ≈ 1.2). The original circuit produced multiple short noise bursts; approximated here as a single filtered burst with an 80 ms decay.
The 909 hi-hat section uses six square-wave oscillators at inharmonic ratios from circuit analysis. They are tuned to 562, 834, 1124, 1395, 1668 and 1995 Hz. All six oscillators sum in a Mixer before a 7 kHz highpass filter removes the fundamental and low partials:
NoteGate → Envelope.gate → VCA.cv
Oscillator(562 Hz, sq) ──┐
Oscillator(834 Hz, sq) ──┤
Oscillator(1124 Hz, sq) ──┤→ Mixer → StateVariable(HP 7 kHz) → VCA
Oscillator(1395 Hz, sq) ──┤
Oscillator(1668 Hz, sq) ──┤
Oscillator(1995 Hz, sq) ──┘
Closed and Open hats share the same spectral source but differ in decay time (45 ms vs 500 ms). The original hi-hat choke that mutes the open hat on a closed-hat trigger is not modelled yet because the current control arcs replace one destination value and do not combine multiple gate sources.
A circuit with 23 parameters across nine instruments is difficult to navigate as a flat list. The val:section property on val:Param bindings groups parameters under named headings in the Controls panel:
:pBdDecay a val:Param ; val:slot 1 ;
val:target :bdAmpEnv ; val:property val:decay ;
lv2:name "Decay" ; lv2:symbol "bd_decay" ; val:section "Bass Drum" ;
units:unit units:ms ; lv2:minimum 50.0 ; lv2:maximum 2000.0 .
The Controls panel renders a coloured label and a divider line at the start of each new section. Bindings without a val:section are displayed ungrouped. Each instrument now exposes a Level parameter as a val:Gain trim before the main mixer.
args.velocity inside the Envelope, which reflects the most recently triggered note, not the specific note that triggered each instrument. In practice this works for sequential playing but not for simultaneous hits at different velocities.To load and play the circuit using the Transmission host:
mcp__transmission__project_open(path: ".../examples/909.ttl")
mcp__transmission__transport_play()
Send MIDI note 36 (bass drum), 38 (snare), 42 (closed hat) on channel 10. Each instrument should respond independently; others should remain silent during each note.
Inspect the compiled circuit graph to verify NoteGate appears in each instrument chain and that control arcs connect to Envelope gate ports:
mcp__transmission__project_get()