Skip to main content

lib/ex_midi.ex

defmodule ExMidi do
  @moduledoc """
  Batteries-included Elixir MIDI.

  `ExMidi` provides a unified API for MIDI message construction, binary
  encode/decode, Standard MIDI File read/write, SYX file support, and a
  streaming MIDI parser.

  ## Message terms

  All MIDI messages are represented as `{:midi, payload}` tuples:

      {:midi, {:note_on, [channel: 1, pitch: 60, velocity: 100]}}

  ## Modules

  - `ExMidi.MidiMsg` — lightweight message constructors (tuple-based)
  - `ExMidi.MidiMessage` — frozen message struct with rich API (copy, hex, dict, etc.)
  - `ExMidi.MidiBin` — binary encode/decode of MIDI messages
  - `ExMidi.MidiParser` — streaming byte-by-byte MIDI parser
  - `ExMidi.MidiFile` — Standard MIDI File read/write (Format 0/1/2)
  - `ExMidi.MidiSyx` — SYX file read/write
  - `ExMidi.MidiUtil` — utility functions
  - `ExMidi.MidiLib` — version information

  ## Quick start

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

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

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

      # Frozen message struct
      msg = ExMidi.MidiMessage.new(:note_on, channel: 1, pitch: 60, velocity: 100)
      ExMidi.MidiMessage.to_hex(msg)
      # => "90 3C 64"

      # Streaming parser
      parser = ExMidi.MidiParser.new()
      parser = ExMidi.MidiParser.feed_bytes(parser, [144, 60, 100])
      {msg, _} = ExMidi.MidiParser.parse(parser)

      # MIDI file
      midi = ExMidi.MidiFile.read("song.mid")
      for msg <- ExMidi.MidiFile.play(midi), do: IO.inspect(msg)
  """

  @doc "Returns the current library version."
  def version, do: ExMidi.MidiLib.version()

  @doc "Returns all version information."
  def versions, do: ExMidi.MidiLib.versions()
end