So that you don't have to remember all the MIDI message codes, this library has a large range of message generating functions. Some common types are below:
Various system messages are supported too, such as sysex/1.
Some functions have an optional high-resolution (14-bit) version. This is activated by providing the high_res: true option, e.g.: Midiex.Message.volume(16383, high_res: true). You can learn more about the resolution of MIDI messages below.
Functions that take notes as a parameter can accept a number, string or atom note representation. For example, middle-C can be represented as 60, "C4" or :C4.
Taking the note_on/1 function as an example, generating a "note on" MIDI message for middle-C <<144, 60, 127>> can be achieved in any of these ways:
Message.note_on(60)
Message.note_on("C4")
Message.note_on(:C4)
To get the MIDI number for a string or atom note representation, see note/1.
aliasMidiex.Message,as:M# Connect to the synth, in this case the Arturia MicroFreak synthsynth=Midiex.ports("Arturia MicroFreak",:output)|>Midiex.open()# Send the note on message, for D3Midiex.send_msg(synth,M.note_on(:D3))# Wait 1 second:timer.sleep(1000)# Send the note off message, for D3Midiex.send_msg(synth,M.note_off(:D3))
For example, taking the status byte for Note On which in HEX is 0x90, and the note Middle C which is 60 and a maximum key velocity of 127, the MIDI message in binary format is:
However, 'high resolution' or 14-bit MIDI messages are possible, giving a maximum of 16,384 values (from 0 to 16,383). When working with controllers such as mod wheels, it allows for finer and smoother changes.
To achieve this higher 14-bit resolution, the MIDI message combines two 7-bit values called:
MSB (Most Significant Byte), which is used for 'coarser' values. This is the bit which has the greatest effect.
LSB (Least Significant Byte), which is used for 'finer' values.
In Elixir, We can unpack a 14-bit value into it's MSB and LSB bits using binary pattern matching, e.g.:
<<msb::7, lsb::7>> = <<value::14>>
The MSB and LSB values are sent as two different messages. Below is what it would look like for a Mod wheel which uses two control change command messages (0x01 and 0x21) to send the MSB and LSB respectively
Creates a channel aftertouch (also known as channel pressure) message.
On a keyboard, an aftertouch message is sent by pressing down further on keys after it has already reached the bottom. Not all keyboards have aftertouch.
With channel aftertouch, one of the following is used; Either the:
average amount of pressure of all the keys held down; or the
single greatest pressure value of all the current depressed keys.
Therefore channel aftertouch is independent of which key or how many keys are held. polyphonic_aftertouch is specific to each key however.
This function takes as it's parameters:
note: a note as a string (e.g. "C4"), atom (e.g. :C4) or number (e.g. 60) as the first parameter
pressure: a number between 0 and 127 representing the pressure on the key. By defaut 127 is used.
The following option can also be passed:
channel: the MIDI channel to which the message will be sent (there are 16 channels per MIDI device, in the range 0 to 15). By default channel 0 is used.
aliasMidiex.Message# Create a series of aftertouch messages from 0 to 127 for the note middle-C (C4)0..127//1|>Enum.map(fnpressure->Message.channel_aftertouch(:C4,pressure:pressure)end)
MIDI Control Change messages are used to control functions in a synthesiser. Controllers include devices such as pedals, levers/sliders, wheels, switches and other control-oriented devices.
This function takes as its parameters:
contoller number: a number between 0-119. Controller numbers between 120-127 are reserved as "Channel Mode Messages".
value: depends on the control function, but usually is a a number between 0 and 127. See the MIDI 1.0 Control Change Messages Spec or consult the MIDI device manual for specific codes an values.
The following option can be passed:
channel: the MIDI channel to which the message will be sent (there are 16 channels per MIDI device, in the range 0 to 15). By default channel 0 is used.
note: a note as a string (e.g. "C4"), atom (e.g. :C4) or number (e.g. 60) as the first parameter
velocity: a number between 0 and 127 representing how hard (or loud) a key was pressed. By defaut 127 is used.
The following options can be passed:
channel: the MIDI channel to which the message will be sent (there are 16 channels per MIDI device, in the range 0 to 15). By default channel 0 is used.
All notes can be switched off with Message.all_notes_off/1.
note: a note as a string (e.g. "C4"), atom (e.g. :C4) or number (e.g. 60) as the first parameter
velocity: a number between 0 and 127 representing how hard (or loud) a key was pressed. By defaut 127 is used.
The following options can be passed:
channel: the MIDI channel to which the message will be sent (there are 16 channels per MIDI device, in the range 0 to 15). By default channel 0 is used.
Note that MIDI channels are in the range 0 - 15. But in MIDI software and hardware it may be offset by +1, so MIDI channel 0 might be called MIDI channel 1 and so on to channel 16.
# Note-on message for middle-CMidiex.Message.note_on(:C4)# Returns: <<144, 60, 127>># Note-on message for middle-C on channel 2 with a velocity of 40Midiex.Message.note_on(:C4,40,channel:2)# Returns: <<146, 60, 40>>
These can be sent to a connection using Midiex.send_msg/2, for example:
aliasMidiex.Message,as:M# Play a noteMidiex.send_msg(piano,M.note_on(:D3))# Bend up, then down, then back to center (8192)(Enum.to_list(8193..16383//1)++Enum.to_list(8191..0//-1)++[8192])|>Enum.each(fnpitch->Midiex.send_msg(piano,M.pitch_bend(pitch))end)
On a keyboard, polyphonic aftertouch (or key pressure) is message sent by pressing down further on a key after it has already reached the bottom. Not all keyboards have aftertouch.
Note: polyphonic_aftertouch is specific to each key, where as channel_aftertouch is average amount of pressure applied to whichever keys are held down.
The function takes as it's parameters:
note: a note as a string (e.g. "C4"), atom (e.g. :C4) or number (e.g. 60) as the first parameter
pressure: a number between 0 and 127 representing the pressure on the key. By defaut 127 is used.
The following options can be passed:
channel: the MIDI channel to which the message will be sent (there are 16 channels per MIDI device, in the range 0 to 15). By default channel 0 is used.
aliasMidiex.Message# Create a series of aftertouch messages from 0 to 127 for the note middle-C (C4)0..127//1|>Enum.map(fnpressure->Message.polyphonic_aftertouch(:C4,pressure)end)
Breath controller messages were originally intended for use with a breath MIDI controller. Blowing harder into the breath controller would produce higher MIDI control values.
Outside of breath control, is can be associated with aftertouch messages or used for modulation.
Takes a number as it's first parameter, in the range of 0-16383.
Switches local control on or off by creating a local control message.
An example of local control is that you may want the synthesizer to be played by means of its own keyboard, therefore setting Midiex.Message.local_control(true).
If you were controlling it from a PC only, and didn't want it to have local control, you could send the Midiex.Message.local_control(false) message.
Creates a MIDI quarter frame message, used to send timing information.
Takes a single byte as the first parameter.
Timing information is in the MIDI time code format, which is hours:minutes:seconds:frames. This follows the same timing information as standard SMPTE timecode.
As MIDI send values in the range of 0-127, a single byte cannot carry the full time. For this reason, 8 quarter frame messages must be sent to piece together the current MIDI time.
The timecode/1 function will automatically convert a hours:minutes:seconds:frames timecode string create the individual quarter frames.
SysEx messages can contain any number of hexadecimal bytes. They the message data is specific to each manufacturers device and include the manufacturer's identification code.
SysEx messages are wrapped in a start (0xF0) and end (0xF7) byte, e.g.:
# Send data to a Roland device# Roland uses the ID of 0x41 (or you can pass the integer of 65)Midiex.Message.sysex(0x41,<<0x01,0x34>>)# Returns <<240, 65, 1, 52, 247>># You can pass integer values insteadMidiex.Message.sysex(65,<<1,52>>)# Returns <<240, 65, 1, 52, 247>>
# Return the code for middle-C (also known as C4)Message.note(:C4)Message.note("C4")Message.note(:MiddleC)Message.note("MiddleC")# These all return: 60# Return the code for A-sharp 3Message.note(:As3)Message.note("A#3")# These both return: 58