Class: MIDI::Track
Overview
A Track is a list of events.
When you modify the events
array, make sure to call recalc_times so each Event gets its time_from_start
recalculated.
A Track also holds a bitmask that specifies the channels used by the track. This bitmask is set when the track is read from the MIDI file by an IO::SeqReader but is not kept up to date by any other methods.
Constant Summary collapse
- UNNAMED =
'Unnamed'
Instance Attribute Summary collapse
-
#channels_used ⇒ Object
Returns the value of attribute channels_used.
-
#events ⇒ Object
Returns the value of attribute events.
-
#sequence ⇒ Object
readonly
Returns the value of attribute sequence.
Instance Method Summary collapse
-
#delete_event(event, call_recalc_times = true) ⇒ Object
If ‘event` exists in @events, deletes it, updates the delta time of the event after it, and calls `recalc_times` by default.
-
#each(&block) ⇒ Object
Iterates over events, yielding each one.
-
#ensure_track_end_meta_event ⇒ Object
Makes sure that we have one and only one end track meta event at the end of this track.
-
#initialize(sequence) ⇒ Track
constructor
A new instance of Track.
- #instrument ⇒ Object
- #instrument=(str_or_bytes) ⇒ Object
-
#merge(event_list) ⇒ Object
Merges an array of events into our event list.
-
#merge_event_lists(list1, list2) ⇒ Object
Merges two event arrays together.
-
#name ⇒ Object
Return track name.
-
#name=(name) ⇒ Object
Set track name.
-
#quantize(length_or_note) ⇒ Object
Quantize every event.
-
#recalc_delta_from_times(starting_at = 0, list = @events) ⇒ Object
(also: #sort)
The opposite of recalc_times: recalculates delta_time for each event from each event’s time_from_start.
-
#recalc_times(starting_at = 0, list = @events) ⇒ Object
Recalculate start times for all events in
list
from starting_at to end.
Constructor Details
#initialize(sequence) ⇒ Track
Returns a new instance of Track.
22 23 24 25 26 27 28 29 30 |
# File 'lib/midilib/track.rb', line 22 def initialize(sequence) @sequence = sequence @events = [] # Bitmask of all channels used. Set when track is read in from # a MIDI file. @channels_used = 0 @instrument = nil end |
Instance Attribute Details
#channels_used ⇒ Object
Returns the value of attribute channels_used.
19 20 21 |
# File 'lib/midilib/track.rb', line 19 def channels_used @channels_used end |
#events ⇒ Object
Returns the value of attribute events.
19 20 21 |
# File 'lib/midilib/track.rb', line 19 def events @events end |
#sequence ⇒ Object (readonly)
Returns the value of attribute sequence.
20 21 22 |
# File 'lib/midilib/track.rb', line 20 def sequence @sequence end |
Instance Method Details
#delete_event(event, call_recalc_times = true) ⇒ Object
If ‘event` exists in @events, deletes it, updates the delta time of the event after it, and calls `recalc_times` by default.
69 70 71 72 73 74 75 76 |
# File 'lib/midilib/track.rb', line 69 def delete_event(event, call_recalc_times = true) i = @events.index(event) return unless i @events[i + 1].delta_time += @events[i].delta_time if i != (@events.length - 1) @events.delete_at(i) recalc_times if call_recalc_times end |
#each(&block) ⇒ Object
Iterates over events, yielding each one.
63 64 65 |
# File 'lib/midilib/track.rb', line 63 def each(&block) # :yields: event @events.each(&block) end |
#ensure_track_end_meta_event ⇒ Object
Makes sure that we have one and only one end track meta event at the end of this track.
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/midilib/track.rb', line 80 def track_ends = @events.select { |e| e.is_a?(MetaEvent) && e. == META_TRACK_END } has_end = !@events.empty? && track_ends[-1] == @events.last # If we only have one end event and it's the last one, there's nothing # to do. return if track_ends.length == 1 && has_end # If we have an end of track event already, leave it alone. track_ends.pop if has_end track_ends.each { |track_end| delete_event(track_end, false) } return if has_end mte = MetaEvent.new(META_TRACK_END, nil, 0) mte.time_from_start = @events.last.time_from_start + mte.delta_time if @events.last @events << mte end |
#instrument ⇒ Object
49 50 51 |
# File 'lib/midilib/track.rb', line 49 def instrument MetaEvent.bytes_as_str(@instrument) end |
#instrument=(str_or_bytes) ⇒ Object
53 54 55 56 57 58 59 60 |
# File 'lib/midilib/track.rb', line 53 def instrument=(str_or_bytes) @instrument = case str_or_bytes when String MetaEvent.str_as_bytes(str_or_bytes) else str_or_bytes end end |
#merge(event_list) ⇒ Object
Merges an array of events into our event list. After merging, the events’ time_from_start values are correct so you don’t need to worry about calling #recalc_times.
101 102 103 104 |
# File 'lib/midilib/track.rb', line 101 def merge(event_list) @events = merge_event_lists(@events, event_list) end |
#merge_event_lists(list1, list2) ⇒ Object
Merges two event arrays together. Does not modify this track.
107 108 109 110 111 112 113 |
# File 'lib/midilib/track.rb', line 107 def merge_event_lists(list1, list2) recalc_times(0, list1) recalc_times(0, list2) list = list1 + list2 recalc_delta_from_times(0, list) list end |
#name ⇒ Object
Return track name. If there is no name, return UNNAMED.
33 34 35 36 |
# File 'lib/midilib/track.rb', line 33 def name event = @events.detect { |e| e.is_a?(MetaEvent) && e. == META_SEQ_NAME } event ? event.data_as_str : UNNAMED end |
#name=(name) ⇒ Object
Set track name. Replaces or creates a name meta-event.
39 40 41 42 43 44 45 46 47 |
# File 'lib/midilib/track.rb', line 39 def name=(name) event = @events.detect { |e| e.is_a?(MetaEvent) && e. == META_SEQ_NAME } if event event.data = name else event = MetaEvent.new(META_SEQ_NAME, name, 0) @events[0, 0] = event end end |
#quantize(length_or_note) ⇒ Object
Quantize every event. length_or_note is either a length (1 = quarter, 0.25 = sixteenth, 4 = whole note) or a note name (“sixteenth”, “32nd”, “8th triplet”, “dotted quarter”).
Since each event’s time_from_start is modified, we call recalc_delta_from_times after each event quantizes itself.
121 122 123 124 125 126 127 128 129 130 |
# File 'lib/midilib/track.rb', line 121 def quantize(length_or_note) delta = case length_or_note when String @sequence.note_to_delta(length_or_note) else @sequence.length_to_delta(length_or_note.to_i) end @events.each { |event| event.quantize_to(delta) } recalc_delta_from_times end |
#recalc_delta_from_times(starting_at = 0, list = @events) ⇒ Object Also known as: sort
The opposite of recalc_times: recalculates delta_time for each event from each event’s time_from_start. This is useful, for example, when merging two event lists. As a side-effect, elements from starting_at are sorted by time_from_start.
146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/midilib/track.rb', line 146 def recalc_delta_from_times(starting_at = 0, list = @events) prev_time_from_start = 0 # We need to sort the sublist. sublist.sort! does not do what we want. # We call mergesort instead of Array.sort because sort is not stable # (it can mix up the order of events that have the same start time). # See http://wiki.github.com/adamjmurray/cosy/midilib-notes for details. list[starting_at..-1] = mergesort(list[starting_at..-1]) do |e1, e2| e1.time_from_start <=> e2.time_from_start end list[starting_at..-1].each do |e| e.delta_time = e.time_from_start - prev_time_from_start prev_time_from_start = e.time_from_start end end |
#recalc_times(starting_at = 0, list = @events) ⇒ Object
Recalculate start times for all events in list
from starting_at to end.
134 135 136 137 138 139 140 |
# File 'lib/midilib/track.rb', line 134 def recalc_times(starting_at = 0, list = @events) t = starting_at == 0 ? 0 : list[starting_at - 1].time_from_start list[starting_at..-1].each do |e| t += e.delta_time e.time_from_start = t end end |