Basic MIDI structures
MIDI files are loaded to/from disc and transformed into a Julia structure MIDIFile
, which contains MIDITrack
s.
MIDIFile
To read and write a MIDI file, use
MIDI.readMIDIFile
— FunctionreadMIDIFile(filename::AbstractString)
Read a file into a MIDIFile
data type.
MIDI.writeMIDIFile
— FunctionwriteMIDIFile(filename::AbstractString, data::MIDIFile)
Write a MIDIFile
as a ".mid" file to the given filename.
writeMIDIFile(filename::AbstractString, notes::Notes)
Create a MIDIFile
directly from notes
, using format 1.
The above functions return / use the MIDIFile
type:
MIDI.MIDIFile
— TypeMIDIFile <: 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
— TypeMIDITrack <: 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.TrackEvent
— TypeTrackEvent <: 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 one of three types:
struct MIDIEvent <: TrackEvent
dT::Int
status::UInt8
data::Array{UInt8,1}
end
struct MetaEvent <: TrackEvent
dT::Int
metatype::UInt8
data::Array{UInt8,1}
end
struct SysexEvent <: TrackEvent
dT::Int
data::Array{UInt8,1}
end
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.
Utility functions
MIDI.BPM
— FunctionBPM(midi)
Return the BPM where the given MIDIFile
was exported at.
MIDI.ms_per_tick
— Functionms_per_tick(tpq, bpm)
ms_per_tick(midi::MIDIFile)
Return how many miliseconds is one tick, based on the beats per minute bpm
and ticks per quarter note tpq
.
MIDI.addevent!
— Functionaddevent!(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!
— Functionaddevents!(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.trackname
— Functiontrackname(track::MIDI.MIDITrack)
Return the name of the given track
as a string, by finding the "track name" MetaEvent
.
If no such event exists, "No track name found"
is returned.
MIDI.addtrackname!
— Functionaddtrackname!(track::MIDI.MIDITrack, name::String)
Add a name to the given track
by attaching the "track name" MetaEvent
to the start of the track
.
MIDI.textevent
— Functiontextevent(eventtype, text)
Create an event using the string text
. The eventtype
can be :text, :lyric, :marker
, which will create the appropriate type of MetaEvent
.
The returned event can be added to a MIDITrack
via either addevent!
or addevents!
for multiple events.
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.findtextevents
— Functionfindtextevents(eventtype, track)
Find all "text" events specifield by eventtype
in the track
. The eventtype
can be :text, :lyric, :marker
, which will find the appropriate MetaEvent
s.
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.
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
— Functionreadvariablelength(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.