Skip to content

Commit

Permalink
Add SequencerSpecificEvent , and empty!(::MIDITrack) (#173)
Browse files Browse the repository at this point in the history
* add SequencerSpecificEvent support

* add test

* update tests

* implement `empty!(::MIDITrack)`

* add isempty for MIDITrack

* fix typo
  • Loading branch information
NeroBlackstone authored Jul 19, 2024
1 parent 3868e9e commit 17119ff
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 8 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.7.0
* New meta event type `SequencerSpecificEvent`.
* Implement `Base.empty!(::MIDITrack)` and `Base.isempty(t::MIDITrack)`.
# v2.6.0
* New functions `metric_time`.
* New functions `duration_metric_time`.
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "MIDI"
uuid = "f57c4921-e30c-5f49-b073-3f2f2ada663e"
repo = "https://github.com/JuliaMusic/MIDI.jl.git"
version = "2.6.0"
version = "2.7.0"

[deps]
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
Expand Down
31 changes: 24 additions & 7 deletions src/events.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,12 @@ const MIDI_EVENTS_DEFS = Dict(
decode = :(Int.(data)),
encode = :(UInt8.([event.semitones, event.scale]))
),

0x7f => (
type = :SequencerSpecificEvent,
fields = ["ssdata::Vector{UInt8}"],
decode = :(data),
encode = :(event.ssdata)
),
# MidiEvents
0x80 => (
type = :NoteOffEvent,
Expand Down Expand Up @@ -124,12 +129,14 @@ function define_type(type, fields, decode, encode_, supertype, typebyte)
$(fields...)
end

""" $($type)(dT::Int, typebyte::UInt8, data::Vector{UInt8})
Returns a `$($type)` event from it's byte representation.
The parameter `typebyte::UInt8` is its's $(($type <: MetaEvent) ? "metatype" : "status") byte.
"""
function $type(dT::Int, typebyte::UInt8, data::Vector{UInt8})
$type(dT, typebyte, $decode...)
if !($type.types[end] <: AbstractArray)
""" $($type)(dT::Int, typebyte::UInt8, data::Vector{UInt8})
Returns a `$($type)` event from it's byte representation.
The parameter `typebyte::UInt8` is its's $(($type <: MetaEvent) ? "metatype" : "status") byte.
"""
function $type(dT::Int, typebyte::UInt8, data::Vector{UInt8})
$type(dT, typebyte, $decode...)
end
end

if $type <: MetaEvent
Expand Down Expand Up @@ -407,3 +414,13 @@ The `PitchBendEvent` informs a MIDI device to modify the pitch in a specific cha
* `pitch::Int` : Value of the pitch bend.
"""
PitchBendEvent

""" SequencerSpecificEvent <: MetaEvent
The `SequencerSpecificEvent` is used to store vendor-proprietary data in a MIDI file.
## Fields:
* `dT::Int` : Delta time in ticks.
* `metatype::UInt8` : Meta type byte of the event.
* `ssdata::Vector{UInt8}` : Vendor-proprietary data.
"""
SequencerSpecificEvent
4 changes: 4 additions & 0 deletions src/miditrack.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
Track chunks begin with four bytes spelling out "MTrk", followed by the length
(in bytes) of the track (see [`readvariablelength`](@ref)), followed by a sequence
of events.
`MIDITrack` implements the `isempty` and `empty!` functions.
"""
mutable struct MIDITrack
events::Vector{TrackEvent}
Expand All @@ -21,6 +23,8 @@ function Base.show(io::IO, t::MIDITrack)
print(io, "$(L)-event MIDITrack: $M MIDI, $T Meta, $X Sysex")
end

Base.empty!(t::MIDITrack) = empty!(t.events)
Base.isempty(t::MIDITrack)::Bool = isempty(t.events)

function readtrack(f::IO)

Expand Down
Binary file added test/SequencerSpecific.mid
Binary file not shown.
18 changes: 18 additions & 0 deletions test/metaevent.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,22 @@
@test_throws errtype MIDI.readsysexevent(Int(input[1]), IOBuffer(input[2:length(input)]))
end
end

@testset "SequencerSpecificEvent" begin
midi = load("SequencerSpecific.mid")
sse = midi.tracks[1].events[1]
@test sse isa MIDI.SequencerSpecificEvent
sse.ssdata == [0x11, 0x21, 0x53, 0x1F]

sse = MIDI.SequencerSpecificEvent(0, 0x7f, [0x11, 0x21, 0x53, 0x1F])
@test sse.ssdata == [0x11, 0x21, 0x53, 0x1F]
end

@testset "check empty" begin
midi = load(testmidi())
track = midi.tracks[1]
@test !isempty(track)
empty!(track)
@test isempty(track)
end
end

0 comments on commit 17119ff

Please sign in to comment.