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.NoteType
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 functions name_to_pitch and pitch_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.NotesType
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.getnotesFunction
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)
writeMIDIFile("test.mid", file)

Read Example

using MIDI
midi = readMIDIFile(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_pitchFunction
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).

See http://newt.phys.unsw.edu.au/jw/notes.html and https://en.wikipedia.org/wiki/C(musicalnote) .

MIDI.pitch_to_nameFunction
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_fundamentalFunction
note_to_fundamental(note(s))

Return a String or Vector{String} with the fundamental pitch of the notes (i.e. without the octave information).