midilib 2.0.2 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/ChangeLog +2 -1
- data/Credits +44 -2
- data/README.rdoc +42 -33
- data/Rakefile +36 -53
- data/TODO.rdoc +13 -2
- data/examples/from_scratch.rb +4 -6
- data/examples/measures_mbt.rb +11 -11
- data/examples/print_program_changes.rb +11 -11
- data/examples/reader2text.rb +191 -190
- data/examples/seq2text.rb +18 -18
- data/examples/split.rb +21 -20
- data/examples/strings.rb +15 -15
- data/examples/transpose.rb +41 -42
- data/install.rb +53 -34
- data/lib/midilib/consts.rb +406 -408
- data/lib/midilib/event.rb +335 -306
- data/lib/midilib/info.rb +5 -7
- data/lib/midilib/io/midifile.rb +424 -452
- data/lib/midilib/io/seqreader.rb +187 -192
- data/lib/midilib/io/seqwriter.rb +151 -147
- data/lib/midilib/measure.rb +78 -80
- data/lib/midilib/mergesort.rb +39 -0
- data/lib/midilib/sequence.rb +99 -86
- data/lib/midilib/track.rb +71 -118
- data/lib/midilib/utils.rb +17 -20
- data/lib/midilib.rb +5 -5
- data/test/event_equality.rb +50 -52
- data/test/test_event.rb +120 -124
- data/test/test_io.rb +107 -40
- data/test/test_mergesort.rb +37 -0
- data/test/test_midifile.rb +6 -19
- data/test/test_sequence.rb +64 -52
- data/test/test_track.rb +126 -155
- data/test/test_varlen.rb +23 -27
- metadata +20 -22
data/lib/midilib/event.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require_relative 'consts'
|
2
|
+
require_relative 'utils'
|
3
3
|
|
4
4
|
module MIDI
|
5
|
-
|
6
|
-
|
7
|
-
class Event
|
5
|
+
# The abstract superclass of all MIDI events.
|
6
|
+
class Event
|
8
7
|
# Modifying delta_time does not affect time_from_start. You need to call
|
9
8
|
# the event's track's +recalc_time+ method.
|
10
9
|
attr_accessor :delta_time
|
@@ -28,9 +27,12 @@ class Event
|
|
28
27
|
attr_accessor :print_channel_numbers_from_one
|
29
28
|
|
30
29
|
def initialize(status = 0, delta_time = 0)
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
@status = status
|
31
|
+
@delta_time = delta_time
|
32
|
+
@time_from_start = 0 # maintained by tracks
|
33
|
+
@print_note_names = false
|
34
|
+
@print_decimal_numbers = false
|
35
|
+
@print_channel_numbers_from_one = false
|
34
36
|
end
|
35
37
|
protected :initialize
|
36
38
|
|
@@ -38,7 +40,7 @@ class Event
|
|
38
40
|
# MIDI stream. In MIDI::EVENT this raises a "subclass responsibility"
|
39
41
|
# exception.
|
40
42
|
def data_as_bytes
|
41
|
-
|
43
|
+
raise 'subclass responsibility'
|
42
44
|
end
|
43
45
|
|
44
46
|
# Quantize this event's time_from_start by moving it to the nearest
|
@@ -46,593 +48,620 @@ class Event
|
|
46
48
|
# modify the event's delta_time, though MIDI::Track#quantize calls
|
47
49
|
# recalc_delta_from_times after it asks each event to quantize itself.
|
48
50
|
def quantize_to(boundary)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
@time_from_start += boundary
|
53
|
-
end
|
51
|
+
diff = @time_from_start % boundary
|
52
|
+
@time_from_start -= diff
|
53
|
+
@time_from_start += boundary if diff >= boundary / 2
|
54
54
|
end
|
55
55
|
|
56
56
|
# For sorting. Uses @time_from_start, which is maintained by this event's
|
57
57
|
# track. I'm not sure this is necessary, since each track has to
|
58
58
|
# maintain its events' time-from-start values anyway.
|
59
|
-
def <=>(
|
60
|
-
|
59
|
+
def <=>(other)
|
60
|
+
@time_from_start <=> other.time_from_start
|
61
61
|
end
|
62
62
|
|
63
63
|
# Returns +val+ as a decimal or hex string, depending upon the value of
|
64
64
|
# @print_decimal_numbers.
|
65
65
|
def number_to_s(val)
|
66
|
-
|
66
|
+
@print_decimal_numbers ? val.to_s : ('%02x' % val)
|
67
67
|
end
|
68
68
|
|
69
69
|
# Returns +val+ as a decimal or hex string, depending upon the value of
|
70
70
|
# @print_decimal_numbers.
|
71
71
|
def channel_to_s(val)
|
72
|
-
|
73
|
-
|
72
|
+
val += 1 if @print_channel_numbers_from_one
|
73
|
+
number_to_s(val)
|
74
74
|
end
|
75
75
|
|
76
76
|
def to_s
|
77
|
-
|
77
|
+
"#{@delta_time}: "
|
78
78
|
end
|
79
|
-
end
|
79
|
+
end
|
80
80
|
|
81
|
-
# The abstract superclass of all channel events (events that have a MIDI
|
82
|
-
# channel, like notes and program changes).
|
83
|
-
class ChannelEvent < Event
|
81
|
+
# The abstract superclass of all channel events (events that have a MIDI
|
82
|
+
# channel, like notes and program changes).
|
83
|
+
class ChannelEvent < Event
|
84
84
|
# MIDI channel, 0-15.
|
85
85
|
attr_accessor :channel
|
86
86
|
|
87
87
|
def initialize(status, channel, delta_time)
|
88
|
-
|
89
|
-
|
88
|
+
super(status, delta_time)
|
89
|
+
@channel = channel
|
90
90
|
end
|
91
91
|
protected :initialize
|
92
92
|
|
93
93
|
def to_s
|
94
|
-
|
94
|
+
super << "ch #{channel_to_s(@channel)} "
|
95
95
|
end
|
96
|
+
end
|
96
97
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
# pressure events.
|
101
|
-
class NoteEvent < ChannelEvent
|
98
|
+
# The abstract superclass of all note on, and note off, and polyphonic
|
99
|
+
# pressure events.
|
100
|
+
class NoteEvent < ChannelEvent
|
102
101
|
attr_accessor :note, :velocity
|
102
|
+
|
103
103
|
def initialize(status, channel, note, velocity, delta_time)
|
104
|
-
|
105
|
-
|
106
|
-
|
104
|
+
super(status, channel, delta_time)
|
105
|
+
@note = note
|
106
|
+
@velocity = velocity
|
107
107
|
end
|
108
108
|
protected :initialize
|
109
109
|
|
110
|
-
PITCHES = %w
|
110
|
+
PITCHES = %w[C C# D D# E F F# G G# A A# B]
|
111
111
|
|
112
112
|
# Returns note name as a pitch/octave string like "C4" or "F#6".
|
113
|
-
def pch_oct(val
|
114
|
-
|
115
|
-
|
116
|
-
|
113
|
+
def pch_oct(val = @note)
|
114
|
+
pch = val % 12
|
115
|
+
oct = (val / 12) - 1
|
116
|
+
"#{PITCHES[pch]}#{oct}"
|
117
117
|
end
|
118
118
|
|
119
119
|
# If @print_note_names is true, returns pch_oct(val) else returns value
|
120
120
|
# as a number using number_to_s.
|
121
121
|
def note_to_s
|
122
|
-
|
122
|
+
@print_note_names ? pch_oct(@note) : number_to_s(@note)
|
123
123
|
end
|
124
124
|
|
125
125
|
def data_as_bytes
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
126
|
+
data = []
|
127
|
+
data << (@status + @channel)
|
128
|
+
data << @note
|
129
|
+
data << @velocity
|
130
130
|
end
|
131
|
-
end
|
131
|
+
end
|
132
132
|
|
133
|
-
class NoteOn < NoteEvent
|
133
|
+
class NoteOn < NoteEvent
|
134
134
|
attr_accessor :off
|
135
|
+
|
135
136
|
def initialize(channel = 0, note = 64, velocity = 64, delta_time = 0)
|
136
|
-
|
137
|
+
super(NOTE_ON, channel, note, velocity, delta_time)
|
137
138
|
end
|
138
139
|
|
139
140
|
def to_s
|
140
|
-
|
141
|
-
|
141
|
+
super <<
|
142
|
+
"on #{note_to_s} #{number_to_s(@velocity)}"
|
142
143
|
end
|
143
|
-
end
|
144
|
+
end
|
144
145
|
|
145
|
-
# Old class name for compatability
|
146
|
-
NoteOnEvent = NoteOn
|
146
|
+
# Old class name for compatability
|
147
|
+
NoteOnEvent = NoteOn
|
147
148
|
|
148
|
-
class NoteOff < NoteEvent
|
149
|
+
class NoteOff < NoteEvent
|
149
150
|
attr_accessor :on
|
151
|
+
|
150
152
|
def initialize(channel = 0, note = 64, velocity = 64, delta_time = 0)
|
151
|
-
|
153
|
+
super(NOTE_OFF, channel, note, velocity, delta_time)
|
152
154
|
end
|
153
155
|
|
154
156
|
def to_s
|
155
|
-
|
156
|
-
|
157
|
+
super <<
|
158
|
+
"off #{note_to_s} #{number_to_s(@velocity)}"
|
157
159
|
end
|
158
|
-
end
|
160
|
+
end
|
159
161
|
|
160
|
-
# Old class name for compatability
|
161
|
-
NoteOffEvent = NoteOff
|
162
|
+
# Old class name for compatability
|
163
|
+
NoteOffEvent = NoteOff
|
162
164
|
|
163
|
-
class PolyPressure < NoteEvent
|
165
|
+
class PolyPressure < NoteEvent
|
164
166
|
def initialize(channel = 0, note = 64, value = 0, delta_time = 0)
|
165
|
-
|
167
|
+
super(POLY_PRESSURE, channel, note, value, delta_time)
|
166
168
|
end
|
167
169
|
|
168
170
|
def pressure
|
169
|
-
|
171
|
+
@velocity
|
170
172
|
end
|
173
|
+
|
171
174
|
def pressure=(val)
|
172
|
-
|
175
|
+
@velocity = val
|
173
176
|
end
|
177
|
+
|
174
178
|
def to_s
|
175
|
-
|
176
|
-
|
179
|
+
super <<
|
180
|
+
"poly press #{channel_to_s(@channel)} #{note_to_s} #{number_to_s(@velocity)}"
|
177
181
|
end
|
178
|
-
end
|
182
|
+
end
|
179
183
|
|
180
|
-
class Controller < ChannelEvent
|
184
|
+
class Controller < ChannelEvent
|
181
185
|
attr_accessor :controller, :value
|
182
186
|
|
183
187
|
def initialize(channel = 0, controller = 0, value = 0, delta_time = 0)
|
184
|
-
|
185
|
-
|
186
|
-
|
188
|
+
super(CONTROLLER, channel, delta_time)
|
189
|
+
@controller = controller
|
190
|
+
@value = value
|
187
191
|
end
|
188
192
|
|
189
193
|
def data_as_bytes
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
+
data = []
|
195
|
+
data << (@status + @channel)
|
196
|
+
data << @controller
|
197
|
+
data << @value
|
194
198
|
end
|
195
199
|
|
196
200
|
def to_s
|
197
|
-
|
201
|
+
super << "cntl #{number_to_s(@controller)} #{number_to_s(@value)}"
|
198
202
|
end
|
199
|
-
end
|
203
|
+
end
|
200
204
|
|
201
|
-
class ProgramChange < ChannelEvent
|
205
|
+
class ProgramChange < ChannelEvent
|
202
206
|
attr_accessor :program
|
203
207
|
|
204
208
|
def initialize(channel = 0, program = 0, delta_time = 0)
|
205
|
-
|
206
|
-
|
209
|
+
super(PROGRAM_CHANGE, channel, delta_time)
|
210
|
+
@program = program
|
207
211
|
end
|
208
212
|
|
209
213
|
def data_as_bytes
|
210
|
-
|
211
|
-
|
212
|
-
|
214
|
+
data = []
|
215
|
+
data << (@status + @channel)
|
216
|
+
data << @program
|
213
217
|
end
|
214
218
|
|
215
219
|
def to_s
|
216
|
-
|
220
|
+
super << "prog #{number_to_s(@program)}"
|
217
221
|
end
|
218
|
-
end
|
222
|
+
end
|
219
223
|
|
220
|
-
class ChannelPressure < ChannelEvent
|
224
|
+
class ChannelPressure < ChannelEvent
|
221
225
|
attr_accessor :pressure
|
222
226
|
|
223
227
|
def initialize(channel = 0, pressure = 0, delta_time = 0)
|
224
|
-
|
225
|
-
|
228
|
+
super(CHANNEL_PRESSURE, channel, delta_time)
|
229
|
+
@pressure = pressure
|
226
230
|
end
|
227
231
|
|
228
232
|
def data_as_bytes
|
229
|
-
|
230
|
-
|
231
|
-
|
233
|
+
data = []
|
234
|
+
data << (@status + @channel)
|
235
|
+
data << @pressure
|
232
236
|
end
|
233
237
|
|
234
238
|
def to_s
|
235
|
-
|
239
|
+
super << "chan press #{number_to_s(@pressure)}"
|
236
240
|
end
|
237
|
-
end
|
241
|
+
end
|
238
242
|
|
239
|
-
class PitchBend < ChannelEvent
|
243
|
+
class PitchBend < ChannelEvent
|
240
244
|
attr_accessor :value
|
241
245
|
|
242
246
|
def initialize(channel = 0, value = 0, delta_time = 0)
|
243
|
-
|
244
|
-
|
247
|
+
super(PITCH_BEND, channel, delta_time)
|
248
|
+
@value = value
|
245
249
|
end
|
246
250
|
|
247
251
|
def data_as_bytes
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
+
data = []
|
253
|
+
data << (@status + @channel)
|
254
|
+
data << (@value & 0x7f) # lsb
|
255
|
+
data << ((@value >> 7) & 0x7f) # msb
|
252
256
|
end
|
253
257
|
|
254
258
|
def to_s
|
255
|
-
|
259
|
+
super << "pb #{number_to_s(@value)}"
|
256
260
|
end
|
257
|
-
end
|
261
|
+
end
|
258
262
|
|
259
|
-
class SystemCommon < Event
|
263
|
+
class SystemCommon < Event
|
260
264
|
def initialize(status, delta_time)
|
261
|
-
|
265
|
+
super(status, delta_time)
|
262
266
|
end
|
263
|
-
end
|
267
|
+
end
|
264
268
|
|
265
|
-
class SystemExclusive < SystemCommon
|
269
|
+
class SystemExclusive < SystemCommon
|
266
270
|
attr_accessor :data
|
267
271
|
|
268
272
|
def initialize(data, delta_time = 0)
|
269
|
-
|
270
|
-
|
273
|
+
super(SYSEX, delta_time)
|
274
|
+
@data = data
|
271
275
|
end
|
272
276
|
|
273
277
|
def data_as_bytes
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
278
|
+
data = []
|
279
|
+
data << @status
|
280
|
+
data << Utils.as_var_len(@data.length)
|
281
|
+
data << @data
|
282
|
+
data << EOX
|
283
|
+
data.flatten
|
280
284
|
end
|
281
285
|
|
282
286
|
def to_s
|
283
|
-
|
287
|
+
super << 'sys ex'
|
284
288
|
end
|
285
|
-
end
|
289
|
+
end
|
286
290
|
|
287
|
-
class SongPointer < SystemCommon
|
291
|
+
class SongPointer < SystemCommon
|
288
292
|
attr_accessor :pointer
|
289
293
|
|
290
294
|
def initialize(pointer = 0, delta_time = 0)
|
291
|
-
|
292
|
-
|
295
|
+
super(SONG_POINTER, delta_time)
|
296
|
+
@pointer = pointer
|
293
297
|
end
|
294
298
|
|
295
299
|
def data_as_bytes
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
+
data = []
|
301
|
+
data << @status
|
302
|
+
data << ((@pointer >> 8) & 0xff)
|
303
|
+
data << (@pointer & 0xff)
|
300
304
|
end
|
301
305
|
|
302
306
|
def to_s
|
303
|
-
|
307
|
+
super << "song ptr #{number_to_s(@pointer)}"
|
304
308
|
end
|
305
|
-
end
|
309
|
+
end
|
306
310
|
|
307
|
-
class SongSelect < SystemCommon
|
311
|
+
class SongSelect < SystemCommon
|
308
312
|
attr_accessor :song
|
309
313
|
|
310
314
|
def initialize(song = 0, delta_time = 0)
|
311
|
-
|
312
|
-
|
315
|
+
super(SONG_SELECT, delta_time)
|
316
|
+
@song = song
|
313
317
|
end
|
314
318
|
|
315
319
|
def data_as_bytes
|
316
|
-
|
317
|
-
|
318
|
-
|
320
|
+
data = []
|
321
|
+
data << @status
|
322
|
+
data << @song
|
319
323
|
end
|
320
324
|
|
321
325
|
def to_s
|
322
|
-
|
326
|
+
super << "song sel #{number_to_s(@song)}"
|
323
327
|
end
|
324
|
-
end
|
328
|
+
end
|
325
329
|
|
326
|
-
class TuneRequest < SystemCommon
|
330
|
+
class TuneRequest < SystemCommon
|
327
331
|
def initialize(delta_time = 0)
|
328
|
-
|
332
|
+
super(TUNE_REQUEST, delta_time)
|
329
333
|
end
|
330
334
|
|
331
335
|
def data_as_bytes
|
332
|
-
|
333
|
-
|
336
|
+
data = []
|
337
|
+
data << @status
|
334
338
|
end
|
335
339
|
|
336
340
|
def to_s
|
337
|
-
|
341
|
+
super << 'tune req'
|
338
342
|
end
|
339
|
-
end
|
343
|
+
end
|
340
344
|
|
341
|
-
class Realtime < Event
|
345
|
+
class Realtime < Event
|
342
346
|
def initialize(status, delta_time)
|
343
|
-
|
347
|
+
super(status, delta_time)
|
344
348
|
end
|
345
349
|
|
346
350
|
def data_as_bytes
|
347
|
-
|
348
|
-
|
351
|
+
data = []
|
352
|
+
data << @status
|
349
353
|
end
|
350
354
|
|
351
355
|
def to_s
|
352
|
-
|
356
|
+
super << "realtime #{number_to_s(@status)}"
|
353
357
|
end
|
354
|
-
end
|
358
|
+
end
|
355
359
|
|
356
|
-
class Clock < Realtime
|
360
|
+
class Clock < Realtime
|
357
361
|
def initialize(delta_time = 0)
|
358
|
-
|
362
|
+
super(CLOCK, delta_time)
|
359
363
|
end
|
360
364
|
|
361
365
|
def to_s
|
362
|
-
|
366
|
+
super << 'clock'
|
363
367
|
end
|
364
|
-
end
|
368
|
+
end
|
365
369
|
|
366
|
-
class Start < Realtime
|
370
|
+
class Start < Realtime
|
367
371
|
def initialize(delta_time = 0)
|
368
|
-
|
372
|
+
super(START, delta_time)
|
369
373
|
end
|
374
|
+
|
370
375
|
def to_s
|
371
|
-
|
376
|
+
super << 'start'
|
372
377
|
end
|
373
|
-
end
|
378
|
+
end
|
374
379
|
|
375
|
-
class Continue < Realtime
|
380
|
+
class Continue < Realtime
|
376
381
|
def initialize(delta_time = 0)
|
377
|
-
|
382
|
+
super(CONTINUE, delta_time)
|
378
383
|
end
|
384
|
+
|
379
385
|
def to_s
|
380
|
-
|
386
|
+
super << 'continue'
|
381
387
|
end
|
382
|
-
end
|
388
|
+
end
|
383
389
|
|
384
|
-
class Stop < Realtime
|
390
|
+
class Stop < Realtime
|
385
391
|
def initialize(delta_time = 0)
|
386
|
-
|
392
|
+
super(STOP, delta_time)
|
387
393
|
end
|
394
|
+
|
388
395
|
def to_s
|
389
|
-
|
396
|
+
super << 'stop'
|
390
397
|
end
|
391
|
-
end
|
398
|
+
end
|
392
399
|
|
393
|
-
class ActiveSense < Realtime
|
400
|
+
class ActiveSense < Realtime
|
394
401
|
def initialize(delta_time = 0)
|
395
|
-
|
402
|
+
super(ACTIVE_SENSE, delta_time)
|
396
403
|
end
|
404
|
+
|
397
405
|
def to_s
|
398
|
-
|
406
|
+
super << 'act sens'
|
399
407
|
end
|
400
|
-
end
|
408
|
+
end
|
401
409
|
|
402
|
-
class SystemReset < Realtime
|
410
|
+
class SystemReset < Realtime
|
403
411
|
def initialize(delta_time = 0)
|
404
|
-
|
412
|
+
super(SYSTEM_RESET, delta_time)
|
405
413
|
end
|
414
|
+
|
406
415
|
def to_s
|
407
|
-
|
416
|
+
super << 'sys reset'
|
408
417
|
end
|
409
|
-
end
|
418
|
+
end
|
410
419
|
|
411
|
-
class MetaEvent < Event
|
412
|
-
attr_reader :meta_type
|
413
|
-
attr_reader :data
|
420
|
+
class MetaEvent < Event
|
421
|
+
attr_reader :meta_type, :data
|
414
422
|
|
415
423
|
def self.bytes_as_str(bytes)
|
416
|
-
|
424
|
+
bytes ? bytes.collect { |byte| byte.chr }.join : nil
|
417
425
|
end
|
418
426
|
|
419
|
-
|
420
|
-
|
421
|
-
str.split(//).collect { |chr| chr.ord }
|
422
|
-
end
|
423
|
-
else
|
424
|
-
def self.str_as_bytes(str)
|
425
|
-
str.split(//).collect { |chr| chr[0] }
|
426
|
-
end
|
427
|
+
def self.str_as_bytes(str)
|
428
|
+
str.split(//).collect { |chr| chr.ord }
|
427
429
|
end
|
428
430
|
|
429
431
|
def initialize(meta_type, data = nil, delta_time = 0)
|
430
|
-
|
431
|
-
|
432
|
-
|
432
|
+
super(META_EVENT, delta_time)
|
433
|
+
@meta_type = meta_type
|
434
|
+
self.data = (data)
|
433
435
|
end
|
434
436
|
|
435
437
|
def data_as_bytes
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
438
|
+
data = []
|
439
|
+
data << @status
|
440
|
+
data << @meta_type
|
441
|
+
data << (@data ? Utils.as_var_len(@data.length) : 0)
|
442
|
+
data << @data if @data
|
443
|
+
data.flatten
|
442
444
|
end
|
443
445
|
|
444
446
|
def data_as_str
|
445
|
-
|
447
|
+
MetaEvent.bytes_as_str(@data)
|
446
448
|
end
|
447
449
|
|
448
450
|
# Stores bytes. If data is a string, splits it into an array of bytes.
|
449
451
|
def data=(data)
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
452
|
+
@data = case data
|
453
|
+
when String
|
454
|
+
MetaEvent.str_as_bytes(data)
|
455
|
+
else
|
456
|
+
data
|
457
|
+
end
|
456
458
|
end
|
457
459
|
|
458
460
|
def to_s
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
end
|
495
|
-
end
|
496
|
-
|
497
|
-
class Marker < MetaEvent
|
461
|
+
str = super()
|
462
|
+
str << "meta #{number_to_s(@meta_type)} "
|
463
|
+
# I know, I know...this isn't OO.
|
464
|
+
str << case @meta_type
|
465
|
+
when META_SEQ_NUM
|
466
|
+
'sequence number'
|
467
|
+
when META_TEXT
|
468
|
+
"text: #{data_as_str}"
|
469
|
+
when META_COPYRIGHT
|
470
|
+
"copyright: #{data_as_str}"
|
471
|
+
when META_SEQ_NAME
|
472
|
+
"sequence or track name: #{data_as_str}"
|
473
|
+
when META_INSTRUMENT
|
474
|
+
"instrument name: #{data_as_str}"
|
475
|
+
when META_LYRIC
|
476
|
+
"lyric: #{data_as_str}"
|
477
|
+
when META_MARKER
|
478
|
+
"marker: #{data_as_str}"
|
479
|
+
when META_CUE
|
480
|
+
"cue point: #{@data}"
|
481
|
+
when META_TRACK_END
|
482
|
+
'track end'
|
483
|
+
when META_SMPTE
|
484
|
+
'smpte'
|
485
|
+
when META_TIME_SIG
|
486
|
+
'time signature'
|
487
|
+
when META_KEY_SIG
|
488
|
+
'key signature'
|
489
|
+
when META_SEQ_SPECIF
|
490
|
+
'sequence specific'
|
491
|
+
else
|
492
|
+
# Some other possible @meta_type values are handled by subclasses.
|
493
|
+
'(other)'
|
494
|
+
end
|
495
|
+
str
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
class Marker < MetaEvent
|
498
500
|
def initialize(msg, delta_time = 0)
|
499
|
-
|
501
|
+
super(META_MARKER, msg, delta_time)
|
500
502
|
end
|
501
|
-
end
|
502
|
-
|
503
|
-
class Tempo < MetaEvent
|
503
|
+
end
|
504
504
|
|
505
|
+
class Tempo < MetaEvent
|
505
506
|
MICROSECS_PER_MINUTE = 1_000_000 * 60
|
506
507
|
|
507
508
|
# Translates beats per minute to microseconds per quarter note (beat).
|
508
|
-
def
|
509
|
-
|
509
|
+
def self.bpm_to_mpq(bpm)
|
510
|
+
MICROSECS_PER_MINUTE / bpm
|
510
511
|
end
|
511
512
|
|
512
513
|
# Translates microseconds per quarter note (beat) to beats per minute.
|
513
|
-
def
|
514
|
-
|
514
|
+
def self.mpq_to_bpm(mpq)
|
515
|
+
MICROSECS_PER_MINUTE.to_f / mpq.to_f
|
515
516
|
end
|
516
517
|
|
517
518
|
def initialize(msecs_per_qnote, delta_time = 0)
|
518
|
-
|
519
|
+
super(META_SET_TEMPO, msecs_per_qnote, delta_time)
|
519
520
|
end
|
520
521
|
|
521
522
|
def tempo
|
522
|
-
|
523
|
+
@data
|
523
524
|
end
|
524
525
|
|
525
526
|
def tempo=(val)
|
526
|
-
|
527
|
+
@data = val
|
527
528
|
end
|
528
529
|
|
529
530
|
def data_as_bytes
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
531
|
+
data = []
|
532
|
+
data << @status
|
533
|
+
data << @meta_type
|
534
|
+
data << 3
|
535
|
+
data << ((@data >> 16) & 0xff)
|
536
|
+
data << ((@data >> 8) & 0xff)
|
537
|
+
data << (@data & 0xff)
|
537
538
|
end
|
538
539
|
|
539
540
|
def to_s
|
540
|
-
|
541
|
+
"tempo #{@data} msecs per qnote (#{Tempo.mpq_to_bpm(@data)} bpm)"
|
541
542
|
end
|
542
|
-
end
|
543
|
+
end
|
543
544
|
|
544
|
-
# Container for time signature events
|
545
|
-
class TimeSig < MetaEvent
|
546
|
-
|
545
|
+
# Container for time signature events
|
546
|
+
class TimeSig < MetaEvent
|
547
547
|
# Constructor
|
548
548
|
def initialize(numer, denom, clocks, qnotes, delta_time = 0)
|
549
|
-
|
549
|
+
super(META_TIME_SIG, [numer, denom, clocks, qnotes], delta_time)
|
550
550
|
end
|
551
|
-
|
551
|
+
|
552
552
|
# Returns the complete event as stored in the sequence
|
553
553
|
def data_as_bytes
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
554
|
+
data = []
|
555
|
+
data << @status
|
556
|
+
data << @meta_type
|
557
|
+
data << 4
|
558
|
+
data << @data[0]
|
559
|
+
data << @data[1]
|
560
|
+
data << @data[2]
|
561
|
+
data << @data[3]
|
562
562
|
end
|
563
563
|
|
564
|
-
# Calculates the duration (in ticks) for a full measure
|
565
|
-
def measure_duration(ppqn)
|
566
|
-
|
564
|
+
# Calculates the duration (in ticks) for a full measure
|
565
|
+
def measure_duration(ppqn)
|
566
|
+
(4 * ppqn * @data[0]) / (2**@data[1])
|
567
567
|
end
|
568
|
-
|
568
|
+
|
569
569
|
# Returns the numerator (the top digit) for the time signature
|
570
570
|
def numerator
|
571
|
-
|
571
|
+
@data[0]
|
572
572
|
end
|
573
|
-
|
573
|
+
|
574
574
|
# Returns the denominator of the time signature. Use it as a power of 2
|
575
575
|
# to get the displayed (lower-part) digit of the time signature.
|
576
576
|
def denominator
|
577
|
-
|
577
|
+
@data[1]
|
578
578
|
end
|
579
|
-
|
579
|
+
|
580
580
|
# Returns the metronome tick duration for the time signature. On
|
581
581
|
# each quarter note, there's 24 ticks.
|
582
582
|
def metronome_ticks
|
583
|
-
|
583
|
+
@data[2]
|
584
584
|
end
|
585
|
-
|
585
|
+
|
586
586
|
# Returns the time signature for the event as a string.
|
587
587
|
# Example: "time sig 3/4"
|
588
588
|
def to_s
|
589
|
-
|
589
|
+
"time sig #{@data[0]}/#{2**@data[1]}"
|
590
590
|
end
|
591
|
-
end
|
591
|
+
end
|
592
592
|
|
593
|
-
# Container for key signature events
|
594
|
-
class KeySig < MetaEvent
|
595
|
-
|
593
|
+
# Container for key signature events
|
594
|
+
class KeySig < MetaEvent
|
596
595
|
# Constructor
|
597
596
|
def initialize(sharpflat, is_minor, delta_time = 0)
|
598
|
-
|
597
|
+
super(META_KEY_SIG, [sharpflat, is_minor], delta_time)
|
599
598
|
end
|
600
|
-
|
599
|
+
|
601
600
|
# Returns the complete event as stored in the sequence
|
602
601
|
def data_as_bytes
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
end
|
610
|
-
|
602
|
+
data = []
|
603
|
+
data << @status
|
604
|
+
data << @meta_type
|
605
|
+
data << 2
|
606
|
+
data << @data[0]
|
607
|
+
data << (@data[1] ? 1 : 0)
|
608
|
+
end
|
609
|
+
|
611
610
|
# Returns true if it's a minor key, false if major key
|
612
611
|
def minor_key?
|
613
|
-
|
612
|
+
@data[1]
|
614
613
|
end
|
615
|
-
|
614
|
+
|
616
615
|
# Returns true if it's a major key, false if minor key
|
617
616
|
def major_key?
|
618
|
-
|
617
|
+
!@data[1]
|
619
618
|
end
|
620
|
-
|
619
|
+
|
621
620
|
# Returns the number of sharps/flats in the key sig. Negative for flats.
|
622
621
|
def sharpflat
|
623
|
-
|
622
|
+
@data[0] > 7 ? @data[0] - 256 : @data[0]
|
624
623
|
end
|
625
|
-
|
624
|
+
|
626
625
|
# Returns the key signature as a text string.
|
627
626
|
# Example: "key sig A flat major"
|
628
627
|
def to_s
|
629
|
-
|
630
|
-
|
631
|
-
|
628
|
+
if minor_key?
|
629
|
+
"key sig #{minorkeys[sharpflat + 7]} minor"
|
630
|
+
else
|
631
|
+
"key sig #{majorkeys[sharpflat + 7]} major"
|
632
|
+
end
|
633
|
+
end
|
634
|
+
|
635
|
+
# Returns the key signature as a code.
|
636
|
+
# Example: "Ab" for "key sig A flat major"
|
637
|
+
def to_code
|
638
|
+
if minor_key?
|
639
|
+
minorkey_codes[sharpflat + 7]
|
640
|
+
else
|
641
|
+
majorkey_codes[sharpflat + 7]
|
642
|
+
end
|
643
|
+
end
|
644
|
+
|
645
|
+
private
|
646
|
+
|
647
|
+
def majorkeys
|
648
|
+
@majorkeys ||= ['C flat', 'G flat', 'D flat', 'A flat', 'E flat', 'B flat', 'F',
|
649
|
+
'C', 'G', 'D', 'A', 'E', 'B', 'F#', 'C#']
|
650
|
+
end
|
651
|
+
|
652
|
+
def minorkeys
|
653
|
+
@minorkeys ||= ['a flat', 'e flat', 'b flat', 'f', 'c', 'g', 'd',
|
632
654
|
'a', 'e', 'b', 'f#', 'c#', 'g#', 'd#', 'a#']
|
633
|
-
minor_key? ? "key sig #{minorkeys[sharpflat + 7]} minor" :
|
634
|
-
"key sig #{majorkeys[sharpflat + 7]} major"
|
635
655
|
end
|
636
|
-
end
|
637
656
|
|
657
|
+
def majorkey_codes
|
658
|
+
@majorkeys_codes ||= ['Cb', 'Gb', 'Db', 'Ab', 'Eb', 'Bb', 'F',
|
659
|
+
'C', 'G', 'D', 'A', 'E', 'B', 'F#', 'C#']
|
660
|
+
end
|
661
|
+
|
662
|
+
def minorkey_codes
|
663
|
+
@minorkeys_codes ||= ['Abm', 'Ebm', 'Bbm', 'Fm', 'Cm', 'Gm', 'Dm',
|
664
|
+
'Am', 'Em', 'Bm', 'F#m', 'C#m', 'G#m', 'D#m', 'A#m']
|
665
|
+
end
|
666
|
+
end
|
638
667
|
end
|