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

channels_used[RW]
events[RW]
sequence[R]

Public Class Methods

new(sequence) click to toggle source
# 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

delete_event(event, call_recalc_times = true) click to toggle source

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
each() { |event| ... } click to toggle source

Iterates over events, yielding each one.

# File lib/midilib/track.rb, line 63
def each(&block) # :yields: event
  @events.each(&block)
end
ensure_track_end_meta_event() click to toggle source

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
instrument() click to toggle source
# File lib/midilib/track.rb, line 49
def instrument
  MetaEvent.bytes_as_str(@instrument)
end
instrument=(str_or_bytes) click to toggle source
# 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) click to toggle source

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
merge_event_lists(list1, list2) click to toggle source

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
name() click to toggle source

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
name=(name) click to toggle source

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(length_or_note) click to toggle source

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
recalc_delta_from_times(starting_at = 0, list = @events) click to toggle source

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
Also aliased as: sort
recalc_times(starting_at = 0, list = @events) click to toggle source

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(starting_at = 0, list = @events)

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.