Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Tracker sequencer

Source of truth: the doc comments on each module struct in patches-modules/src/ define the canonical port names, parameter ranges, and behaviour. This page is kept in sync with those comments.

The tracker sequencer is a two-module system for song-driven step sequencing. A MasterSequencer reads a named song from the patch’s tracker data and drives one or more PatternPlayer modules via poly clock buses. Pattern and song data are defined in the .patches file using pattern and song blocks (see DSL reference — Patterns & songs).

How it fits together

┌─────────────────────┐
│  MasterSequencer    │
│  song: my_song      │──clock[bass]──▶ PatternPlayer (channels: [note, vel])
│  bpm: 120           │──clock[lead]──▶ PatternPlayer (channels: [note, vel])
│  rows_per_beat: 4   │──clock[drums]─▶ PatternPlayer (channels: [hit])
└─────────────────────┘

The MasterSequencer walks through the song order, emitting timing and pattern-selection data on each clock bus. Each PatternPlayer decodes that data, looks up the relevant pattern, and outputs cv/trigger/gate signals per channel.


MasterSequencer — Song-driven transport

Drives song playback with transport controls, swing, and a poly clock bus per song channel.

Inputs

PortKindDescription
startmonoRising edge resets and begins playback
stopmonoRising edge halts and resets playback
pausemonoRising edge halts playback in place
resumemonoRising edge continues from current position

Outputs

PortKindDescription
clock[i]polyClock bus per channel (i in 0..N−1, N = channels)

The clock bus carries four poly voices:

VoiceSignalDescription
0pattern reset1.0 on first tick of a new pattern
1pattern bank indexfloat-encoded integer (−1 = stop sentinel)
2tick trigger1.0 on each step
3tick durationseconds per tick

Parameters

NameTypeRangeDefaultDescription
bpmfloat1.0–999.0120.0Tempo in beats per minute
rows_per_beatint1–644Steps per beat
songsong_namenoneName of the song to play
loopbooltrueLoop at end of song
autostartbooltrueBegin playback on activation
swingfloat0.0–1.00.5Swing ratio for alternating steps
syncenumauto/free/hostautoClock source: auto picks host when hosted, otherwise free

Shape arguments

The channels shape argument defines the song channels. Use named aliases to match the song’s lane names:

module seq: MasterSequencer(channels: [bass, lead, drums]) {
    song: my_song, bpm: 140, rows_per_beat: 4
}

PatternPlayer — Pattern step sequencer

A generic multi-channel step sequencer that reads a poly clock bus, steps through pattern data, and outputs cv1/cv2/trigger/gate signals per channel. The PatternPlayer does not know whether its channels are notes, drums, or automation — all channels produce the same four output types.

Inputs

PortKindDescription
clockpolyClock bus from MasterSequencer

Outputs

PortKindDescription
cv1[i]monoControl voltage 1 per channel (i in 0..N−1)
cv2[i]monoControl voltage 2 per channel (i in 0..N−1)
trigger[i]monoTrigger per channel (i in 0..N−1)
gate[i]monoGate per channel (i in 0..N−1)

Shape arguments

The channels shape argument defines the pattern channels. Use named aliases to match the pattern’s channel names:

module pp: PatternPlayer(channels: [note, vel])

Output conventions

For note channels, cv1 carries the V/oct pitch (same convention as Osc) and trigger/gate signal note-on events. For drum/trigger channels, trigger fires on each hit and cv2 carries the velocity value.

Step events (ADR 0077 unified grammar)

The PatternPlayer consumes the row-build pass’s resolved StepEffect per cell — a single typed dispatch covering rests, triggered notes, rolls, slides, step-cv changes, and continuation cells. See the DSL reference — Step events for the full cell taxonomy, close rule, and worked-example timelines. The player-side behaviour is:

  • Triggered cell (value, value*N, value:cv2, value>value, value>): snap cv1/cv2 at the start of the tick and fire trigger. *N opens a roll; value>value opens-and-closes a one-tick slide; value> opens a multi-tick slide whose close target is resolved when the close cell arrives.
  • Step-cv (/value): snap cv1 (and :cv2 if present) at the tick boundary; no trigger. Gate stays high.
  • Slide flow (>_, _ absorbed by a slide): no trigger; cv ramps per the slide’s per-channel schedule. Gate stays high.
  • Slide close-in-tick (>value): cv1 (and :cv2 if present) ramp to the close value across this tick; no trigger.
  • Tie sustain (_ with no open modifier): gate stays high; cv held.
  • Tie-spread roll (_ absorbed by a *N anchor): the N sub-triggers spread evenly across the anchor tick plus the absorbed run. Per-tick sub-event placement respects swung tick durations — each sub-event’s sample offset is computed against the current tick’s duration, not the anchor’s, so swing within a span sits precisely on the swung beats.
  • Rest (.): gate drops; channel state resets (any open slide is closed at its current cv).
# tie-spread roll across two ticks then a fresh hit
hit: x*3 _ x .

# slide opens at tick 1, ramps through ticks 1+2, lands on G4 at the
# boundary, holds tick 3
note: E4> _ /G4

# slide opens, ramps continuously across three ticks, finishes inside
# tick 3
note: E4> _ >G4

# step cv without retrigger
note: E4 /G4 _

Wiring examples

Drum machine

One PatternPlayer per drum voice, each with a single hit channel:

module kick_pp: PatternPlayer(channels: [hit])
seq.clock[kick] -> kick_pp.clock

module kick_drum: Kick { pitch: 50, decay: 0.4 }
kick_pp.trigger[hit] -> kick_drum.trigger
kick_pp.cv2[hit]     -> kick_vca.velocity

Melodic voice

A PatternPlayer with note and vel channels driving a synth voice:

module bass_pp: PatternPlayer(channels: [note, vel])
seq.clock[bass] -> bass_pp.clock

bass_pp.cv1[note]     -> voice.voct
bass_pp.gate[note]    -> voice.gate
bass_pp.trigger[note] -> voice.trigger
bass_pp.cv1[vel]      -> voice.velocity