class MIDI::Track
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.
Constants
- UNNAMED
Attributes
Public Class Methods
# 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
Public Instance Methods
If ‘event` exists in @events, deletes it, updates the delta time of the event after it, and calls `recalc_times` by default.
# 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
Iterates over events, yielding each one.
# File lib/midilib/track.rb, line 63 def each(&block) # :yields: event @events.each(&block) end
Makes sure that we have one and only one end track meta event at the end of this track.
# File lib/midilib/track.rb, line 80 def ensure_track_end_meta_event track_ends = @events.select { |e| e.is_a?(MetaEvent) && e.meta_type == 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
# File lib/midilib/track.rb, line 49 def instrument MetaEvent.bytes_as_str(@instrument) end
# 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
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
.
# File lib/midilib/track.rb, line 101 def merge(event_list) @events = merge_event_lists(@events, event_list) ensure_track_end_meta_event end
Merges two event arrays together. Does not modify this track.
# 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
Return track name. If there is no name, return UNNAMED
.
# File lib/midilib/track.rb, line 33 def name event = @events.detect { |e| e.is_a?(MetaEvent) && e.meta_type == META_SEQ_NAME } event ? event.data_as_str : UNNAMED end
Set track name. Replaces or creates a name meta-event.
# File lib/midilib/track.rb, line 39 def name=(name) event = @events.detect { |e| e.is_a?(MetaEvent) && e.meta_type == META_SEQ_NAME } if event event.data = name else event = MetaEvent.new(META_SEQ_NAME, name, 0) @events[0, 0] = event end end
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.
# 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
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.
# 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
Recalculate start times for all events in list
from starting_at to end.
# 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
Sort events by their time_from_start. After sorting, recalc_delta_from_times
is called to make sure that the delta times reflect the possibly new event order.
Note: this method is redundant, since recalc_delta_from_times
sorts the events first. This method may go away in a future release, or at least be aliased to recalc_delta_from_times.