Notes
Note information in MIDI files is typically encoded using NOTEON and NOTEOFF events. A music note however contains more information besides the start and end; we bundle this information into the types Note and Notes. These two structures are central to the way JuliaMusic operates. In addition, all note events of a MIDITrack can be obtained with the function getnotes.
#
MIDI.Note — Type
Note(pitch, velocity, position, duration, channel = 0) <: AbstractNote
Mutable data structure describing a music note. A bundle of many notes results in the Notes struct, which is the output of the getnotes function.
If the channel of the note is 0 (default), it is not shown.
You can also create a Note with the following keyword constructor:
Note(pitch, position; velocity = 100, duration = 960, channel = 0)
Note(pitch_name::String; position = 0, velocity = 100, duration = 960, channel = 0)
Fields:
-
pitch::UInt8: Pitch, starting from C-1 = 0, adding one per semitone. Use the functionsname_to_pitchandpitch_to_namefor integer and string representations. -
velocity::UInt8: Dynamic intensity. Cannot be higher than 127 (0x7F). -
position::UInt: Position in absolute time (since beginning of track), in ticks. -
duration::UInt: Duration in ticks. -
channel::UInt8 = 0: Channel of the track that the note is played on. Cannot be higher than 127 (0x7F).
#
MIDI.Notes — Type
Notes(note_vector, tpq = 960) -> Notes
Notes(notes_string::String, tpq::Int = 960) -> Notes
A data structure describing a collection of music notes, bundled with the ticks per quarter note (so that the notes can be attributed rhythmic value).
Notes can be initialized by string, the name of notes are separated by spaces.
Notes("C2 F3 D#6")
Notes can be iterated and accessed as the given note_vector. This eliminates the need for custom iteration or search functions. For example, to get the note of maximum pitch you can do:
max_pitch, index_max = findmax(n -> n.pitch, notes)
max_pitch_note = notes[index_max]
#
MIDI.getnotes — Function
getnotes(midi::MIDIFile [, trackno])
Find all NoteOnEvents and NoteOffEvents in the trackno track of a midi (default 1 or 2), that correspond to the same note value (pitch) and convert them into the Note datatype. There are special cases where NoteOffEvent is actually encoded as NoteOnEvent with 0 velocity, but getnotes takes care of this.
Notice that the first track of a midi typically doesn’t have any notes, which is why the function defaults to track 2.
getnotes(track::MIDITrack, tpq = 960)
Find the notes from track directly, passing also the ticks per quarter note.
Returns: Notes{Note}, setting the ticks per quarter note as tpq. You can find the originally exported ticks per quarter note from the original MIDIFile through midi.tpq.
If you have some notes and you want to add them to a track, use
#
MIDI.addnotes! — Function
addnotes!(track::MIDITrack, notes)
Add given notes to given track, internally doing all translations from absolute time to relative time.
Finally, you can use the function getnotnotes(track) to get all TrackEvents that are not NOTEON or NOTEOFF.
Write Example
using MIDI
C = Note(60, 96, 0, 192)
E = Note(64, 96, 48, 144)
G = Note(67, 96, 96, 96)
inc = 192
file = MIDIFile()
track = MIDITrack()
notes = Notes() # tpq automatically = 960
push!(notes, C)
push!(notes, E)
push!(notes, G)
# Notes one octave higher
C = Note(60 + 12, 96, C.position+inc, 192)
E = Note(64 + 12, 96, E.position+inc, 144)
G = Note(67 + 12, 96, G.position+inc, 96)
addnotes!(track, notes)
addtrackname!(track, "simple track")
push!(file.tracks, track)
save("test.mid", file)
Read Example
using MIDI
midi = load(testmidi())
MIDIFile (format=1, tpq=960) with tracks:
American Rock Beat 06 Tom
Drums
Bass
ORIGINAL
# Track number 3 is a quantized bass MIDI track
bass = midi.tracks[3]
notes = getnotes(bass, midi.tpq)
println("Notes of track $(trackname(bass)):")
notes
177 Notes with tpq=960
Note A♯2 | vel = 95 | pos = 7680, dur = 690
Note A♯2 | vel = 71 | pos = 9280, dur = 308
Note G♯2 | vel = 52 | pos = 9600, dur = 668
Note G♯2 | vel = 58 | pos = 11200, dur = 338
Note G2 | vel = 71 | pos = 11520, dur = 701
Note G♯2 | vel = 83 | pos = 13120, dur = 281
Note G2 | vel = 73 | pos = 13440, dur = 855
⋮
Note F♯1 | vel = 73 | pos = 185280, dur = 878
Note F1 | vel = 85 | pos = 186240, dur = 964
Note A1 | vel = 88 | pos = 187200, dur = 904
Note A♯1 | vel = 81 | pos = 188160, dur = 900
Note B1 | vel = 77 | pos = 189120, dur = 945
Note C2 | vel = 83 | pos = 190080, dur = 847
Note F2 | vel = 90 | pos = 191040, dur = 713
Conversions
#
MIDI.name_to_pitch — Function
name_to_pitch(name::String) -> Int
Return the pitch value of the given note name, which can be of the form capital_letter*sharp*octave where:
-
capital_letter: from"A"to"G". -
sharp: one of"#""♯""b""♭"or"". -
octave: any integer (as a string), the octave number (an octave is 12 pitches). If not given it is assumed"5".
We define E.g. name_to_pitch("C4") === 60 (i.e. string "C4", representing the middle-C, corresponds to pitch 60).
#
MIDI.pitch_to_name — Function
pitch_to_name(pitch; flat=false) -> String
Return the name of the pitch, e.g. F5, A♯3 etc. in modern notation given the pitch value in integer. When flat=true, accidentals are printed as flats, e.g. A♯3 is printed as B♭3.
Reminder: middle C has pitch 60 and is displayed as C4.
#
MusicManipulations.note_to_fundamental — Function
note_to_fundamental(note(s))
Return a String or Vector{String} with the fundamental pitch of the notes (i.e. without the octave information).
#
MIDI.hz_to_pitch — Function
hz_to_pitch(hz::Real, A4::Real = 440) -> pitch::Int
Inverse of pitch_to_hz.
#
MIDI.pitch_to_hz — Function
pitch_to_hz(pitch::Integer, A4::Real = 440) -> hz::Real
Return the frequency value of the given midi note, optionally given the reference for middle A. See https://en.wikipedia.org/wiki/Pianokeyfrequencies and https://librosa.org/doc/main/modules/librosa/core/convert.html#midito_hz.
NamedNote
Note with specific note name. This type need MusicManipulations.jl.
#
MusicManipulations.NamedNote — Type
NamedNote(name, velocity, position, duration, channel = 0) <: AbstractNote
Mutable data structure describing a music note with certain pitch name. A bundle of many notes results in the Notes struct, which is the output of the getnotes function.
If the channel of the note is 0 (default), it is not shown.
You can also create a NamedNote with the following keyword constructor:
NamedNote(pitch_name::String; position = 0, velocity = 100, duration = 960, channel = 0)
NamedNote(n::Note; pitch_name::String = "")
Initialize Notes{NamedNote} from a pitch name string, use spaces to separate pitch name.
NamedNotes(notes_string::String; tpq::Int = 960)
# NamedNotes("C#6 Db5 E4")
Converte NamedNote to Note:
Note(n::NamedNote)
We use attribute name_to_pitch(name),duration,position,channel,velocity to compare NamedNote. So:
|
NamedNote("Db5") == NamedNote("C#5")
# true
Fields:
-
name::String: Pitch name, Uppercase. -
velocity::UInt8: Dynamic intensity. Cannot be higher than 127 (0x7F). -
position::UInt: Position in absolute time (since beginning of track), in ticks. -
duration::UInt: Duration in ticks. -
channel::UInt8 = 0: Channel of the track that the note is played on. Cannot be higher than 127 (0x7F).