Skip to content

Commit e5a7850

Browse files
authored
Merge pull request #82 from JoelHobson/Notes
Notes, BPM, ms_per_tick
2 parents e84a6d9 + aa20407 commit e5a7850

File tree

5 files changed

+67
-18
lines changed

5 files changed

+67
-18
lines changed

README.md

+16-11
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,22 @@ MIDIfile = readMIDIfile("test.mid")
5656
writeMIDIfile("filename.mid", MIDIfile)
5757
```
5858

59+
Two functions that may also be of use are: `BPM(midi)` and `ms_per_tick(midi)`.
60+
5961
Creating a new file with arbitrary notes
6062
----------------------------------------
6163

6264
```
6365
# Arguments are pitch (MIDI note number), duration (in ticks), position in track (in ticks), channel (0-15) and velocity (0-127)
64-
C = MIDI.Note(60, 96, 0, 0)
65-
E = MIDI.Note(64, 96, 48, 0)
66-
G = MIDI.Note(67, 96, 96, 0)
66+
using MIDI
67+
C = Note(60, 96, 0, 0)
68+
E = Note(64, 96, 48, 0)
69+
G = Note(67, 96, 96, 0)
6770
6871
inc = 96
69-
file = MIDI.MIDIFile()
70-
track = MIDI.MIDITrack()
71-
notes = MIDI.Note[]
72+
file = MIDIFile()
73+
track = MIDITrack()
74+
notes = Notes()
7275
i = 0
7376
for v in values(MIDI.GM) # GM is a map of all the general MIDI instrument names and their codes
7477
push!(notes, C)
@@ -79,15 +82,15 @@ for v in values(MIDI.GM) # GM is a map of all the general MIDI instrument names
7982
C.position += inc
8083
E.position += inc
8184
G.position += inc
82-
C = MIDI.Note(60, 96, C.position+inc, 0)
83-
E = MIDI.Note(64, 96, E.position+inc, 0)
84-
G = MIDI.Note(67, 96, G.position+inc, 0)
85+
C = Note(60, 96, C.position+inc, 0)
86+
E = Note(64, 96, E.position+inc, 0)
87+
G = Note(67, 96, G.position+inc, 0)
8588
i += 1
8689
end
8790
88-
MIDI.addnotes(track, notes)
91+
addnotes(track, notes)
8992
push!(file.tracks, track)
90-
MIDI.writeMIDIfile("test_out.mid", file)
93+
writeMIDIfile("test_out.mid", file)
9194
```
9295

9396
Data structures and functions you should know
@@ -138,6 +141,8 @@ end
138141

139142
Value is a number indicating pitch class & octave (middle-C is 60). Position is an absolute time (in ticks) within the track. Please note that velocity cannot be higher than 127 (0x7F). Integers can be added to, or subtracted from notes to change the pitch, and notes can be directly compared with ==. Constants exist for the different pitch values at octave 0. MIDI.C, MIDI.Cs, MIDI.Db, etc. Enharmonic note constants exist as well (MIDI.Fb). Just add 12*n to the note to transpose to octave n.
140143

144+
In addition, the alias `Notes = Vector{Note}` is also exported for ease-of-use.
145+
141146
```
142147
type MIDIEvent <: TrackEvent
143148
dT::Int

src/MIDI.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ A Julia library for reading and writing MIDI files.
33
"""
44
module MIDI
55

6+
include("note.jl")
67
include("trackevent.jl")
78
include("midievent.jl")
89
include("metaevent.jl")
910
include("sysexevent.jl")
10-
include("note.jl")
1111
include("miditrack.jl")
1212
include("midifile.jl")
1313
include("constants.jl")

src/midifile.jl

+42-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export MIDIFile, readMIDIfile, writeMIDIfile
2+
export BPM, ms_per_tick
23

34
"""
45
MIDIFile <: Any
@@ -27,7 +28,7 @@ end
2728

