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
73
74
# 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.



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

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



150
151
152
153
154
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
# File 'lib/midilib/sequence.rb', line 150

def get_measures      
   # Collect time sig events and scan for last event time
   time_sigs = []
   max_pos = 0
   @tracks.each  { |t|
     t.each { |e| 
       time_sigs << e if e.kind_of?(MIDI::TimeSig)
       max_pos = e.time_from_start if e.time_from_start > max_pos
     }
   }
   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 { |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) { |i|                  
          measures << MIDI::Measure.new(curr_meas_no, 
                      curr_pos, measure_length, oldnumer, olddenom, oldbeats) 
          curr_meas_no += 1
          curr_pos += measure_length
      }
      oldnumer, olddenom, oldbeats = te.numerator, te.denominator, te.metronome_ticks
      measure_length = te.measure_duration(@ppqn)        
   }      
   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.



114
115
116
# File 'lib/midilib/sequence.rb', line 114

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.



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

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.



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

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.



81
82
83
# File 'lib/midilib/sequence.rb', line 81

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.



94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/midilib/sequence.rb', line 94

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

#read(io, proc = nil) ⇒ Object

Reads a MIDI stream.



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

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.



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

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