Basic MIDI structures

MIDI files are loaded to/from disc and transformed into a Julia structure MIDIFile, which contains MIDITracks.

MIDI IO

To read and write a MIDI file, the load and save functions can be used. These extend the FileIO interface. The syntax is

load(filename) -> midi::MIDIFile

Load the midi file contained in filename (which must end in ".mid") and return it as a MIDIFile.

save(filename, data::MIDIFile)

Write a MIDIFile as a ".mid" file to the given filename.

save(filename, notes::Notes)

Create a MIDIFile directly from notes, using format 1, and then save it.

MIDIFile

MIDI.MIDIFileType
MIDIFile <: Any

Type representing a file of MIDI data.

Fields

  • format::UInt16 : The format of the file. Can be 0, 1 or 2.
  • tpq::Int16 : The time division of the track, ticks-per-quarter-note.
  • tracks::Array{MIDITrack, 1} : The array of contained tracks.

MIDITrack

The most important field of a MIDIFile is the tracks field. It contains as many tracks as the user wants. The tracks themselves contain all "musical" information in the form of the "events" we mentioned in MIDI: The least you need to know.

MIDI.MIDITrackType
MIDITrack <: Any

MIDITrack is simply a container for TrackEvents, since its only field is events::Vector{TrackEvent}.

Track chunks begin with four bytes spelling out "MTrk", followed by the length (in bytes) of the track (see readvariablelength), followed by a sequence of events.

MIDI.TrackEventType
TrackEvent <: Any

Abstract supertype for all MIDI events.

All track events begin with a variable length time value (see readvariablelength) and have a field named dT which contains it. This number notes after how many ticks since the last event does the current even takes place.

MIDIEvents then resume with a MIDI channel message defined in constants.jl. They're followed by 1 or 2 bytes, depending on the channel message (see MIDI.EVENTTYPETOLENGTH). If no valid channel message is identified, the previous seen channel message is used. After that the MIDI command is encoded.

MetaEvents and SysexEvents both resume with a specific byte (see constants.jl).

The TrackEvent themselves can be broken into three types.

abstract type MIDIEvent <: TrackEvent end

abstract type MetaEvent <: TrackEvent end

struct SysexEvent <: TrackEvent
    dT::Int
    data::Array{UInt8,1}
end

The various midi and meta events found in a midifile have their own types. More information can be found at the Meta Events and MIDI Events sections below.

Typically the most relevant information of a MIDITrack are the notes contained within. For this reason, special functions getnotes and addnotes! exist, which can be found in the Notes page.

Meta Events

MIDI.SequenceNumberEventType
SequenceNumberEvent <: MetaEvent

The SequenceNumberEvent contains the number of a sequence in type 0 and 1 MIDI files, or the pattern number in type 2 MIDI files.

Fields:

  • dT::Int : Delta time in ticks.
  • metatype::UInt8 : Meta type byte of the event.
  • number::Int : Sequence number.
MIDI.TextEventType
TextEvent <: MetaEvent

The TextEvent contains a text within the MIDI file.

Fields:

  • dT::Int : Delta time in ticks.
  • metatype::UInt8 : Meta type byte of the event.
  • text::String : The text in the event.
MIDI.CopyrightNoticeEventType
CopyrightNoticeEvent <: MetaEvent

The CopyrightNoticeEvent contains a copyright notice in a MIDI file.

Fields:

  • dT::Int : Delta time in ticks.
  • metatype::UInt8 : Meta type byte of the event.
  • text::String : The copyright notice in text.
MIDI.TrackNameEventType
TrackNameEvent <: MetaEvent

The TrackNameEvent contains either the name of a MIDI sequence (when in MIDI type 0 or MIDI type 2 files, or when in the first track of a MIDI type 1 file), or the name of a MIDI track (when in other tracks of a MIDI type 1 file).

Fields:

  • dT::Int : Delta time in ticks.
  • metatype::UInt8 : Meta type byte of the event.
  • text::String : The track name in text.
MIDI.InstrumentNameEventType
InstrumentNameEvent <: MetaEvent

The InstrumentNameEvent contains the name of the instrument to be used in a track.

Fields:

  • dT::Int : Delta time in ticks.
  • metatype::UInt8 : Meta type byte of the event.
  • text::String : The instrument name in text.
MIDI.LyricEventType
LyricEvent <: MetaEvent

