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.
MIDIEvent
s 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.
MetaEvent
s and SysexEvent
s 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.