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.



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

def initialize
  @tracks = []
  @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.



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

def clocks
  @clocks
end

#denomObject

Returns the value of attribute denom.



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

def denom
  @denom
end

#formatObject

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



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

def format
  @format
end

#numerObject

Returns the value of attribute numer.



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

def numer
  @numer
end

#ppqnObject

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



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

def ppqn
  @ppqn
end

#qnotesObject

Returns the value of attribute qnotes.



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

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.



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

def reader_class
  @reader_class
end

#tracksObject

Array with all tracks for the sequence



30
31
32
# File 'lib/midilib/sequence.rb', line 30

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.



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

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.



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

def beats_per_minute
  return DEFAULT_TEMPO if @tracks.nil? || @tracks.empty?

  event = @tracks.first.events.detect { |e| e.is_a?(MIDI::Tempo) }
  event ? Tempo.mpq_to_bpm(event.tempo) : DEFAULT_TEMPO
end

#each(&block) ⇒ Object

Iterates over the tracks.



154
155
156
# File 'lib/midilib/sequence.rb', line 154

def each(&block) # :yields: track
  @tracks.each(&block)
end

#get_measuresObject

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



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
194
195
196
197
198
199
200
201
202
# File 'lib/midilib/sequence.rb', line 160

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.is_a?(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 = 4
  olddenom = 2
  oldbeats = 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 = te.numerator
    olddenom = te.denominator
    oldbeats = 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 by calling ‘.round` on the floating-point result.

See also note_to_delta and note_to_length.



122
123
124
# File 'lib/midilib/sequence.rb', line 122

def length_to_delta(length)
  (@ppqn * length).round
end

#nameObject

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



128
129
130
131
132
# File 'lib/midilib/sequence.rb', line 128

def name
  return UNNAMED if @tracks.empty?

  @tracks.first.name
end

#name=(name) ⇒ Object

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



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

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.



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

def note_to_delta(name)
  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.



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

def note_to_length(name)
  name.strip!
  name =~ /^(dotted)?(.*?)(triplet)?$/
  dotted = Regexp.last_match(1)
  note_name = Regexp.last_match(2)
  triplet = Regexp.last_match(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

  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.



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

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

#read(io, &block) ⇒ Object

Reads a MIDI stream.



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

def read(io, &block) # :yields: track, num_tracks, index
  reader = @reader_class.new(self, &block)
  reader.read_from(io)
end

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

Sets the time signature.



58
59
60
61
62
63
# File 'lib/midilib/sequence.rb', line 58

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

#write(io, midi_format = 1, &block) ⇒ Object

Writes to a MIDI stream. midi_format defaults to 1.



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

def write(io, midi_format = 1, &block) # :yields: track, num_tracks, index
  writer = @writer_class.new(self, midi_format, &block)
  writer.write_to(io)
end