View Source Midiex.Message (Midiex v0.6.3)

Convenience functions for creating binary MIDI messages.

midi-message-functions

MIDI message functions

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.

notes

Notes

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.

example

Example

alias Midiex.Message, as: M
# Connect to the synth, in this case the Arturia MicroFreak synth
synth = Midiex.ports("Arturia MicroFreak", :output) |> Midiex.open()

# Send the note on message, for D3
Midiex.send_msg(synth, M.note_on(:D3))

# Wait 1 second
:timer.sleep(1000)

# Send the note off message, for D3
Midiex.send_msg(synth, M.note_off(:D3))

about-midi-messages

About MIDI messages

MIDI messages are in the format of:

status_byte + note_number (0-127) + velocity (0-127)

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:

<<0x90, 60, 127>>

resolution-bits

Resolution (bits)

coarse-resolution

Coarse resolution

This type of message format is sometimes called 'coarse' or 7-bit MIDI, as it takes a maximum of 128 values only (from 0 to 127).

high-resolution

High resolution

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

<<msb::7, lsb::7>> = <<mod_wheel_value::14>>
msb_binary = control_change(1, msb)
lsb_binary = control_change(0x21, lsb)
<<msb_binary::binary, lsb_binary::binary>>

more-information

More information

https://www.midi.org/midi-articles/about-midi-part-3-midi-messages

Link to this section Summary

Channel voice messages

Creates a channel aftertouch (also known as channel pressure) message.

Creates a MIDI CC or 'control change' message.

Creates a MIDI note-off message.

Creates a MIDI note-on message.

Creates a pitch bend message, representing a change in pitch.

Creates a polyphonic aftertouch message.

Creates a program change message, used select the instrument type to play sounds with or a different 'patch'.

Channel change messages

Controls the left and right balance, generally for stereo patches.

Creates a bank select (also known as bank switch) message.

Creates a breath controller messsage.

Creates an data entry message for MSB.

Creates a foot or pedal controller messsage.

Creates a modulation (mod) wheel message.

Change the panoramic (pan) of a channel.

Creates a portamento (slide or glide) time messsage.

Creates a volume messsage. This was formally called 'Main Volume' in the MIDI 1.0 spec.

Channel mode messages

Creates an all notes off message. This mutes all sounding notes.

Switches local control on or off by creating a local control message.

Creates a message which will set the device to monophonic mode.

Creates a message which will set the device to polyphonic mode.

Creates a message that will reset all controllers to their default.

Creates an all sound off message. This mutes all sound regardless of release time or sustain.

System messages

Creates a MIDI active sense message.

Creates a MIDI clock message, used for clock synchronization.

Creates a MIDI quarter frame message, used to send timing information.

Creates a MIDI reset message.

Creates a MIDI continue message.

Creates a MIDI start message.

Creates a MIDI stop message.

Creates a system exclusive message, also known as a SysEx message.

Creates a eight MIDI quarter frame messages to represent a single hours:minutes:seconds:frames timecode string.

Functions

Returns the MIDI numerical code for a note.

Link to this section Channel voice messages

Link to this function

channel_aftertouch(note, pressure, opts \\ [])

View Source

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:

  1. note: a note as a string (e.g. "C4"), atom (e.g. :C4) or number (e.g. 60) as the first parameter
  2. 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.

example

Example

alias Midiex.Message

# Create a series of aftertouch messages from 0 to 127 for the note middle-C (C4)
0..127//1
|> Enum.map(fn pressure -> Message.channel_aftertouch(:C4, pressure: pressure) end)
Link to this function

control_change(control_number, value \\ 0, opts \\ [])

View Source

Creates a MIDI CC or 'control change' message.

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:

  1. contoller number: a number between 0-119. Controller numbers between 120-127 are reserved as "Channel Mode Messages".
  2. 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.

example

Example

The MIDI CC message of 123 equates to "All Notes Off" (a Channel Mode Message), thus stopping all notes being played.

# Create a 'all notes off' CC message.
Midiex.control_change(127)

reference

Reference

See the official MIDI 1.0 Control Change Messages Spec.

Link to this function

note_off(note, velocity \\ 123, opts \\ [])

View Source

Creates a MIDI note-off message.

Takes as it's parameters:

  1. note: a note as a string (e.g. "C4"), atom (e.g. :C4) or number (e.g. 60) as the first parameter
  2. 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.

example

Example

# Note-off for middle-C
Message.note_off(:C4)

# Returns: <<128, 60, 127>>
Link to this function

note_on(note, velocity \\ 127, opts \\ [])