2829
"""
2930
readMIDIfile(filename::AbstractString)
30-
Read a file into a MIDIFile data type.
31+
Read a file into a `MIDIFile` data type.
3132
"""
3233
function readMIDIfile(filename::AbstractString)
3334
if length(filename) < 4 || filename[end-3:end] != ".mid"
@@ -79,3 +80,43 @@ function writeMIDIfile(filename::AbstractString, data::MIDIFile)
7980

8081
close(f)
8182
end
83+
84+
85+
"""
86+
BPM(midi)
87+
Return the BPM where the given `MIDIFile` was exported at.
88+
"""
89+
function BPM(t::MIDI.MIDIFile)
90+
# META-event list:
91+
tlist = [x for x in t.tracks[1].events]
92+
tttttt = Vector{UInt32}
93+
# Find the one that corresponds to Set-Time:
94+
# The event tttttt corresponds to the command
95+
# FF 51 03 tttttt Set Tempo (in microseconds per MIDI quarter-note)
96+
# See here (page 8):
97+
# http://www.cs.cmu.edu/~music/cmsip/readings/Standard-MIDI-file-format-updated.pdf
98+
for i in 1:length(tlist)
99+
if typeof(tlist[i]) == MIDI.MetaEvent
100+
y = tlist[i]
101+
if y.metatype == 0x51
102+
tttttt = y.data
103+
end
104+
end
105+
end
106+
# Get the microsecond number from tttttt
107+
unshift!(tttttt , 0x00)
108+
u = ntoh(reinterpret(UInt32, tttttt)[1])
109+
μs = Int64(u)
110+
# BPM:
111+
BPM = round(Int, 60000000/μs)
112+
end
113+
114+
"""
115+
ms_per_tick(midi, bpm::Integer = BPM(midi)) -> ms::Float64
116+
Given a `MIDIFile`, return how many miliseconds is one tick, based
117+
on the `bpm`. By default the `bpm` is the BPM the midi file was exported at.
118+
"""
119+
function ms_per_tick(midi::MIDI.MIDIFile, bpm::Int = BPM(MIDI))
120+
tpq = midi.timedivision
121+
tick_ms = (1000*60)/(bpm*tpq)
122+
end

src/miditrack.jl

+3-3
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,11 @@ function addnote(track::MIDITrack, note::Note)
142142
end
143143

144144
"""
145-
addnotes(track::MIDITrack, notes::Vector{Note})
145+
addnotes(track::MIDITrack, notes::Notes)
146146
Add given `notes` to given `track`, internally doing all translations from
147147
absolute time to relative time.
148148
"""
149-
function addnotes(track::MIDITrack, notes::Array{Note, 1})
149+
function addnotes(track::MIDITrack, notes::Notes)
150150
for note in notes
151151
addnote(track, note)
152152
end
@@ -161,7 +161,7 @@ the `Note` datatype provided by this Package. Ordering is done based on position
161161
There are special cases where NOTEOFF is actually encoded as NOTEON with 0 velocity.
162162
`getnotes` takes care of this.
163163
164-
Returns: `Vector{Note}`.
164+
Returns: `Notes` (which is `Vector{Note}`).
165165
"""
166166
function getnotes(track::MIDITrack)
167167
notes = Note[]

src/note.jl

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
export Note
1+
export Note, Notes
22

33
"""
44
Note <: Any
5-
Data structure describing a "music note".
5+
Data structure describing a "music note". The alias `Notes = Vector{Note}` is also
6+
provided.
67
## Fields:
78
* `value::UInt8` : Pitch, starting from C0 = 0, adding one per semitone (middle-C is 60).
89
* `duration::UInt` : Duration in ticks.
@@ -27,6 +28,8 @@ type Note
2728
end
2829
end
2930

31+
Notes = Vector{Note}
32+
3033
import Base.+, Base.-, Base.==
3134

3235
+(n::Note, i::Integer) = Note(n.value + i, n.duration, n.position, n.channel, n.velocity)

0 commit comments

Comments
 (0)