MIDI Stages
Overview
| Read MIDI Processor first for prerequisite information. |
A stage is a processor proxy. It stands in front of another processor, handles the messages it reads, delivers a message of its own to the processor behind it, and passes everything else through unaltered. Stages nest, and dispatch is called once at the front, so a message reaches the outermost stage first. Every stage is optional, and a processor that takes the messages as they arrive leaves them out. See MIDI for where the stages sit in a chain.
Q has these:
| Stage | What it reads | Where |
|---|---|---|
|
Controllers 6, 38 and 98 to 101, as one parameter. |
This page |
|
Controllers 0 to 63, as one 14 bit value. |
This page |
|
Controllers 120 and above, as channel mode messages. |
This page |
|
Messages of either protocol, as the other’s. |
|
|
MPE channels, as per-note expression. |
|
|
MIDI 2.0 per-note messages, as the same expression. |
|
|
Stream messages, as an endpoint’s description. |
The three on this page all read control changes, because MIDI 1.0 says several things with that one message that are not a control at all.
A MIDI controller is a performance parameter that can vary while notes sound, such as modulation depth, volume or sustain. A MIDI channel has 128 controllers, each a seven bit value set by a control_change carrying its number and the new value. The specification assigns a meaning to many of the numbers. See MIDI 1.0 Messages for the message and MIDI Controller Numbers for the numbers.
Certain controller numbers are parts of a larger message. Each group has a stage that reads it:
- Parameters
-
Controllers 101 and 100 name a registered parameter, 99 and 98 a non-registered one, and 6 and 38 set the value, with 96 and 97 to step it. The address and the value are 14 bits each.
parameter_readerholds the parts and delivers oneregistered_controllerorassignable_controller. - 14 bit controllers
-
Controllers 0 to 31 each have a counterpart at 32 to 63 carrying the fine half of the same value.
cc14_readerjoins the pair and delivers onecontrol_change_14. - Channel modes
-
Controllers 120 and above are commands to the instrument: silence it, or restrict it to one note at a time.
mode_readerdelivers each as a message of its own,all_sound_offand the rest.
Use Case
A controller sets the pitch bend range to twelve semitones by sending registered parameter 0, which is controllers 101, 100, 6 and 38. parameter_reader assembles them into one registered_controller. And when a stuck note needs silencing, the All Notes Off from the same controller arrives through mode_reader as a message of its own. The synth receives two messages, not six controllers.
struct bending_synth : midi::processor
{
using midi::processor::operator();
void operator()(midi::registered_controller msg, std::size_t)
{
if (msg.number() == 0) // pitch bend sensitivity
_bend_range = msg.value() >> 7; // semitones, in the coarse half
}
void operator()(midi::all_notes_off, std::size_t)
{
release_every_voice(); // the panic button
}
int _bend_range = 2;
};
bending_synth synth;
auto chain = midi::parameter_reader{midi::mode_reader{synth}};
Include
#include <q/midi/parameters.hpp> // registered_controller, assignable_controller, parameter_reader
#include <q/midi/controllers.hpp> // control_change_14, cc14_reader
#include <q/midi/modes.hpp> // the channel mode messages, mode_reader
Declaration
namespace cycfi::q::midi_1_0
{
template <typename P>
class parameter_reader
{
public:
static constexpr std::uint16_t null_number = 0x3FFF;
static constexpr std::uint16_t max_value = 0x3FFF;
explicit parameter_reader(P next);
template <typename Message>
void operator()(Message msg, std::size_t time);
void operator()(
control_change msg, std::size_t time);
};
template <typename P>
class cc14_reader
{
public:
static constexpr std::uint8_t pairs = 32;
explicit cc14_reader(P next);
template <typename Message>
void operator()(Message msg, std::size_t time);
void operator()(
control_change msg, std::size_t time);
};
template <typename P>
class mode_reader
{
public:
explicit mode_reader(P next);
template <typename Message>
void operator()(Message msg, std::size_t time);
void operator()(
control_change msg, std::size_t time);
};
}
Expressions
Notation
P-
A type that conforms to
Processor. proc-
Instance of
P. r-
Instance of
parameter_reader<P>,cc14_reader<P>ormode_reader<P>. msg-
Instance of a MIDI message.
time-
A
std::size_ttime stamp.
Constructors
| Expression | Semantics |
|---|---|
|
A stage that reads parameters for |
|
A stage that joins 14 bit controllers for |
|
A stage that reads the channel mode messages for |
Chaining and Order
Stages nest in one expression, and dispatch is called once, at the front:
namespace midi = cycfi::q::midi_1_0;
auto chain = midi::parameter_reader{midi::mode_reader{midi::cc14_reader{my_synth}}};
midi::dispatch(msg, time, chain);
A message goes to the outermost stage first.
parameter_reader belongs outside cc14_reader. Data entry, controllers 6 and 38, is the value of a parameter to the one and a coarse and fine pair to the other. Outermost, parameter_reader takes every data entry first, as the parameter’s value. The other way round, cc14_reader would take them all and no parameter would ever be set. mode_reader reads controllers 120 and up, which neither of the others touches, so it goes anywhere.
|
Parameters
A midi_1_0::parameter is addressed by a 14 bit number and holds a 14 bit value. It is not a message on the wire. Six controllers carry it: 101 and 100 name a registered parameter, 99 and 98 a non-registered one, and 6 and 38, data entry, set the value, with 96 and 97 to step it up and down. A registered parameter has a meaning the specification assigns, pitch bend sensitivity being number 0. An unregistered one means whatever the instrument says it means.
midi_2_0 names these three types the same way, since they mean the same thing in both protocols. The MIDI 2.0 ones carry a 32 bit value and arrive as single messages, and are covered in MIDI 2.0 Messages.
|
parameter_reader assembles them, so a processor reads a whole parameter instead of the halves:
struct parameter : message_base
{
constexpr parameter(
std::uint8_t channel, std::uint16_t number, std::uint16_t value);
constexpr std::uint8_t channel() const;
constexpr std::uint16_t number() const;
constexpr std::uint16_t value() const;
};
struct registered_controller : parameter
{
using parameter::parameter;
};
struct assignable_controller : parameter
{
using parameter::parameter;
};
struct my_synth : midi::processor
{
using midi::processor::operator();
void operator()(midi::registered_controller msg, std::size_t time)
{
if (msg.number() == 0) // pitch bend sensitivity
set_bend_range(msg.value() >> 7); // semitones, in the coarse half
}
};
The selection is kept for each channel and lasts until it is replaced or ended: a controller names a parameter once, then sends data entries for as long as it sweeps it. Each data entry, coarse or fine, reports the parameter with the value so far, and a coarse one clears the fine half, since a device that sends only the coarse half means the value it names. The number arrives in halves too, and the two need not both be sent.
Parameter 127/127, null_number, is the specification’s way of saying "no parameter". It ends the selection, so a data entry that follows a finished gesture lands nowhere. A data entry with no parameter selected is taken and reports nothing.
|
14 Bit Controllers
Controllers 0 to 31 have a partner 32 higher holding the fine half of the same value. The pair is one control. cc14_reader joins them, and the processor behind it receives a control_change_14 with the coarse controller’s number and the value both halves make:
struct control_change_14 : message_base
{
constexpr control_change_14(
std::uint8_t channel, cc::controller ctrl, std::uint16_t value);
constexpr std::uint8_t channel() const;
constexpr cc::controller controller() const;
constexpr std::uint16_t value() const;
};
Controllers 0 to 63 reach the processor behind it as control_change_14 and no longer as control_change. Everything else, 64 and up included, is untouched.
Each half reports as it arrives; the coarse half does not wait for a fine half that in most cases never comes. A controller that sends both reports twice, once coarse and then refined, as a moved fader does anyway. It needs no clock, and a library that may run inside an audio callback has none. A coarse half replaces the value outright, since the fine half it was paired with described the old position.
void operator()(midi::control_change_14 msg, std::size_t time)
{
if (msg.controller() == midi::cc::modulation)
set_modulation(msg.value() / 16383.0f);
}
Channel Modes
Controllers 120 and up are commands to the instrument: stop sounding, forget every controller position, play on one channel or all of them, play one note at a time or many. They are controllers only because MIDI had no room left for new status bytes. mode_reader makes each its own type, so a synth handles the ones it uses and ignores the rest, rather than switching on a controller number.
| Message | Controller | Meaning |
|---|---|---|
|
120 |
Silence every voice at once, envelopes and all. |
|
121 |
Every controller back to its default: wheels centered, pedals up. |
|
122 |
Whether the instrument’s own keyboard still plays its own voices, or only sends. |
|
123 |
Release every sounding note, as a note-off would. Voices in their release stage keep ringing; |
|
124 |
Respond on one channel. |
|
125 |
Respond on every channel. |
|
126 |
One note at a time, over |
|
127 |
Many notes at a time. |
struct channel_mode : message_base
{
constexpr channel_mode(std::uint8_t channel);
constexpr std::uint8_t channel() const;
};
struct local_control : channel_mode
{
constexpr local_control(std::uint8_t channel, bool on);
constexpr bool on() const;
};
struct mono_mode : channel_mode
{
constexpr mono_mode(std::uint8_t channel, std::uint8_t channels);
constexpr std::uint8_t channels() const;
};
all_sound_off, reset_all_controllers, all_notes_off, omni_off, omni_on and poly_mode derive from channel_mode and add nothing to it.
What MIDI 2.0 Does Instead
MIDI 2.0 needs almost none of this. A parameter is a single registered_controller or assignable_controller message there, carrying the same 14 bit address with a 32 bit value. A 14 bit pair has no counterpart at all, since every MIDI 2.0 value is 32 bits already. Channel modes are the exception: they are still controllers 120 to 127, carried by a midi_2_0::control_change, and Q has no midi_2_0::mode_reader, so a MIDI 2.0 processor either tests the controller number itself or puts a translator in front. See MIDI 2.0 Messages and MIDI Translation.