Skip to content

Commit

Permalink
Add matric_time and duration_metric_time (#166)
Browse files Browse the repository at this point in the history
* add matric_time and duration_metric_time

* Update src/midifile.jl

Co-authored-by: George Datseris <[email protected]>

* add documentation

---------

Co-authored-by: George Datseris <[email protected]>
  • Loading branch information
NeroBlackstone and Datseris authored Jun 9, 2023
1 parent 3a4ee47 commit 67091e7
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 3 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
# v2.6.0
* New functions `metric_time`.
* New functions `duration_metric_time`.
# v2.5.0
* Implement `Note(pitch_name::String; position = 0, velocity = 100, duration = 960, channel = 0)`
* Implement `Notes(notes_string::String, tpq::Int = 960)`
Expand Down
4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
name = "MIDI"
uuid = "f57c4921-e30c-5f49-b073-3f2f2ada663e"
repo = "https://github.com/JuliaMusic/MIDI.jl.git"
version = "2.5.0"
version = "2.6.0"

[deps]
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e"

[compat]
FileIO = "1"
IterTools = "1.8.0"
julia = "1"

[extras]
Expand Down
2 changes: 2 additions & 0 deletions src/MIDI.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ A Julia library for reading and writing MIDI files.
module MIDI

using Base.Unicode
using IterTools

include("constants.jl")
include("note.jl")
Expand Down Expand Up @@ -34,6 +35,7 @@ export pitch_to_name, name_to_pitch, is_octave
export pitch_to_hz, hz_to_pitch
export TrackEvent, MetaEvent, MIDIEvent, SysexEvent
export readvariablelength, writevariablelength
export metric_time,duration_metric_time

"""
testmidi()
Expand Down
31 changes: 30 additions & 1 deletion src/midifile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,40 @@ end
"""
ms_per_tick(tpq, qpm)
ms_per_tick(midi::MIDIFile)
Return how many miliseconds is one tick, based
Return how many milliseconds is one tick, based
on the quarter notes per minute `qpm` and ticks per quarter note `tpq`.
"""
ms_per_tick(midi::MIDI.MIDIFile, qpm = qpm(midi)) = ms_per_tick(midi.tpq, qpm)
ms_per_tick(tpq, qpm) = (1000*60)/(qpm*tpq)

getnotes(midi::MIDIFile, trackno = midi.format == 0 ? 1 : 2) =
getnotes(midi.tracks[trackno], midi.tpq)

"""
metric_time(midi::MIDIFile,note::AbstractNote)::Float64
Return how many milliseconds elapsed at `note` position.
Matric time calculations need `tpq` field of `MIDIFile`.
Apparently it only make sense if the `note` coming from `MIDIFile`, otherwise you can't get the correct result.
"""
function metric_time(midi::MIDIFile,note::AbstractNote)::Float64
# get all tempo change event before note
tc_tuples = filter(x->x[1]<=note.position,tempochanges(midi))
# how many ticks between two tempo changes event
tempo_ticks = map(x->x[2][1]-x[1][1],partition(tc_tuples,2,1))
push!(tempo_ticks,note.position-last(tc_tuples)[1])
return mapreduce(x -> ms_per_tick(midi.tpq, x[1][2]) * x[2], +, zip(tc_tuples,tempo_ticks))
end

"""
duration_metric_time(midi::MIDIFile,note::AbstractNote)::Float64
Return `note` duration time in milliseconds.
Matric time calculations need `tpq` field of `MIDIFile`.
Apparently it only make sense if the `note` coming from `MIDIFile`, otherwise you can't get the correct result.
"""
function duration_metric_time(midi::MIDIFile,note::AbstractNote)::Float64
tc_tuple = (0,0.0)
for tc in tempochanges(midi)
tc[1] <= note.position ? tc_tuple = tc : break
end
return ms_per_tick(midi.tpq,tc_tuple[2])*note.duration
end
15 changes: 14 additions & 1 deletion test/miditrack.jl
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ invalidtestvalues = [
onotes = getnotes(original_track)
tnotes = getnotes(test_track)
identical = true
for i = 1:length(onotes)
for i = eachindex(onotes)
if onotes[i] != tnotes[i]
identical = false
end
Expand All @@ -209,4 +209,17 @@ invalidtestvalues = [
@test identical

end

@testset "metric_time" begin
midifile = load("./tempo_change.mid")
notes = getnotes(midifile.tracks[1])

@test metric_time(midifile,last(notes)) == 8500.0
@test metric_time(midifile,first(notes)) == 0.0
@test metric_time(midifile,notes[5]) == 2500.0

@test round(duration_metric_time(midifile,notes[2])) == 474
@test round(duration_metric_time(midifile,last(notes))) == 948
@test round(duration_metric_time(midifile,first(notes))) == 474
end
end
Binary file added test/tempo_change.mid
Binary file not shown.

0 comments on commit 67091e7

Please sign in to comment.