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"}
]
endThen run:
mix deps.get
Quick Start
alias ExMidi.MidiMsg
alias ExMidi.MidiBin
alias ExMidi.MidiMessage
alias ExMidi.MidiFile
alias ExMidi.MidiParserMessage 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=2Streaming 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
| Function | Description |
|---|---|
MidiMsg.note_on/2, /3 | Note on (pitch, velocity) |
MidiMsg.note_off/1, /2, /3 | Note off |
MidiMsg.aftertouch/1, /2 | Channel aftertouch |
MidiMsg.poly_aftertouch/2, /3 | Polyphonic aftertouch |
MidiMsg.pitch_bend/1, /2, /3 | Pitch bend |
MidiMsg.program_change/1, /2 | Program change |
MidiMsg.cc/2, /3 | Control change |
Control Change (Channel Mode)
| Message | Control Value |
|---|---|
| All Sound Off | MidiMsg.cc(1, 120, 0) |
| Reset All Controllers | MidiMsg.cc(1, 121, 0) |
| Local Control Off | MidiMsg.cc(1, 122, 0) |
| All Notes Off | MidiMsg.cc(1, 123, 0) |
| Omni Mode Off | MidiMsg.cc(1, 124, 0) |
| Omni Mode On | MidiMsg.cc(1, 125, 0) |
| Poly Mode On | MidiMsg.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
| Function | Description |
|---|---|
MidiMsg.sys_ex/1 | System Exclusive |
:tune_request | Tune Request |
Real-Time
| Function | Description |
|---|---|
MidiMsg.rt_clock/0 | MIDI clock (24 per quarter) |
MidiMsg.rt_start/0 | Start |
MidiMsg.rt_continue/0 | Continue |
MidiMsg.rt_stop/0 | Stop |
MidiMsg.rt_tick/0 | Tick |
MidiMsg.rt_reset/0 | Reset |
Meta Messages
| Function | Description |
|---|---|
MidiMsg.text/1 | Text event |
MidiMsg.copyright/1 | Copyright notice |
MidiMsg.track_sequence_name/1 | Track/sequence name |
MidiMsg.instrument/1 | Instrument name |
MidiMsg.lyric/1 | Lyric |
MidiMsg.marker/1 | Marker |
MidiMsg.cuepoint/1 | Cue point |
MidiMsg.tempo_bpm/1 | Tempo (BPM) |
MidiMsg.time_sig/4 | Time signature |
MidiMsg.keysig/4 | Key signature |
MidiMsg.smpte/5 | SMPTE offset |
MidiMsg.sequencer_data/1 | Sequencer-specific data |
MidiMsg.sequence_number/1 | Sequence number |
MidiMsg.device/1 | MIDI device port |
MidiMsg.program/1 | Program name |
MidiMsg.undefined/1 | Undefined meta event |
Module Overview
| Module | Description |
|---|---|
ExMidi | Top-level API with version/0 and versions/0 |
ExMidi.MidiMsg | Lightweight tuple-based message constructors |
ExMidi.MidiMessage | Frozen struct with copy/2, to_bytes/1, to_hex/1, to_map/1 |
ExMidi.MidiBin | Binary encode/decode between tuples and wire format |
ExMidi.MidiParser | Streaming byte-by-byte MIDI parser |
ExMidi.MidiFile | Standard MIDI File read/write (Formats 0, 1, 2) |
ExMidi.MidiSyx | SYX file read/write (binary and plaintext hex) |
ExMidi.MidiUtil | Utilities: note names, quantize, BPM conversion, text output |
ExMidi.MidiLib | Library version information |
Development
# Setup
mix setup
# Run tests
mix test
# Code quality
mix quality
License
Apache-2.0