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

parameter_reader

Controllers 6, 38 and 98 to 101, as one parameter.

This page

cc14_reader

Controllers 0 to 63, as one 14 bit value.

This page

mode_reader

Controllers 120 and above, as channel mode messages.

This page

to_midi1, to_midi2

Messages of either protocol, as the other’s.

MIDI Translation

mpe_reader

MPE channels, as per-note expression.

Per-Note Expression

per_note_reader

MIDI 2.0 per-note messages, as the same expression.

Per-Note Expression

stream_responder, endpoint_inquiry

Stream messages, as an endpoint’s description.

MIDI Endpoint

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_reader holds the parts and delivers one registered_controller or assignable_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_reader joins the pair and delivers one control_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_reader delivers each as a message of its own, all_sound_off and 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> or mode_reader<P>.

msg

Instance of a MIDI message.

time

A std::size_t time stamp.

Constructors

Expression Semantics

parameter_reader{proc}

A stage that reads parameters for proc. P is deduced: an lvalue is referred to and a temporary is owned, so a chain can be built in one expression and kept.

cc14_reader{proc}

A stage that joins 14 bit controllers for proc, in the same way.

mode_reader{proc}

A stage that reads the channel mode messages for proc, in the same way.

Function Call

Expression Semantics Return Type

r(msg, time)

Read msg if it is a control_change this stage reads, calling the processor behind it with what it means. Pass any other message through.

void

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.

Four control changes, the last two each delivering a parameter
Figure 1. parameter_reader names the parameter from the first pair and delivers nothing. Each data entry after it delivers the parameter with the value so far, so a coarse half alone is never lost.
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:

A 7 bit controller as one message and a 14 bit controller as two
Figure 2. A 7 bit controller is one control_change. A 14 bit controller is two, the coarse half on controller n and the fine half on n + 32, and cc14_reader joins them into one value.
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.

Three control changes above 120, each becoming a named message
Figure 3. A channel mode is one control_change, which mode_reader delivers as a message of its own. The value means something different in each, or nothing at all.
Message Controller Meaning

all_sound_off

120

Silence every voice at once, envelopes and all.

reset_all_controllers

121

Every controller back to its default: wheels centered, pedals up.

local_control

122

Whether the instrument’s own keyboard still plays its own voices, or only sends. on() is true for a value of 64 and up.

all_notes_off

123

Release every sounding note, as a note-off would. Voices in their release stage keep ringing; all_sound_off silences them.

omni_off

124

Respond on one channel.

omni_on

125

Respond on every channel.

mono_mode

126

One note at a time, over channels() channels. Zero means every channel the instrument has.

poly_mode

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.

A MIDI 2.0 control_change with its 32 bit value
Figure 4. A MIDI 2.0 control_change. The value is a whole 32 bit word, so there is no fine half and nothing to join.