MIDI message flow¶
This page traces a MIDI message all the way through a Remote Script, in both directions. It ties together the pieces from Architecture, Components, Layers & Modes and the ControlSurface lifecycle: once you can follow a single button press end to end, the rest of the framework falls into place.
Two directions, one loop¶
Every interaction is a round trip:
- Downstream (hardware -> Live): a physical action becomes a MIDI message, which the script interprets and turns into a Live Object Model operation.
- Upstream (Live -> hardware): Live's state changes, a listener fires, and the script sends MIDI back to update an LED, motor fader, or display.
Feedback is not a nicety bolted on at the end -- it is half the system. A pad that launches a clip but never lights up is only half-wired.
Downstream: a button press, step by step¶
- Hardware emits MIDI. Pressing a pad sends, say, a Note On on channel 1, note 36, velocity 127.
- Live routes it to your script. Only messages your surface asked for (via the MIDI map, below) are delivered. They arrive at the framework's input handling.
- Dispatch to an Element. The framework matches the message's type/channel/number to the
ButtonElement(orEncoderElement,SliderElement...) that was created for it. The element is the first object that "owns" that message. - Element to bound control. The element forwards its value to whatever control it is bound to in the currently active layer. This is the hinge: the same element reaches a different handler depending on the mode (see Layers & Modes).
- Component interprets it. The bound control belongs to a component
(
SessionComponent,MixerComponent, ...). The component decides what the press means in context -- "launch clip slot (track 1, scene 5)". - Call the LOM. The component performs the action through the Live Object Model:
clip_slot.fire(). Live does the musical work.
At no point did the element know about clips, nor did the component know about MIDI note numbers. Each layer speaks only to its neighbours.
Upstream: feedback, step by step¶
- Live's state changes. The clip starts;
ClipSlot.is_playing(or the slot's playing-status) flips. - A listener fires. When the component was built, it registered a listener on that LOM property. Live calls it now. (This is why listeners, not polling, are the heart of the framework -- see the lifecycle guide.)
- Component computes new feedback. It maps the new state to a value/colour -- "this slot is now playing -> green".
- Send to the Element. The component sets the bound control's value; the element turns
that into an outgoing MIDI message via
send_value(...). - Live sends MIDI out; the pad lights up. The hardware receives the message and updates its LED.
Steps 1-6 and 7-11 form one closed loop. Press -> fire -> state change -> listener -> feedback -> light. The user perceives it as instant.
The MIDI map: why Live doesn't send you everything¶
Routing every controller message into Python would be wasteful and slow. Instead the surface declares a MIDI map: the set of notes/CCs it cares about and how each should be handled. Live builds this map and routes accordingly. There are two handling styles:
- Direct mapping -- Live connects an element straight to a LOM target (e.g. a fader to a device parameter) and updates it in the C++ core, with no Python round-trip. Lowest latency; use it for continuous controls that map 1:1 to a parameter.
- Forwarding -- the message is delivered to your script so a component can run logic on it (e.g. a pad that means different things in different modes). Slightly more overhead, far more flexible.
Rebuilding the map¶
The map reflects the current wiring. When a mode switch rebinds elements, the map is stale.
The surface calls request_rebuild_midi_map(); Live batches the request and later calls
back into build_midi_map(midi_map_handle), where each live element re-declares its
routing. Two consequences worth internalising:
- rebuilds are asynchronous -- you request, Live calls back; don't expect it inline;
- a mode change is, under the hood, largely a map rebuild plus enabling/disabling components.
SysEx, displays and special messages¶
Not everything is a note or CC. Many controllers use SysEx for firmware handshakes
(identity requests), entering "host"/"native" mode, RGB LED colours, and text displays.
Scripts typically send a SysEx preamble on connect and a reset on disconnect. SysEx is
handled through dedicated element/handler classes rather than the note/CC path; look for the
device's sysex module and its MIDI constants in the
API Reference.
The loop at a glance¶
[ press pad ]
| MIDI in
v
Element ──(current layer)──> Component ──> LOM: clip_slot.fire()
|
v
Live changes state
|
listener fires
|
Element <──(send_value)── Component <───────────────┘
| MIDI out
v
[ pad turns green ]
See also: ControlSurface lifecycle for when listeners and the MIDI map are set up, and the LOM reference for the observable properties that drive feedback.