View Source

Creates a MIDI note-on message.

Takes as it's parameters:

  1. note: a note as a string (e.g. "C4"), atom (e.g. :C4) or number (e.g. 60) as the first parameter
  2. 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.

examples

Examples

# Note-on message for middle-C
Midiex.Message.note_on(:C4)

# Returns: <<144, 60, 127>>

# Note-on message for middle-C on channel 2 with a velocity of 40
Midiex.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:

alias Midiex.Message
Midiex.send_msg(out_conn, Message.note_on(:C4, 40, channel: 2))
Link to this function

pitch_bend(bend, opts \\ [])

View Source

Creates a pitch bend message, representing a change in pitch.

Pitch bend change messages a usually sent from a keyboard with a pitch bend wheel or lever.

Takes as it's first parameter, the pitch bend amount. The channel cane be passed as an option.

The range of a pitch bend is as follows:

  • 0-8191 represent negative bends,
  • 8192 (Hex: 0x2000) is no bend and
  • 8193-16383 are positive bends

example

Example

alias Midiex.Message, as: M

# Play a note
Midiex.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(fn pitch -> Midiex.send_msg(piano, M.pitch_bend(pitch)) end)
Link to this function

polyphonic_aftertouch(note, pressure, opts \\ [])

View Source

Creates a polyphonic aftertouch message.

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:

  1. note: a note as a string (e.g. "C4"), atom (e.g. :C4) or number (e.g. 60) as the first parameter
  2. 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.

example

Example

alias Midiex.Message

# Create a series of aftertouch messages from 0 to 127 for the note middle-C (C4)
0..127//1
|> Enum.map(fn pressure -> Message.polyphonic_aftertouch(:C4, pressure) end)
Link to this function

program_change(prog_num, opts \\ [])

View Source

Creates a program change message, used select the instrument type to play sounds with or a different 'patch'.

This function takes one data byte which specifies the new program number.

To apply this to a particular channel, use the channel option, e.g.:

Message.program_change(1, channel: 3)

Link to this section Channel change messages

Link to this function

balance(value, opts \\ [])

View Source

Controls the left and right balance, generally for stereo patches.

A value of 64 equals the center.

Values below 64 moves the sound to the left, and above to the right.

example

Example

Midiex.Message.balance(64)

14-bit-version

14-bit version

If you want a high-resolution balance message (e.g. in the range of 0-16383), you can pass the option high_res: true, e.g.:

# Center channel (high res)
Midiex.Message.balance(8191, high_res: true)

# Returns:
<<176, 8, 63, 176, 40, 127>>
Link to this function

bank_select(bank, opts \\ [])

View Source

Creates a bank select (also known as bank switch) message.

Takes a bank number as it's first parameter, in the range of 0-16383. The number of banks is dependent on the device.

A channel number can be provided as an option.

Link to this function

breath_controller(value, opts \\ [])

View Source

Creates a breath controller messsage.

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.

A channel number can be provided as an option.

Link to this function

data_entry_msb(value, opts \\ [])

View Source

Creates an data entry message for MSB.

Used to control the value for NRPN (Non-Registered Parameter Number) or RPN (Registered Parameter Number) parameters.

Takes a number as it's first parameter, in the range of 0-16383.

A channel number can be provided as an option.

Link to this function

foot_controller(value, opts \\ [])

View Source

Creates a foot or pedal controller messsage.

Takes a number as it's first parameter, in the range of 0-16383.

A channel number can be provided as an option.

Link to this function

mod_wheel(bank, opts \\ [])

View Source

Creates a modulation (mod) wheel message.

Modulation wheels are often used for vibrato effects (pitch, loudness, brighness), however what is modulated is based on the patch.

Takes a number as it's first parameter, in the range of 0-16383.

A channel number can be provided as an option.

Change the panoramic (pan) of a channel.

This shifts the sound from the left or right ear in when playing stereo.

Values below 64 moves the sound to the left, and above to the right.

example

Example

# Pan to middle
Midiex.Message.pan(64)

14-bit-version

14-bit version

If you want a high-resolution pan message (e.g. in the range of 0-16383), you can pass the option high_res: true.

# Pan to middle (high res version)
Midiex.Message.pan(8191, high_res: true)
Link to this function

portamento(value, opts \\ [])

View Source

Creates a portamento (slide or glide) time messsage.

Portamento is the rate to slide between 2 notes played in sequence, sliding the pitch up or down from one note to the next.

Takes a number as it's first parameter, in the range of 0-16383.

A channel number can be provided as an option.

Link to this function