The LyricEvent contains the lyrics (usually syllables) in a MIDI file.

Fields:

  • dT::Int : Delta time in ticks.
  • metatype::UInt8 : Meta type byte of the event.
  • text::String : The lyric in text.
MIDI.MarkerEventType
MarkerEvent <: MetaEvent

The MarkerEvent contains the text of a marker.

Fields:

  • dT::Int : Delta time in ticks.
  • metatype::UInt8 : Meta type byte of the event.
  • text::String : The marker text.
MIDI.CuePointEventType
CuePointEvent <: MetaEvent

The CuePointEvent contains a cue in a MIDI file.

Fields:

  • dT::Int : Delta time in ticks.
  • metatype::UInt8 : Meta type byte of the event.
  • text::String : The cue in text.
MIDI.MIDIChannelPrefixEventType
MIDIChannelPrefixEvent <: MetaEvent

The MIDIChannelPrefixEvent contains a channel number to which the following meta messages are sent to.

Fields:

  • dT::Int : Delta time in ticks.
  • metatype::UInt8 : Meta type byte of the event.
  • channel::Int : The channel number.
MIDI.EndOfTrackEventType
EndOfTrackEvent <: MetaEvent

The EndOfTrackEvent denotes the end of a track.

Fields:

  • dT::Int : Delta time in ticks.
  • metatype::UInt8 : Meta type byte of the event.
MIDI.SetTempoEventType
SetTempoEvent <: MetaEvent

The SetTempoEvent sets the tempo of a MIDI sequence in terms of microseconds per quarter note.

Fields:

  • dT::Int : Delta time in ticks.
  • metatype::UInt8 : Meta type byte of the event.
  • tempo::Int : The tempo in microseconds per quarter note.
MIDI.TimeSignatureEventType
TimeSignatureEvent <: MetaEvent

The TimeSignatureEvent contains the time signature of a MIDI sequence.

Fields:

  • dT::Int : Delta time in ticks.
  • metatype::UInt8 : Meta type byte of the event.
  • numerator::Int : Numerator of the time signature.
  • denominator::Int : Denominator of the time signature.
  • clockticks::Int : MIDI clock ticks per click.
  • notated32nd_notes::Int : Number of 32nd notes per beat.
MIDI.KeySignatureEventType
KeySignatureEvent <: MetaEvent

The KeySignatureEvent contains the key signature and scale of a MIDI file.

Fields:

  • dT::Int : Delta time in ticks.
  • metatype::UInt8 : Meta type byte of the event.
  • semitones::Int : Number of flats or sharps.
  • scale::Int : Scale of the MIDI file - 0 if the scale is major and 1 if the scale is minor.

MIDI Events

MIDI.NoteOffEventType
NoteOffEvent <: MIDIEvent

The NoteOffEvent informs a MIDI device to release a note.

Fields:

  • dT::Int : Delta time in ticks.
  • status::UInt8 : The status byte of the event.
  • note::Int : Note to turn off.
  • velocity::Int : Velocity of the note.
MIDI.NoteOnEventType
NoteOnEvent <: MIDIEvent

The NoteOnEvent informs a MIDI device to play a note. A NoteOnEvent with 0 velocity acts as a NoteOffEvent.

Fields:

  • dT::Int : Delta time in ticks.
  • status::UInt8 : The status byte of the event.
  • note::Int : Note to turn on.
  • velocity::Int : Velocity of the note.
MIDI.AftertouchEventType
AftertouchEvent <: MIDIEvent

The AftertouchEvent informs a MIDI device to apply pressure to a note.

Fields:

  • dT::Int : Delta time in ticks.
  • status::UInt8 : The status byte of the event.
  • note::Int : Note to apply the pressure to.
  • pressure::Int : Amount of pressure to be applied.
MIDI.ControlChangeEventType
ControlChangeEvent <: MIDIEvent

The ControlChangeEvent informs a MIDI device to change the value of a controller.

Fields:

  • dT::Int : Delta time in ticks.
  • status::UInt8 : The status byte of the event.
  • controller::Int : Controller number.
  • value::Int : Value received by the controller.
MIDI.ProgramChangeEventType
ProgramChangeEvent <: MIDIEvent

The ProgramChangeEvent informs a MIDI device to select a program number in a specific channel.

Fields:

  • dT::Int : Delta time in ticks.
  • status::UInt8 : The status byte of the event.
  • program::Int : The new program number.
