Skip to content

MIDI Tracks & Time

A MIDI track is a channel on the mixer that sends notes instead of sound. This page explains how a track keeps its identity while you edit, and when the values it sends are decided.

MidiTrack("bass") makes the track the first time it runs. Every time after that it returns the same track, with everything set on it — output, input, velocity, MPE, tuning, key, CC lanes — intact. That is what lets you re-run a line while the music plays: you talk to the track that is already there, without a gap and without a second one stacking up. Two tracks need two names, the way two tracks in a DAW do.

A new track has no output, like a MIDI track in Live with “MIDI To” set to “No Output”. bass >> MidiOutput("synth", 2) routes it, the same >> that picks an audio track’s output; run bass >> MidiOutput("synth", 3) and the track moves to channel 3 from the next cycle, its pattern, CC and input thru with it. bass >> nowhere sets it back to no output.

A track lives as long as a variable holds it. When nothing holds it any more, it is deleted at the end of that statement: it stops playing, its input stops coming through, and its name is free. That is the same whether you rebound the last variable, its block ended, or you never bound it at all — MidiTrack("x") << [C4]; makes a track and deletes it again in one statement, so nothing plays. Bind the track you want to hear: let x = MidiTrack("x"); x >> MidiOutput("synth"); x << [C4];.

Ports and aliases separate the rig from the piece

Section titled “Ports and aliases separate the rig from the piece”

A track’s output names a destination alias, such as "synth", while preferences map that alias to a port on this machine. The piece can keep the same destination name when another rig has a different device name. Channels still select parts within that destination; channel 2 on one port is not channel 2 on every port.

Outputs and inputs are separate declarations because sending to a synth and listening to a keyboard are separate connections. The ordered preference entries also carry facts about each device, such as whether its absence is expected. A missing device keeps its alias so the track’s intent stays visible rather than silently moving to another instrument.

Applying a preferences reload looks for the declared ports again. Connecting a cable alone does not yet refresh them. The file’s mapping wins on reload, including a mapping to a device that is currently absent. How Preferences Are Applied explains the lifecycle; Send MIDI to a Synth or DAW shows the declarations.

MIDI tracks and audio tracks run on the same cycle clock. A pattern on one track and a pattern on another start together and stay together, however long either is, and a pattern sent mid-cycle waits for the same boundary on every track. There is nothing to synchronise, because there is only one clock. Cycles & Time describes it.

A velocity pattern gives each note the value of the step it starts in.

A velocity signal is read at the moment the note fires, the way a hardware sequencer reads a velocity CV when its gate opens. Nothing is asked about the future, so any signal works — a smoothed one, a dsp signal, an envelope — and a slow envelope shapes four notes across a cycle rather than stamping all four with one value. A signal that is a pure function of time lands on the gate exactly; one that carries state is read to within one internal block, a few milliseconds.

A signal never produces velocity 0. In MIDI, a note-on at velocity 0 is a note-off, so a signal at the bottom of its range gives 1, the quietest velocity MIDI has.

A note that carries its own ^ velocity keeps it, whatever the track’s velocity is.

A CC lane fed a signal behaves like a knob wired to the synth. The audio engine sends the value the signal holds now, a set number of times a second and only when it changes, whether the transport is running or not — so a controller remapped onto a synth keeps working with the music stopped.

A CC lane fed a pattern is part of the music instead. Its values are events on the timeline, played with the notes, and only while the transport runs.

A file has no gate to read a signal at, so midi_export() runs the signals across the exported span, the way a bounce runs the audio graph. Each velocity is taken at its own note and each CC value at its own moment, so a filtered LFO writes the curve you hear rather than one flat value.

A signal can only be advanced by one owner at a time. So while an export with signals runs, it owns the engine and playback pauses. An export with none — plain patterns, fixed CC values — has nothing to run and never interrupts playback.