volume(volume_num, opts \\ [])

View Source

Creates a volume messsage. This was formally called 'Main Volume' in the MIDI 1.0 spec.

Takes a number as it's first parameter, in the range of 0-127.

For example, to set to maximum volume:

Midiex.Message.volume(127)

# Returns:
<<176, 7, 127>>

14-bit-version

14-bit version

If you want a high-resolution volume message (e.g. in the range of 0-16383), you can pass the option high_res: true, e.g.:

Midiex.Message.volume(16383, high_res: true)

# Returns:
<<176, 7, 127, 176, 39, 127>>

A channel number can be provided as an option.

Link to this section Channel mode messages

Link to this function

all_notes_off(opts \\ [])

View Source

Creates an all notes off message. This mutes all sounding notes.

The release time will still be maintained.

Notes held by sustain will not turn off until sustain pedal is depressed.

Link to this function

local_control(true_or_false \\ true, opts \\ [])

View Source

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.

More information at: https://electronicmusic.fandom.com/wiki/Local_control

Link to this function

mono_mode(true_or_false \\ true, number_of_channels \\ 0, opts \\ [])

View Source
@spec mono_mode(boolean(), any(), keyword()) :: <<_::24>>

Creates a message which will set the device to monophonic mode.

Takes as it's parameters:

  1. true to set mono mode on, or false to switch on poly mode
  2. the number of channels, or 0 if the number of channels equals the number of voices in the receiver.

example

Example

# Set device to monophonic mode
Midiex.Message.mono_mode()

# Returns:
<<176, 126, 0>>
Link to this function

omni_mode(true_or_false \\ true, opts \\ [])

View Source

Sets Omni mode on or off.

The first parameter takes one of the following booleans:

  • true to switch Omni mode on
  • false to swithc Omni mode off
Link to this function

poly_mode(true_or_false \\ true, opts \\ [])

View Source

Creates a message which will set the device to polyphonic mode.

Takes as it's parameters:

  1. true to set poly mode on, or false to switch on mono mode (or use mono_mode)

example

Example

Midiex.Message.poly_mode()

# Returns
<<176, 127, 0>>
Link to this function

reset_controllers(opts \\ [])

View Source

Creates a message that will reset all controllers to their default.

Creates an all sound off message. This mutes all sound regardless of release time or sustain.

Link to this section System messages

Creates a MIDI active sense message.

Used to tell listening devices that the MIDI connection is still active.

Creates a MIDI clock message, used for clock synchronization.

The MIDI clock message is a timing message that is sent at regular intervals to tell the listening devices where it is in terms of time.

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.

Creates a MIDI reset message.

Various MIDI devices will interpret this message differently. Often it will cause a device to stop playing and set the song position to the beginning.

The MIDI 1.0 specification says the following should occur when a reset message is sent:

  • The modulation wheel, hold pedal, portamento pedal, sostenuto pedal and soft pedal are set to 0
  • The pitch wheel is set to center which is usually 64, but could be set to 0
  • The channel pressure and the key pressure are set to 0
  • Registered and nonregistered parameter numbers (98 to 101) are set to 127
  • Expression is set to 127.

Creates a MIDI continue message.

Used to tell listening devices to resume playback of the current MIDI sequence.

Creates a MIDI start message.

Used to tell listening devices to commence playback of the current MIDI sequence.

Creates a MIDI stop message.

Used to tell listening devices to stop playing the current MIDI sequence.

Creates a system exclusive message, also known as a SysEx message.

This function takes the following parameters:

  1. SysEx ID number: A code representing the device manufacturer, see the offical list of manurfacturer IDs
  2. Data: series of hex data bytes representing the message body. The hex data bytes values are between 0x00 and 0x7F (0 and 127).

about-sysex

About SysEx

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.:

<<0xF0, id_number, data, 0xF7>>

example

Example

# 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 instead
Midiex.Message.sysex(65, <<1, 52>>)

# Returns <<240, 65, 1, 52, 247>>
Link to this function

timecode(timecode_string)

View Source

Creates a eight MIDI quarter frame messages to represent a single hours:minutes:seconds:frames timecode string.

example

Example

Midiex.Message.timecode("01:00:00:00")

# Returns
# <<241, 0, 241, 16, 241, 32, 241, 48, 241, 64, 241, 80, 241, 97, 241, 112>>

Link to this section Functions

Returns the MIDI numerical code for a note.

Takes either a string or atom representation of a note as the first parameter.

example

Example

# 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 3
Message.note(:As3)
Message.note("A#3")

# These both return: 58