Add a MIDI controller and MIDI learn
Questa pagina è in inglese: in italiano ci sono per ora i tutorial e il glossario; menu, ricerca e navigazione sono in italiano.
A footswitch, a pad or a fader becomes a binding: a MIDI trigger that fires a command. Learn lets the user press the control instead of typing its number. Everything here runs without hardware on the fakes; swap in a device adapter for the real thing.
Uses: core/midi (USES midi). Runnable version:
examples/midi/gate.
1. Open the input
Section titled “1. Open the input”MidiInputs opens ports by name through a MidiInputDirectory and feeds
every message, in order, to an activity monitor, the learn arbiter, the
trigger matcher, an MSC receiver and your own consumer:
midi::ActivityMonitor monitor;midi::TriggerMatcher matcher;midi::MidiInputs inputs (midiInputDirectory, { monitor, &matcher, nullptr, nullptr, &keyboard });inputs.open ("Stage Keys");midiInputDirectory is base::testing::FakeMidiInputDirectory in a test.
In an application it is LibremidiDevices from the libremidi adapter (no
JUCE; MIDI 1.0 and MIDI 2.0, see
examples/libremidi/ports) or
JuceMidiDevices from juce_midi (in the shuffle_juce
satellite). Both also implement the output directory.
2. Bind a control to a command
Section titled “2. Bind a control to a command”midi::Binding footswitch;footswitch.id = 1;footswitch.trigger.kind = midi::TriggerKind::controller;footswitch.trigger.number = 85; // CC 85, any channelfootswitch.onPress = commands::makeCommand ("cues.go");matcher.publish ({ footswitch });The matcher runs on the MIDI input thread without allocating. The command is not applied there: on a control timer, take what fired and submit it like any other request:
for (const auto& fired : matcher.takeFired()) session.submit (fired.command, midi::showControlTriggerProfile(), {});showControlTriggerProfile() marks the source, so GO protection, show mode
and the recorder treat it as a show-control trigger.
3. MIDI learn
Section titled “3. MIDI learn”In an application, bindings are show state and change through the MIDI
commands (midiCommands(), MidiCommandTarget). Learn is a request the
target hands out; the arbiter captures the next eligible message for one
owner at a time:
midi::LearnArbiter learn ([&ui] (const std::string& owner) { ui.learnCancelled (owner); });midi::MidiInputs inputs (midiInputDirectory, { monitor, &matcher, &learn, nullptr, nullptr });
// after every applied command:matcher.publish (midi::bindingsToPublish (target.setup()));for (auto& r : target.takeLearnRequests()) r.arm ? learn.arm (r.owner, r.kinds) : learn.end (r.owner);
// on the control timer:if (auto capture = learn.takeCapture()) session.submit (midi::bindTriggerCommand (*capture, valueOfOwner), ownerProfile, {});The capture becomes a command too, so learning a pedal can be undone. The full wiring, with MSC and inhibit switches, is in core/midi.
4. Panic
Section titled “4. Panic”A tracked output remembers held notes and pedals; panic releases exactly those, then sends all-notes-off:
midi::PanicService panic (midiOutputDirectory, tasks);const auto report = panic.panicAll(); // report.messagesSent, report.portsSentReference: specs/midi.md (triggers and learn MI-R76 to MI-R86).
© 2026 Umberto Sorbo. Il software è distribuito con la Apache License, Version 2.0, e fornito “così com’è”, senza garanzie o condizioni di alcun tipo; leggi l’esclusione di responsabilità.
SHUFFLE Framework e il logo di SHUFFLE Framework sono marchi di Umberto Sorbo; la licenza non concede alcun diritto su di essi (marchi). Gli altri nomi appartengono ai rispettivi titolari. Caratteri: IBM Plex Sans e JetBrains Mono, SIL Open Font License 1.1.
Contatti: shuffleframework@gmail.com · Instagram @shuffleframework. Problemi di sicurezza: scrivi allo stesso indirizzo con “Security” nell’oggetto, non in una issue pubblica.