MIDI I/O
Resonon isn’t a closed world — it speaks MIDI, so it can drive a hardware synth, sequence a DAW, or take notes and knob-turns from a controller. In this chapter you’ll connect an output port, send a pattern to it, read a controller, and map a knob to a parameter with MIDI learn.
Sending MIDI
Section titled “Sending MIDI”Declaring a Port
Section titled “Declaring a Port”First, see what’s out there. midi_ports() returns the output ports your system
exposes:
let ports = midi_ports();print(ports); // ["IAC Driver Bus 1", "Prophet Rev2", ...]Which ports this rig has is a fact about the rig, not about the music, so it lives in
~/.resonon/preferences.toml and is opened before any of your code runs. Every entry gets an
alias — a short name your patterns use:
[[midi.outputs]]alias = "default"port = "IAC Driver Bus 1"resonon setup walks you through the ports this rig has and writes the entries for you;
resonon --list-ports prints one per attached device, ready to paste. reload_preferences() puts a
change in force without restarting.
On macOS the IAC Driver is the usual way to pipe MIDI into a DAW — enable it in Audio MIDI Setup → MIDI Studio.
Sending Notes
Section titled “Sending Notes”A MidiTrack is the MIDI counterpart to the AudioTrack you’ve been using — but
instead of feeding an instrument, its notes go out the port on a chosen channel.
Send it a pattern with <<, exactly like an audio track:
let melody = MidiTrack(1); // channel 1melody << [C4 E4 G4 E4];
PLAY;Point a software or hardware instrument at channel 1 and you’ll hear your pattern. Everything you know about patterns — chords, rests, Euclidean rhythms — works unchanged; only the destination is different.
Routing to Several Destinations
Section titled “Routing to Several Destinations”Give each entry its own alias and you can fan tracks out to different ports and channels — your DAW on one, a hardware synth on another:
[[midi.outputs]]alias = "daw"port = "IAC Driver Bus 1"
[[midi.outputs]]alias = "synth"port = "Prophet Rev2"optional = truelet drums = MidiTrack(10, 110, "daw"); // to the DAW, channel 10, velocity 110let bass = MidiTrack(1, 90, "synth"); // to hardware, channel 1let pad = MidiTrack(3, 80, "synth"); // to hardware, channel 3
drums << [bd _ sd _, ch ch ch ch];bass << [C2 _ C2 G2];pad << [C4, E4, G4];
PLAY;MidiTrack(channel, velocity, alias, name) — velocity, alias, and the display
name are all optional (defaulting to 100, "default", and unnamed). Re-route a
track at any time with .output:
bass.output("daw", 1); // move bass onto the DAW, channel 1When you lose track of what’s going where, midi_routing() prints the whole
picture — every output, input, track route, and the clock state.
Receiving MIDI
Section titled “Receiving MIDI”Input mirrors output. midi_input_ports() lists controllers, and a [[midi.inputs]] entry
opens one under an alias:
let in_ports = midi_input_ports();print(in_ports); // ["Arturia KeyStep 37", ...][[midi.inputs]]alias = "kb"port = "KeyStep"optional = trueoptional = true is for a controller you do not always have plugged in: it is reported once
and the session continues.
Route that input into a track with .input, and choose how it monitors — whether
incoming notes pass straight through to the output:
let keys = MidiTrack(1, 100) .input("kb") .monitor("in") .output("daw");
PLAY;Now the KeyStep plays through resonon and out to your DAW. The monitor modes:
| Mode | Behavior |
|---|---|
"off" | No pass-through (the default) |
"in" | Always pass input straight to output |
"auto" | Pass through only while no pattern is playing on the track |
You can narrow input down to one channel, or open it up to everything:
let bass = MidiTrack(2, 100).input("kb", 1); // only channel 1 from "kb"let omni = MidiTrack(3, 80).input("all"); // every port, every channelMapping Knobs with CC
Section titled “Mapping Knobs with CC”Controller knobs and faders send CC (control-change) messages, and resonon
hands them to you as signals — the same signals you can route into any parameter.
Cc(n) reads CC n from any channel; Cc(channel, n) pins it to one channel
(channels are 0–15):
let cutoff = Cc(74); // CC 74, any channellet mod_wheel = Cc(0, 1); // CC 1 on channel 0A CC arrives normalized to 0.0–1.0. Shape it and send it into a parameter with
<<, just like an LFO:
use "std/effects" { Lowpass, Delay };
let filter = Lowpass("lowpass", 2000);let delay = Delay("delay", 0.25, 0.5);drums.fx(#[filter, delay]);
filter.param("Cutoff") << Cc(74).smooth(50).range_exp(200, 8000);delay.param("Feedback") << Cc(71).smooth(30).range(0.1, 0.6);.smooth(ms) irons out the zipper noise of stepped 7-bit values, and .range /
.range_exp map the 0–1 signal onto a useful span — reach for range_exp on
anything frequency-like, where your ear hears ratios rather than steps.
MIDI Learn
Section titled “MIDI Learn”Don’t know which CC a knob sends? Let resonon listen. Cc_learn() waits up to ten
seconds for the first CC message, then returns a signal bound to it:
let knob = Cc_learn();// "Waiting for CC input... (10s timeout)"// ...twist a knob on your controller...// "Learned: Cc(0, 74)"Once you know the number, hard-code it so your script doesn’t pause to learn every time:
let knob = Cc(0, 74);Syncing to a Clock
Section titled “Syncing to a Clock”To lock tempo with external gear, either send MIDI clock to your outputs or follow a clock coming in:
midi_clock_send(true); // become the clock mastersetbpm(120);PLAY;midi_clock_follow("daw"); // follow an incoming clock insteadFollowing needs the incoming port declared as a [[midi.inputs]] entry with that alias.
Sending and following are two ends of the same wire — a session is either the master or a follower, not both.
MIDI is aligned with your audio automatically, plugin latency included, so you should not have to touch anything. If one device still sits early or late against the rest, trim that device:
midi_latency(); // where the offset comes frommidi_port_trim("cv", -2.5); // this synth is late; pull it forwardmidi_port_clock_trim("cv", -2.5); // extra nudge for its clock input onlyNext Steps
Section titled “Next Steps”That’s the whole round trip — out to your gear, back from a controller, and locked to a shared clock. The MIDI chapters go deeper on each piece:
- MIDI Output — full track API, microtonal tuning, MPE, export
- MIDI Input & CC — channel filtering, monitor modes, CC in custom DSP
- Clock Sync — virtual ports and DAW configuration per platform
- MIDI Reference — every MIDI function and method