MIDI Tracks
MIDI tracks send pattern data to external synthesizers, DAWs, and hardware over MIDI. Each track targets a specific channel and output port, and can optionally receive MIDI input for live play-through.
Creating a MIDI Track
Section titled “Creating a MIDI Track”MidiTrack(...) creates a track. The first argument can be a name (string) or a channel (number). If the first argument is a number, the track is named "unnamed".
| Parameter | Type | Description |
|---|---|---|
name | String (optional) | Track name, defaults to "unnamed" |
channel | Number | MIDI channel (1—16) |
velocity | Number (optional) | Default velocity (0—127), defaults to 100 |
port_alias | String (optional) | Output port alias, defaults to "default" |
Constructor forms
Section titled “Constructor forms”Without a name:
MidiTrack(channel)MidiTrack(channel, velocity)MidiTrack(channel, velocity, port_alias)With a name:
MidiTrack(name, channel)MidiTrack(name, channel, velocity)MidiTrack(name, channel, velocity, port_alias)Examples
Section titled “Examples”// Channel only — unnamed, velocity 100, default portlet melody = MidiTrack(1);
// Channel + velocitylet bass = MidiTrack(2, 80);
// Named track with velocitylet lead = MidiTrack("lead", 1, 110);
// Full form — named, routed to a specific port aliaslet synth = MidiTrack("pad", 3, 90, "hardware");Sending Patterns
Section titled “Sending Patterns”Use the output operator << to send a pattern to a track:
melody << [C4 E4 G4 E4];You can change the pattern at any time. The new pattern takes effect at the next cycle boundary:
melody << [C4 D4 E4 F4 G4];Track Methods
Section titled “Track Methods”MIDI tracks expose methods for routing, velocity, input, and monitoring. Each method has a mutating variant that modifies the track in place and a pure variant (prefixed with with_) that returns a new track, leaving the original unchanged.
| Method | Mutating | Description |
|---|---|---|
.output(alias) | yes | Route to output port |
.output(alias, channel) | yes | Route to port on specific channel |
.with_output(...) | no | Pure variant of .output() |
.velocity(n) | yes | Set default velocity (0—127) |
.velocity(signal) | yes | Set signal-driven velocity modulation |
.velocity(pattern) | yes | Set pattern-driven velocity modulation |
.with_velocity(n) | no | Pure variant of .velocity() |
.input(alias) | yes | Receive from input port (all channels) |
.input(alias, channel) | yes | Receive from specific channel |
.with_input(...) | no | Pure variant of .input() |
.monitor(mode) | yes | Set monitor mode ("off", "in", "auto") |
.with_monitor(mode) | no | Pure variant of .monitor() |
Output routing
Section titled “Output routing”.output(alias) routes the track to a named port alias. .output(alias, channel) also changes the MIDI channel:
let track = MidiTrack("keys", 1, 100);
// Route to the "daw" port aliastrack.output("daw");
// Route to "synth" on channel 5track.output("synth", 5);If the alias is not currently connected, Resonon logs a warning but does not error — you can connect the port later.
Velocity
Section titled “Velocity”.velocity(n) sets the default velocity for the track. Notes without an explicit velocity use this value:
let soft = MidiTrack("pad", 4, 60);soft.velocity(40); // even softer
// Pure variant for chaininglet loud = soft.with_velocity(120);.velocity() also accepts a signal for dynamic velocity modulation. Each note’s velocity is sampled from the signal at event time:
let melody = MidiTrack("synth", 1);melody << [C4 D4 E4 F4];melody.velocity(sine(0.5).range(60, 127));.velocity() also accepts a pattern. Each note’s velocity is determined by the velocity pattern event active at that note’s start time:
let melody = MidiTrack("synth", 1);melody << [C4 D4 E4 F4];melody.velocity([100 60 80 120]);Per-note @ velocity always takes precedence over signal or pattern:
// C4 stays at 100, F4 stays at 50, D4/E4 get signal valuesmelody << [C4@100 D4 E4 F4@50];melody.velocity(sine(1).range(60, 127));The last .velocity() call wins — calling .velocity(n) clears any active signal or pattern, and .velocity(signal) or .velocity(pattern) overrides the static default. Signal and pattern are mutually exclusive.
Input routing and monitoring
Section titled “Input routing and monitoring”.input(alias) configures a track to receive MIDI from an input port. .monitor(mode) controls whether incoming MIDI is passed through to the track’s output:
| Mode | Behavior |
|---|---|
"off" | Input is received but not passed through (default) |
"in" | Always pass input through to output |
"auto" | Pass through when no pattern is playing |
let keys = MidiTrack("keys", 1, 100);keys.input("keyboard");keys.monitor("in");The special alias "all" receives from all connected input ports:
keys.input("all");See MIDI Input for details on connecting input ports and input routing.
Multi-Port Routing
Section titled “Multi-Port Routing”Port aliases let you send different tracks to different MIDI destinations — for example, one track to your DAW and another to a hardware synthesizer.
Setting up ports
Section titled “Setting up ports”Use midi_connect(port_name, alias) to open a connection with a named alias:
midi_connect("IAC Driver Bus 1", "daw");midi_connect("Prophet Rev2", "synth");Without an alias, the port is connected as "default":
midi_connect("IAC Driver Bus 1");// equivalent to midi_connect("IAC Driver Bus 1", "default")Routing tracks to ports
Section titled “Routing tracks to ports”Route tracks via the constructor or the .output() method:
// Via constructorlet drums = MidiTrack("drums", 10, 110, "daw");let pad = MidiTrack("pad", 1, 80, "synth");
// Via methodlet lead = MidiTrack("lead", 2, 100);lead.output("daw");Checking and disconnecting ports
Section titled “Checking and disconnecting ports”// List available portsPRINT midi_ports();
// Check which ports are connectedPRINT midi_connected();
// Disconnect a specific aliasmidi_disconnect("synth");
// Disconnect the default portmidi_disconnect();Multi-port example
Section titled “Multi-port example”// Connect to DAW for recording and hardware synth for monitoringmidi_connect("IAC Driver Bus 1", "daw");midi_connect("Prophet Rev2", "synth");
let bass = MidiTrack("bass", 1, 90, "daw");let lead = MidiTrack("lead", 2, 100, "daw");let pad = MidiTrack("pad", 1, 80, "synth");
bass << [C2 _ E2 _ G2 _ E2 _];lead << [C4 E4 G4 C5];pad << <Stack(chord(C4, "maj7"))>;
PLAY;Cycle Synchronization
Section titled “Cycle Synchronization”When multiple tracks are playing, they share the same cycle clock. Patterns sent to different tracks stay aligned:
let bass = MidiTrack(2, 80);bass << [C3 _ C3 G2];
let chords = MidiTrack(3, 90);chords << <Stack(chord(C4, "major")) Stack(chord(G3, "major"))>;Both tracks loop in sync.
Playback Control
Section titled “Playback Control”Start and stop playback with PLAY and PAUSE:
PLAY;
// ... music plays ...
PAUSE;Playback resumes from where it left off. All active tracks respond to the same transport.
Builder Pattern
Section titled “Builder Pattern”Pure methods (with_*) return new tracks, so you can chain them for a complete setup in one expression:
let track = MidiTrack("lead", 1, 100) .with_output("daw") .with_input("keyboard") .with_monitor("auto");This is equivalent to:
let track = MidiTrack("lead", 1, 100);track.output("daw");track.input("keyboard");track.monitor("auto");Design Patterns
Section titled “Design Patterns”| Pattern | Technique | Musical effect | Use case |
|---|---|---|---|
| Multi-timbral | Tracks on different channels | Layer instruments | Orchestration |
| Multi-port | Tracks to different aliases | Multiple destinations | DAW + hardware |
| Velocity layers | Different default velocities | Dynamic contrast | Expressive parts |
| Input thru | .input() + .monitor("in") | Live play-through | Performance |
| Builder chain | .with_*() methods | Clean setup | Complex routing |
Musical Examples
Section titled “Musical Examples”Multi-timbral setup
Section titled “Multi-timbral setup”Assign separate channels to each instrument. Most DAWs and multi-timbral synths map channels to different sounds:
let lead = MidiTrack("lead", 1, 100);let bass = MidiTrack("bass", 2, 80);let drums = MidiTrack("drums", 10, 110);
lead << [E5 D5 C5 D5];bass << [C3 _ C3 G2];drums << [bd _ sn _];
PLAY;Channel 10 is conventionally used for drums in General MIDI.
Multi-port routing
Section titled “Multi-port routing”Record to a DAW while simultaneously monitoring through a hardware synth:
midi_connect("IAC Driver Bus 1", "daw");midi_connect("Minilogue", "synth");
let track = MidiTrack("lead", 1, 100, "daw");let monitor = MidiTrack("monitor", 1, 100, "synth");
let melody = [C4 E4 G4 C5];track << melody;monitor << melody;
PLAY;Live performance setup
Section titled “Live performance setup”Combine keyboard input with a backing pattern:
midi_connect("IAC Driver Bus 1", "daw");
let backing = MidiTrack("backing", 1, 80, "daw");backing << [C3 _ E3 _ G3 _ E3 _];
let live = MidiTrack("live", 2, 100) .with_output("daw") .with_input("keyboard") .with_monitor("in");
PLAY;The live track passes keyboard input directly to the DAW while the backing track loops its pattern.
Debugging and Introspection
Section titled “Debugging and Introspection”Common errors
Section titled “Common errors”| Error | Cause | Fix |
|---|---|---|
MIDI channel must be 1-16 | Channel out of range | Use a channel between 1 and 16 |
MIDI velocity must be 0-127 | Velocity out of range | Use a velocity between 0 and 127 |
Invalid monitor mode | Unrecognized mode string | Use "off", "in", or "auto" |
| Port alias warning in log | Alias not yet connected | Call midi_connect() with the alias first |
Checking connections
Section titled “Checking connections”// List all available output portsPRINT midi_ports();
// Check which ports are connected (returns dict or false)PRINT midi_connected();
// List available input portsPRINT midi_input_ports();See Also
Section titled “See Also”- Pattern Basics — how patterns work
- DAW Setup — connect Resonon to your DAW
- Samplers — use built-in audio instead of external MIDI
- MIDI Input — receive MIDI from controllers
- MIDI CC — send continuous controller messages
- MIDI Export — export patterns to Standard MIDI Files