MIDI.ChannelPressureEventType
ChannelPressureEvent <: MIDIEvent

The ChannelPressureEvent informs a MIDI device to apply pressure to a specific channel.

Fields:

  • dT::Int : Delta time in ticks.
  • status::UInt8 : The status byte of the event.
  • pressure::Int : Amount of the pressure to be applied.
MIDI.PitchBendEventType
PitchBendEvent <: MIDIEvent

The PitchBendEvent informs a MIDI device to modify the pitch in a specific channel.

Fields:

  • dT::Int : Delta time in ticks.
  • status::UInt8 : The status byte of the event.
  • pitch::Int : Value of the pitch bend.

Utility functions

MIDI.qpmFunction
qpm(midi)

Return the QPM (quarter notes per minute) where the given MIDIFile was exported at. Returns 120 if not found.

MIDI.bpmFunction
bpm(midi)

Return the BPM where the given MIDIFile was exported at. Returns QPM if not found.

MIDI.ms_per_tickFunction
ms_per_tick(tpq, qpm)
ms_per_tick(midi::MIDIFile)

Return how many milliseconds is one tick, based on the quarter notes per minute qpm and ticks per quarter note tpq.

MIDI.addevent!Function
addevent!(track::MIDITrack, time::Int, event::TrackEvent)

Add an event to the track at given time. The time is in absolute time, not relative.

If you want to add multiple events in one go, you should use the addevents! function instead.

MIDI.addevents!Function
addevents!(track::MIDITrack, times, events)

Add given events to given track at given times, internally doing all translations from absolute time to relative time.

Using this function is more efficient than a loop over single addevent! calls.

MIDI.tracknameFunction
trackname(track::MIDI.MIDITrack)

Return the name of the given track as a string, by finding the TrackNameEvent.

If no such event exists, "No track name found" is returned.

MIDI.addtrackname!Function
addtrackname!(track::MIDI.MIDITrack, name::String)

Add a name to the given track by attaching the TrackNameEvent to the start of the track.

MIDI.findtexteventsFunction
findtextevents(eventtype, track)

Find all text events specifield by eventtype in the track. The eventtype can be `TextEvent, LyricEvent, MarkerEvent, which will find the appropriate meta events.

For convenience, this function does not return the events themselves. Instead, it returns three vectors: the first is the strings of the events, the second is the indices of the events in the track and the third is the absolute position of the events (since start of track).

Notice - common music score editors like e.g. MuseScore, GuitarPro, etc., do not export the lyrics and text information when exporting midi files.

Notice - Cubase can read the marker events and MuseScore can read the lyrics events. We haven't seen any editor that can read the text events, so far.

MIDI.tempochangesFunction
tempochanges(midi)

Return a vector of (position, tempo) tuples for all the tempo events in the given MIDIFile where position is in absolute time (from the beginning of the file) in ticks and tempo is in quarter notes per minute. Returns [(0, 120.0)] if there are no tempo events.

Low-Level API

In this section we show the low-level API that allows one to actually read bytes from a file and transform them into Julia structures.

MIDI.readvariablelengthFunction
readvariablelength(f::IO)

Variable length numbers in MIDI files are represented as a sequence of bytes. If the first bit is 0, we're looking at the last byte in the sequence. The remaining 7 bits indicate the number.

Other useful functions that are not exported are

writeevent
readMIDIevent
readmetaevent
readsysexevent
get_abs_pos

Lastly, see the file MIDI/src/constants.jl for message types, event types, etc.

MIDI: The least you need to know

This section serves as a crash-course on the MIDI format. For more info see the wikipedia page, read the official MIDI specifications or have a look at the comprehensive tutorial at recordingblogs.com.

A MIDI file typically comes in pieces called tracks that play simultaneously. Each track can have 16 different channels, numbered 0-15. Each channel can be thought of as a single instrument, though that instrument can be changed throughout that track. A track contains events. The three types of events are MIDI events, META events, and system exclusive (SYSEX) events. All events begin with the time since the last event (dT) in ticks. The number of ticks per quarter note is given by the tpq of the midi file, MIDIFile.tpq (see MIDIFile).

  • MIDI events handle things related to the actual notes as well as sound texture, such as playing a note or moving the pitch-wheel.

  • META events take care of things like adding copyright text, authorship information, track naming etc.

  • SYSEX events are used to transmit arbitrary data. Their contents depend on the intended recipient.