Elixir MIDI library — message construction, binary encode/decode, Standard MIDI File (SMF) read/write, SYX support, and streaming parser. Inspired by midi (Erlang) & mido (Python) lib.

All MIDI messages are represented as {:midi, payload} tuples, providing a unified API across all modules.


Installation

Add ex_midi to your mix.exs:

def deps do
  [
    {:ex_midi, "~> 0.1.0"}
  ]
end

Then run:

mix deps.get

Quick Start

alias ExMidi.MidiMsg
alias ExMidi.MidiBin
alias ExMidi.MidiMessage
alias ExMidi.MidiFile
alias ExMidi.MidiParser

Message construction (tuple-based)

msg = MidiMsg.note_on(1, 60, 100)
# => {:midi, {:note_on, [channel: 1, pitch: 60, velocity: 100]}}

msg = MidiMsg.note_on(60, 100)
# => {:midi, {:note_on, [pitch: 60, velocity: 100]}}

Binary encode/decode

MidiBin.encode(MidiMsg.note_on(1, 60, 100))
# => <<144, 60, 100>>

MidiBin.decode(<<144, 60, 100>>)
# => {:midi, {:note_on, [channel: 1, pitch: 60, velocity: 100]}}

Frozen message struct (rich API)

msg = MidiMessage.new(:note_on, channel: 1, pitch: 60, velocity: 100)

MidiMessage.to_bytes(msg)
# => <<144, 60, 100>>

MidiMessage.to_hex(msg)
# => "90 3C 64"

MidiMessage.copy(msg, channel: 2)
# =>  MidiMessage with channel=2

Streaming parser

parser = MidiParser.new()
parser = MidiParser.feed_bytes(parser, [144, 60, 100])
{msg, parser} = MidiParser.parse(parser)
# => {:midi, {:note_on, [channel: 1, pitch: 60, velocity: 100]}}

MIDI File read/write

# Read
midi = MidiFile.read("song.mid")

# Inspect
MidiFile.format(midi)    # 0, 1, or 2
MidiFile.division(midi)  # ticks per quarter note
MidiFile.tracks(midi)    # list of tracks

# Playback simulation
for msg <- MidiFile.play(midi), do: IO.inspect(msg)

SYX (System Exclusive) files

alias ExMidi.MidiSyx

# Read
messages = MidiSyx.read_file("patch.syx")

# Write (binary format)
MidiSyx.write_file("patch.syx", messages)

# Write (plaintext hex format)
MidiSyx.write_file("patch.syx", messages, plaintext: true)

Message Types

Channel Voice

FunctionDescription
MidiMsg.note_on/2, /3Note on (pitch, velocity)
MidiMsg.note_off/1, /2, /3Note off
MidiMsg.aftertouch/1, /2Channel aftertouch
MidiMsg.poly_aftertouch/2, /3Polyphonic aftertouch
MidiMsg.pitch_bend/1, /2, /3Pitch bend
MidiMsg.program_change/1, /2Program change
MidiMsg.cc/2, /3Control change

Control Change (Channel Mode)

MessageControl Value
All Sound OffMidiMsg.cc(1, 120, 0)
Reset All ControllersMidiMsg.cc(1, 121, 0)
Local Control OffMidiMsg.cc(1, 122, 0)
All Notes OffMidiMsg.cc(1, 123, 0)
Omni Mode OffMidiMsg.cc(1, 124, 0)
Omni Mode OnMidiMsg.cc(1, 125, 0)
Poly Mode OnMidiMsg.cc(1, 127, 0)

Use MidiMsg.cc(channel, control, value) for generic CC messages, or MidiBin.encode/1 on the {:mode, ...} tuple for strict mode messages.

System Common

FunctionDescription
MidiMsg.sys_ex/1System Exclusive
:tune_requestTune Request

Real-Time

FunctionDescription
MidiMsg.rt_clock/0MIDI clock (24 per quarter)
MidiMsg.rt_start/0Start
MidiMsg.rt_continue/0Continue
MidiMsg.rt_stop/0Stop
MidiMsg.rt_tick/0Tick
MidiMsg.rt_reset/0Reset

Meta Messages

FunctionDescription
MidiMsg.text/1Text event
MidiMsg.copyright/1Copyright notice
MidiMsg.track_sequence_name/1Track/sequence name
MidiMsg.instrument/1Instrument name
MidiMsg.lyric/1Lyric
MidiMsg.marker/1Marker
MidiMsg.cuepoint/1Cue point
MidiMsg.tempo_bpm/1Tempo (BPM)
MidiMsg.time_sig/4Time signature
MidiMsg.keysig/4Key signature
MidiMsg.smpte/5SMPTE offset
MidiMsg.sequencer_data/1Sequencer-specific data
MidiMsg.sequence_number/1Sequence number
MidiMsg.device/1MIDI device port
MidiMsg.program/1Program name
MidiMsg.undefined/1Undefined meta event

Module Overview

ModuleDescription
ExMidiTop-level API with version/0 and versions/0
ExMidi.MidiMsgLightweight tuple-based message constructors
ExMidi.MidiMessageFrozen struct with copy/2, to_bytes/1, to_hex/1, to_map/1
ExMidi.MidiBinBinary encode/decode between tuples and wire format
ExMidi.MidiParserStreaming byte-by-byte MIDI parser
ExMidi.MidiFileStandard MIDI File read/write (Formats 0, 1, 2)
ExMidi.MidiSyxSYX file read/write (binary and plaintext hex)
ExMidi.MidiUtilUtilities: note names, quantize, BPM conversion, text output
ExMidi.MidiLibLibrary version information

Development

# Setup
mix setup

# Run tests
mix test

# Code quality
mix quality

License

Apache-2.0