Class: MIDI::Sequence

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/midilib/sequence.rb

Overview

A MIDI::Sequence contains MIDI::Track objects.

Constant Summary collapse

UNNAMED =
'Unnamed Sequence'
DEFAULT_TEMPO =
120
NOTE_TO_LENGTH =
{
  'whole' => 4.0,
  'half' => 2.0,
  'quarter' => 1.0,
  'eighth' => 0.5,
  '8th' => 0.5,
  'sixteenth' => 0.25,
  '16th' => 0.25,
  'thirty second' => 0.125,
  'thirtysecond' => 0.125,
  '32nd' => 0.125,
  'sixty fourth' => 0.0625,
  'sixtyfourth' => 0.0625,
  '64th' => 0.0625
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeSequence

Returns a new instance of Sequence.


45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/midilib/sequence.rb', line 45

def initialize
  @tracks = Array.new()
  @ppqn = 480

  # Time signature
  @numer = 4		# Numer + denom = 4/4 time default
  @denom = 2
  @clocks = 24    # Bug fix  Nov 11, 2007 - this is not the same as ppqn!
  @qnotes = 8

  @reader_class = IO::SeqReader
  @writer_class = IO::SeqWriter
end

Instance Attribute Details

#clocksObject

Returns the value of attribute clocks


37
38
39
# File 'lib/midilib/sequence.rb', line 37

def clocks
  @clocks
end

#denomObject

Returns the value of attribute denom


37
38
39
# File 'lib/midilib/sequence.rb', line 37

def denom
  @denom
end

#formatObject

The MIDI file format (0, 1, or 2)


36
37
38
# File 'lib/midilib/sequence.rb', line 36

def format
  @format
end

#numerObject

Returns the value of attribute numer


37
38
39
# File 'lib/midilib/sequence.rb', line 37

def numer
  @numer
end

#ppqnObject

Pulses (i.e. clocks) Per Quarter Note resolution for the sequence


34
35
36
# File 'lib/midilib/sequence.rb', line 34

def ppqn
  @ppqn
end

#qnotesObject

Returns the value of attribute qnotes


37
38
39
# File 'lib/midilib/sequence.rb', line 37

def qnotes
  @qnotes
end

#reader_classObject

The class to use for reading MIDI from a stream. The default is MIDI::IO::SeqReader. You can change this at any time.


40
41
42
# File 'lib/midilib/sequence.rb', line 40

def reader_class
  @reader_class
end

#tracksObject

Array with all tracks for the sequence


32
33
34
# File 'lib/midilib/sequence.rb', line 32

def tracks
  @tracks
end

#writer_classObject

The class to use for writeing MIDI from a stream. The default is MIDI::IO::SeqWriter. You can change this at any time.


43
44
45
# File 'lib/midilib/sequence.rb', line 43

def writer_class
  @writer_class
end

Instance Method Details

#beats_per_minuteObject Also known as: bpm, tempo

Returns the song tempo in beats per minute.


68
69
70
71
72
# File 'lib/midilib/sequence.rb', line 68

def beats_per_minute
  return DEFAULT_TEMPO if @tracks.nil? || @tracks.empty?
  event = @tracks.first.events.detect { |e| e.kind_of?(MIDI::Tempo) }
  return event ? (Tempo.mpq_to_bpm(event.tempo)) : DEFAULT_TEMPO
end

#eachObject

Iterates over the tracks.


149
150
151
# File 'lib/midilib/sequence.rb', line 149

def each			# :yields: track
  @tracks.each { |track| yield track }
end

#get_measuresObject

Returns a Measures object, which is an array container for all measures in the sequence


155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/midilib/sequence.rb', line 155

def get_measures
  # Collect time sig events and scan for last event time
  time_sigs = []
  max_pos = 0
  @tracks.each  do |t|
    t.each do |e|
      time_sigs << e if e.kind_of?(MIDI::TimeSig)
      max_pos = e.time_from_start if e.time_from_start > max_pos
    end
  end
  time_sigs.sort { |x,y| x.time_from_start <=> y.time_from_start }

  # Add a "fake" time sig event at the very last position of the sequence,
  # just to make sure the whole sequence is calculated.
  t = MIDI::TimeSig.new(4, 2, 24, 8, 0)
  t.time_from_start = max_pos
  time_sigs << t

  # Default to 4/4
  measure_length = @ppqn * 4
  oldnumer, olddenom, oldbeats = 4, 2, 24

  measures = MIDI::Measures.new(max_pos, @ppqn)
  curr_pos = 0
  curr_meas_no = 1
  time_sigs.each do |te|
    meas_count = (te.time_from_start - curr_pos) / measure_length
    meas_count += 1 if (te.time_from_start - curr_pos) % measure_length > 0
    1.upto(meas_count) do |i|
      measures << MIDI::Measure.new(curr_meas_no, curr_pos, measure_length,
                                    oldnumer, olddenom, oldbeats)
      curr_meas_no += 1
      curr_pos += measure_length
    end
    oldnumer, olddenom, oldbeats = te.numerator, te.denominator, te.metronome_ticks
    measure_length = te.measure_duration(@ppqn)
  end
  measures
end

#length_to_delta(length) ⇒ Object

Translates length (a multiple of a quarter note) into a delta time. For example, 1 is a quarter note, 1.0/32.0 is a 32nd note, 1.5 is a dotted quarter, etc. Be aware when using division; 1/32 is zero due to integer mathematics and rounding. Use floating-point numbers like 1.0 and 32.0. This method always returns an integer.

See also note_to_delta and note_to_length.


119
120
121
# File 'lib/midilib/sequence.rb', line 119

def length_to_delta(length)
  return (@ppqn * length).to_i
end

#nameObject

Returns the name of the first track (track zero). If there are no tracks, returns UNNAMED.


125
126
127
128
# File 'lib/midilib/sequence.rb', line 125

def name
  return UNNAMED if @tracks.empty?
  return @tracks.first.name()
end

#name=(name) ⇒ Object

Hands the name to the first track. Does nothing if there are no tracks.


131
132
133
134
# File 'lib/midilib/sequence.rb', line 131

def name=(name)
  return if @tracks.empty?
  @tracks.first.name = name
end

#note_to_delta(name) ⇒ Object

Given a note length name like “whole”, “dotted quarter”, or “8th triplet”, return the length of that note in quarter notes as a delta time.


86
87
88
# File 'lib/midilib/sequence.rb', line 86

def note_to_delta(name)
  return length_to_delta(note_to_length(name))
end

#note_to_length(name) ⇒ Object

Given a note length name like “whole”, “dotted quarter”, or “8th triplet”, return the length of that note in quarter notes as a floating-point number, suitable for use as an argument to length_to_delta.

Legal names are any value in NOTE_TO_LENGTH, optionally prefixed by “dotted_” and/or suffixed by “_triplet”. So, for example, “dotted_quarter_triplet” returns the length of a dotted quarter-note triplet and “32nd” returns 1/32.


99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/midilib/sequence.rb', line 99

def note_to_length(name)
  name.strip!
  name =~ /^(dotted)?(.*?)(triplet)?$/
  dotted, note_name, triplet = $1, $2, $3
  note_name.strip!
  mult = 1.0
  mult = 1.5 if dotted
  mult /= 3.0 if triplet
  len = NOTE_TO_LENGTH[note_name]
  raise "Sequence.note_to_length: \"#{note_name}\" not understood in \"#{name}\"" unless len
  return len * mult
end

#pulses_to_seconds(pulses) ⇒ Object

Pulses (also called ticks) are the units of delta times and event time_from_start values. This method converts a number of pulses to a float value that is a time in seconds.


79
80
81
# File 'lib/midilib/sequence.rb', line 79

def pulses_to_seconds(pulses)
  (pulses.to_f / @ppqn.to_f / beats_per_minute()) * 60.0
end

#read(io, proc = nil) ⇒ Object

Reads a MIDI stream.


137
138
139
140
# File 'lib/midilib/sequence.rb', line 137

def read(io, proc = nil)	# :yields: track, num_tracks, index
  reader = @reader_class.new(self, block_given?() ? Proc.new() : proc)
  reader.read_from(io)
end

#time_signature(numer, denom, clocks, qnotes) ⇒ Object

Sets the time signature.


60
61
62
63
64
65
# File 'lib/midilib/sequence.rb', line 60

def time_signature(numer, denom, clocks, qnotes)
  @numer = numer
  @denom = denom
  @clocks = clocks
  @qnotes = qnotes
end

#write(io, proc = nil) ⇒ Object

Writes to a MIDI stream.


143
144
145
146
# File 'lib/midilib/sequence.rb', line 143

def write(io, proc = nil)	# :yields: track, num_tracks, index
  writer = @writer_class.new(self, block_given?() ? Proc.new() : proc)
  writer.write_to(io)
end