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
— TypeNote(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_pitch
andpitch_to_name
for 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
— TypeNotes(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
— Functiongetnotes(midi::MIDIFile [, trackno])
Find all NoteOnEvent
s and NoteOffEvent
s 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!
— Functionaddnotes!(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
— Functionname_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
).
See http://newt.phys.unsw.edu.au/jw/notes.html and https://en.wikipedia.org/wiki/C(musicalnote) .
MIDI.pitch_to_name
— Functionpitch_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
— Functionnote_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
— Functionhz_to_pitch(hz::Real, A4::Real = 440) -> pitch::Int
Inverse of pitch_to_hz
.
MIDI.pitch_to_hz
— Functionpitch_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
— TypeNamedNote(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)
Warning: 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).
Missing docstring for NamedNotes
. Check Documenter's build log for details.