Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notes, BPM, ms_per_tick #82

Merged
merged 9 commits into from
Dec 5, 2017
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,22 @@ MIDIfile = readMIDIfile("test.mid")
writeMIDIfile("filename.mid", MIDIfile)
```

Two functions that may also be of use are: `BPM(midi)` and `ms_per_tick(midi)`.

Creating a new file with arbitrary notes
----------------------------------------

```
# Arguments are pitch (MIDI note number), duration (in ticks), position in track (in ticks), channel (0-15) and velocity (0-127)
C = MIDI.Note(60, 96, 0, 0)
E = MIDI.Note(64, 96, 48, 0)
G = MIDI.Note(67, 96, 96, 0)
using MIDI
C = Note(60, 96, 0, 0)
E = Note(64, 96, 48, 0)
G = Note(67, 96, 96, 0)

inc = 96
file = MIDI.MIDIFile()
track = MIDI.MIDITrack()
notes = MIDI.Note[]
file = MIDIFile()
track = MIDITrack()
notes = Notes()
i = 0
for v in values(MIDI.GM) # GM is a map of all the general MIDI instrument names and their codes
push!(notes, C)
Expand All @@ -79,15 +82,15 @@ for v in values(MIDI.GM) # GM is a map of all the general MIDI instrument names
C.position += inc
E.position += inc
G.position += inc
C = MIDI.Note(60, 96, C.position+inc, 0)
E = MIDI.Note(64, 96, E.position+inc, 0)
G = MIDI.Note(67, 96, G.position+inc, 0)
C = Note(60, 96, C.position+inc, 0)
E = Note(64, 96, E.position+inc, 0)
G = Note(67, 96, G.position+inc, 0)
i += 1
end

MIDI.addnotes(track, notes)
addnotes(track, notes)
push!(file.tracks, track)
MIDI.writeMIDIfile("test_out.mid", file)
writeMIDIfile("test_out.mid", file)
```

Data structures and functions you should know
Expand Down Expand Up @@ -138,6 +141,8 @@ end

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.

In addition, the alias `Notes = Vector{Note}` is also exported for ease-of-use.

```
type MIDIEvent <: TrackEvent
dT::Int
Expand Down
2 changes: 1 addition & 1 deletion src/MIDI.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ A Julia library for reading and writing MIDI files.
"""
module MIDI

include("note.jl")
include("trackevent.jl")
include("midievent.jl")
include("metaevent.jl")
include("sysexevent.jl")
include("note.jl")
include("miditrack.jl")
include("midifile.jl")
include("constants.jl")
Expand Down
39 changes: 38 additions & 1 deletion src/midifile.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export MIDIFile, readMIDIfile, writeMIDIfile
export BPM, ms_per_tick

"""
MIDIFile <: Any
Expand Down Expand Up @@ -27,7 +28,7 @@ end

"""
readMIDIfile(filename::AbstractString)
Read a file into a MIDIFile data type.
Read a file into a `MIDIFile` data type.
"""
function readMIDIfile(filename::AbstractString)
if length(filename) < 4 || filename[end-3:end] != ".mid"
Expand Down Expand Up @@ -79,3 +80,39 @@ function writeMIDIfile(filename::AbstractString, data::MIDIFile)

close(f)
end


"""
BPM(midi)
Return the BPM where the given `MIDIFile` was exported at.
"""
function BPM(t::MIDI.MIDIFile)
# META-event list:
tlist = [x for x in t.tracks[1].events]
tttttt = Vector{UInt32}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a comment explaining the reasoning behind tttttt? I see your comment below referring to it as tt-tt-tt, but I still don't quite follow what it means.

# Find the one that corresponds to Set-Time:
for i in 1:length(tlist)
if typeof(tlist[i]) == MIDI.MetaEvent
y = tlist[i]
if y.metatype == 0x51
tttttt = y.data
end
end
end
# Get the microsecond number from this tt-tt-tt
unshift!(tttttt , 0x00)
u = ntoh(reinterpret(UInt32, tttttt)[1])
μs = Int64(u)
# BPM:
BPM = round(Int, 60000000/μs)
end

"""
ms_per_tick(midi, bpm::Integer = BPM(midi)) -> ms::Float64
Given a `MIDIFile`, return how many miliseconds is one tick, based
on the `bpm`. By default the `bpm` is the BPM the midi file was exported at.
"""
function ms_per_tick(midi::MIDI.MIDIFile, bpm::Int = BPM(MIDI))
tpq = midi.timedivision
tick_ms = (1000*60)/(bpm*tpq)
end
6 changes: 3 additions & 3 deletions src/miditrack.jl
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,11 @@ function addnote(track::MIDITrack, note::Note)
end

"""
addnotes(track::MIDITrack, notes::Vector{Note})
addnotes(track::MIDITrack, notes::Notes)
Add given `notes` to given `track`, internally doing all translations from
absolute time to relative time.
"""
function addnotes(track::MIDITrack, notes::Array{Note, 1})
function addnotes(track::MIDITrack, notes::Notes)
for note in notes
addnote(track, note)
end
Expand All @@ -161,7 +161,7 @@ the `Note` datatype provided by this Package. Ordering is done based on position
There are special cases where NOTEOFF is actually encoded as NOTEON with 0 velocity.
`getnotes` takes care of this.

Returns: `Vector{Note}`.
Returns: `Notes` (which is `Vector{Note}`).
"""
function getnotes(track::MIDITrack)
notes = Note[]
Expand Down
7 changes: 5 additions & 2 deletions src/note.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
export Note
export Note, Notes

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

Notes = Vector{Note}

import Base.+, Base.-, Base.==

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