Basic MIDI structures
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.MIDIFile — Type
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.MIDITrack — Type
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.
MIDITrack implements the isempty and empty! functions.
#
MIDI.TrackEvent — Type
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.
Meta Events
#
MIDI.SequenceNumberEvent — Type
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.TextEvent — Type
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.CopyrightNoticeEvent — Type
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.TrackNameEvent — Type
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.InstrumentNameEvent — Type
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.LyricEvent — Type
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.MarkerEvent — Type
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.CuePointEvent — Type
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.MIDIChannelPrefixEvent — Type
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.EndOfTrackEvent — Type
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.SetTempoEvent — Type
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.TimeSignatureEvent — Type
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.KeySignatureEvent — Type
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.NoteOffEvent — Type
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.NoteOnEvent — Type
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.AftertouchEvent — Type
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.ControlChangeEvent — Type
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.ProgramChangeEvent — Type
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.ChannelPressureEvent — Type
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.PitchBendEvent — Type
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.qpm — Function
qpm(midi)
Return the initial QPM (quarter notes per minute) where the given MIDIFile was exported at. This value is constant, and will not change even if the tempo change event is triggered. Returns 120 if not found. To get a list of QPM over time, use tempochanges.
#
MIDI.bpm — Function
bpm(midi)
Return the BPM where the given MIDIFile was exported at. Returns QPM if not found.
#
MIDI.ms_per_tick — Function
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.trackname — Function
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.findtextevents — Function
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.tempochanges — Function
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.readvariablelength — Function
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.