midilib 2.0.4 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +7 -0
  2. data/ChangeLog +2 -1
  3. data/Credits +44 -2
  4. data/README.rdoc +13 -9
  5. data/Rakefile +36 -53
  6. data/TODO.rdoc +13 -2
  7. data/examples/from_scratch.rb +4 -6
  8. data/examples/measures_mbt.rb +11 -11
  9. data/examples/print_program_changes.rb +11 -11
  10. data/examples/reader2text.rb +191 -191
  11. data/examples/seq2text.rb +18 -18
  12. data/examples/split.rb +21 -20
  13. data/examples/strings.rb +15 -15
  14. data/examples/transpose.rb +41 -42
  15. data/html/MIDI/ActiveSense.html +89 -213
  16. data/html/MIDI/ChannelEvent.html +95 -224
  17. data/html/MIDI/ChannelPressure.html +103 -241
  18. data/html/MIDI/Clock.html +89 -213
  19. data/html/MIDI/Continue.html +89 -213
  20. data/html/MIDI/Controller.html +105 -246
  21. data/html/MIDI/Event.html +134 -358
  22. data/html/MIDI/IO/MIDIFile.html +544 -1148
  23. data/html/MIDI/IO/SeqReader.html +273 -577
  24. data/html/MIDI/IO/SeqWriter.html +233 -439
  25. data/html/MIDI/IO.html +48 -164
  26. data/html/MIDI/KeySig.html +148 -291
  27. data/html/MIDI/Marker.html +73 -192
  28. data/html/MIDI/Measure.html +104 -267
  29. data/html/MIDI/Measures.html +106 -259
  30. data/html/MIDI/MetaEvent.html +171 -352
  31. data/html/MIDI/NoteEvent.html +114 -276
  32. data/html/MIDI/NoteOff.html +95 -223
  33. data/html/MIDI/NoteOn.html +95 -223
  34. data/html/MIDI/PitchBend.html +104 -242
  35. data/html/MIDI/PolyPressure.html +102 -246
  36. data/html/MIDI/ProgramChange.html +103 -241
  37. data/html/MIDI/Realtime.html +96 -230
  38. data/html/MIDI/Sequence.html +256 -576
  39. data/html/MIDI/SongPointer.html +104 -242
  40. data/html/MIDI/SongSelect.html +103 -241
  41. data/html/MIDI/Start.html +89 -213
  42. data/html/MIDI/Stop.html +89 -213
  43. data/html/MIDI/SystemCommon.html +73 -192
  44. data/html/MIDI/SystemExclusive.html +106 -244
  45. data/html/MIDI/SystemReset.html +89 -213
  46. data/html/MIDI/Tempo.html +127 -309
  47. data/html/MIDI/TimeSig.html +119 -300
  48. data/html/MIDI/Track.html +214 -494
  49. data/html/MIDI/TuneRequest.html +96 -230
  50. data/html/MIDI/Utils.html +91 -233
  51. data/html/MIDI.html +142 -526
  52. data/html/Object.html +197 -0
  53. data/html/README_rdoc.html +280 -486
  54. data/html/TODO_rdoc.html +68 -145
  55. data/html/created.rid +15 -14
  56. data/html/css/fonts.css +167 -0
  57. data/html/css/rdoc.css +639 -0
  58. data/html/fonts/Lato-Light.ttf +0 -0
  59. data/html/fonts/Lato-LightItalic.ttf +0 -0
  60. data/html/fonts/Lato-Regular.ttf +0 -0
  61. data/html/fonts/Lato-RegularItalic.ttf +0 -0
  62. data/html/fonts/SourceCodePro-Bold.ttf +0 -0
  63. data/html/fonts/SourceCodePro-Regular.ttf +0 -0
  64. data/html/images/add.png +0 -0
  65. data/html/images/arrow_up.png +0 -0
  66. data/html/images/delete.png +0 -0
  67. data/html/images/tag_blue.png +0 -0
  68. data/html/index.html +230 -446
  69. data/html/js/darkfish.js +22 -91
  70. data/html/js/navigation.js +4 -41
  71. data/html/js/navigation.js.gz +0 -0
  72. data/html/js/search.js +41 -25
  73. data/html/js/search_index.js +1 -1
  74. data/html/js/search_index.js.gz +0 -0
  75. data/html/js/searcher.js +9 -8
  76. data/html/js/searcher.js.gz +0 -0
  77. data/html/table_of_contents.html +1111 -505
  78. data/install.rb +53 -34
  79. data/lib/midilib/consts.rb +406 -408
  80. data/lib/midilib/event.rb +335 -306
  81. data/lib/midilib/info.rb +5 -7
  82. data/lib/midilib/io/midifile.rb +424 -452
  83. data/lib/midilib/io/seqreader.rb +200 -192
  84. data/lib/midilib/io/seqwriter.rb +151 -147
  85. data/lib/midilib/measure.rb +78 -80
  86. data/lib/midilib/mergesort.rb +39 -0
  87. data/lib/midilib/sequence.rb +93 -87
  88. data/lib/midilib/track.rb +71 -118
  89. data/lib/midilib/utils.rb +17 -20
  90. data/lib/midilib.rb +5 -5
  91. data/test/event_equality.rb +50 -52
  92. data/test/test_event.rb +120 -124
  93. data/test/test_io.rb +118 -38
  94. data/test/test_mergesort.rb +37 -0
  95. data/test/test_midifile.rb +6 -19
  96. data/test/test_sequence.rb +62 -61
  97. data/test/test_track.rb +126 -155
  98. data/test/test_varlen.rb +23 -27
  99. metadata +67 -62
  100. data/html/IO.html +0 -259
  101. data/html/MIDI/MIDI/MIDI/Array.html +0 -353
  102. data/html/MIDI/MIDI/MIDI.html +0 -204
  103. data/html/MIDI/MIDI.html +0 -204
  104. data/html/js/jquery.js +0 -18
  105. data/html/rdoc.css +0 -543
@@ -1,467 +1,439 @@
1
- require 'midilib/consts'
2
-
3
- if RUBY_VERSION < '1.9'
4
- class IO
5
- def readbyte
6
- c = getc()
7
- raise 'unexpected EOF' unless c
8
- c
9
- end
10
- end
11
- end
1
+ require_relative '../consts'
12
2
 
13
3
  module MIDI
4
+ module IO
5
+ # A MIDIFile parses a MIDI file and calls methods when it sees MIDI events.
6
+ # Most of the methods are stubs. To do anything interesting with the events,
7
+ # override these methods (those between the "The rest of these are NOPs by
8
+ # default" and "End of NOPs" comments).
9
+ #
10
+ # See SeqReader for a subclass that uses these methods to create Event
11
+ # objects.
12
+ class MIDIFile
13
+ MThd_BYTE_ARRAY = [77, 84, 104, 100] # "MThd"
14
+ MTrk_BYTE_ARRAY = [77, 84, 114, 107] # "MTrk"
15
+
16
+ # This array is indexed by the high half of a status byte. Its
17
+ # value is either the number of bytes needed (1 or 2) for a channel
18
+ # message, or 0 if it's not a channel message.
19
+ NUM_DATA_BYTES = [
20
+ 0, 0, 0, 0, 0, 0, 0, 0, # 0x00 - 0x70
21
+ 2, 2, 2, 2, 1, 1, 2, 0 # 0x80 - 0xf0
22
+ ]
23
+
24
+ attr_accessor :curr_ticks, :ticks_so_far, :bytes_to_be_read, :no_merge, :skip_init, :raw_var_num_data, :raw_data # Current time, from delta-time in MIDI file # Number of delta-time ticks so far # Counts number of bytes expected # true means continued sysex are not collapsed # true if initial garbage should be skipped
14
25
 
15
- module IO
16
-
17
- # A MIDIFile parses a MIDI file and calls methods when it sees MIDI events.
18
- # Most of the methods are stubs. To do anything interesting with the events,
19
- # override these methods (those between the "The rest of these are NOPs by
20
- # default" and "End of NOPs" comments).
21
- #
22
- # See SeqReader for a subclass that uses these methods to create Event
23
- # objects.
24
- class MIDIFile
25
-
26
- MThd_BYTE_ARRAY = [77, 84, 104, 100] # "MThd"
27
- MTrk_BYTE_ARRAY = [77, 84, 114, 107] # "MTrk"
28
-
29
- # This array is indexed by the high half of a status byte. Its
30
- # value is either the number of bytes needed (1 or 2) for a channel
31
- # message, or 0 if it's not a channel message.
32
- NUM_DATA_BYTES = [
33
- 0, 0, 0, 0, 0, 0, 0, 0, # 0x00 - 0x70
34
- 2, 2, 2, 2, 1, 1, 2, 0 # 0x80 - 0xf0
35
- ]
36
-
37
- attr_accessor :curr_ticks # Current time, from delta-time in MIDI file
38
- attr_accessor :ticks_so_far # Number of delta-time ticks so far
39
- attr_accessor :bytes_to_be_read # Counts number of bytes expected
40
-
41
- attr_accessor :no_merge # true means continued sysex are not collapsed
42
- attr_accessor :skip_init # true if initial garbage should be skipped
43
-
44
- # Raw data info
45
- attr_accessor :raw_time_stamp_data
46
- attr_accessor :raw_var_num_data
47
- attr_accessor :raw_data
48
-
49
- def initialize
50
- @no_merge = false
51
- @skip_init = true
52
- @io = nil
53
- @bytes_to_be_read = 0
54
- @msg_buf = nil
55
- end
56
-
57
- # The only public method. Each MIDI event in the file causes a
58
- # method to be called.
59
- def read_from(io)
60
- error('must specify non-nil input stream') if io.nil?
61
- @io = io
62
-
63
- ntrks = read_header()
64
- error('No tracks!') if ntrks <= 0
65
-
66
- ntrks.times { read_track() }
67
- end
68
-
69
- # This default getc implementation tries to read a single byte
70
- # from io and returns it as an integer.
71
- def getc
72
- @bytes_to_be_read -= 1
73
- @io.readbyte()
74
- end
75
-
76
- # Return the next +n+ bytes from @io as an array.
77
- def get_bytes(n)
78
- buf = []
79
- n.times { buf << getc() }
80
- buf
81
- end
82
-
83
- # The default error handler.
84
- def error(str)
85
- loc = @io.tell() - 1
86
- raise "#{self.class.name} error at byte #{loc} (0x#{'%02x' % loc}): #{str}"
87
- end
88
-
89
- # The rest of these are NOPs by default.
90
-
91
- # MIDI header.
92
- def header(format, ntrks, division)
93
- end
94
-
95
- def start_track(bytes_to_be_read)
96
- end
97
-
98
- def end_track()
99
- end
100
-
101
- def note_on(chan, note, vel)
102
- end
103
-
104
- def note_off(chan, note, vel)
105
- end
106
-
107
- def pressure(chan, note, press)
108
- end
109
-
110
- def controller(chan, control, value)
111
- end
112
-
113
- def pitch_bend(chan, msb, lsb)
114
- end
115
-
116
- def program(chan, program)
117
- end
118
-
119
- def chan_pressure(chan, press)
120
- end
121
-
122
- def sysex(msg)
123
- end
124
-
125
- def meta_misc(type, msg)
126
- end
127
-
128
- def sequencer_specific(type, msg)
129
- end
130
-
131
- def sequence_number(num)
132
- end
133
-
134
- def text(type, msg)
135
- end
136
-
137
- def eot()
138
- end
139
-
140
- def time_signature(numer, denom, clocks, qnotes)
141
- end
142
-
143
- def smpte(hour, min, sec, frame, fract)
144
- end
145
-
146
- def tempo(microsecs)
147
- end
148
-
149
- def key_signature(sharpflat, is_minor)
150
- end
151
-
152
- def arbitrary(msg)
153
- end
154
-
155
- # End of NOPs.
156
-
157
-
158
- # Read through 'MThd' or 'MTrk' header string. If skip is true, attempt
159
- # to skip initial trash. If there is an error, #error is called.
160
- def read_mt_header_string(bytes, skip)
161
- b = []
162
- bytes_to_read = 4
163
- while true
164
- data = get_bytes(bytes_to_read)
165
- b += data
166
- if b.length < 4
167
- error("unexpected EOF while trying to read header" +
168
- " string #{s}")
169
- end
170
-
171
- # See if we found the bytes we're looking for
172
- return if b == bytes
173
-
174
- if skip # Try again with the next char
175
- i = b[1..-1].index(bytes[0])
176
- if i.nil?
177
- b = []
178
- bytes_to_read = 4
179
- else
180
- b = b[i..-1]
181
- bytes_to_read = 4 - i
182
- end
183
- else
184
- error("header string #{bytes.collect{|b| b.chr}.join} not found")
185
- end
186
- end
187
- end
188
-
189
- # Read a header chunk.
190
- def read_header
191
- @bytes_to_be_read = 0
192
- read_mt_header_string(MThd_BYTE_ARRAY, @skip_init) # "MThd"
193
-
194
- @bytes_to_be_read = read32()
195
- format = read16()
196
- ntrks = read16()
197
- division = read16()
198
-
199
- header(format, ntrks, division)
26
+ # Raw data info
27
+ attr_accessor :raw_time_stamp_data
200
28
 
201
- # Flush any extra stuff, in case the length of the header is not 6
202
- if @bytes_to_be_read > 0
203
- get_bytes(@bytes_to_be_read)
204
- @bytes_to_be_read = 0
205
- end
29
+ def initialize
30
+ @no_merge = false
31
+ @skip_init = true
32
+ @io = nil
33
+ @bytes_to_be_read = 0
34
+ @msg_buf = nil
35
+ end
206
36
 
207
- return ntrks
208
- end
37
+ # The only public method. Each MIDI event in the file causes a
38
+ # method to be called.
39
+ def read_from(io)
40
+ error('must specify non-nil input stream') if io.nil?
41
+ @io = io
209
42
 
210
- # Read a track chunk.
211
- def read_track
212
- c = c1 = type = needed = 0
213
- sysex_continue = false # True if last msg was unfinished
214
- running = false # True when running status used
215
- status = 0 # (Possibly running) status byte
216
-
217
- @bytes_to_be_read = 0
218
- read_mt_header_string(MTrk_BYTE_ARRAY, false)
219
-
220
- @bytes_to_be_read = read32()
221
- @curr_ticks = @ticks_so_far = 0
222
-
223
- start_track()
224
-
225
- while @bytes_to_be_read > 0
226
- @curr_ticks = read_var_len() # Delta time
227
- @ticks_so_far += @curr_ticks
228
-
229
- # Copy raw var num data into raw time stamp data
230
- @raw_time_stamp_data = @raw_var_num_data.dup()
231
-
232
- c = getc() # Read first byte
233
-
234
- if sysex_continue && c != EOX
235
- error("didn't find expected continuation of a sysex")
236
- end
237
-
238
- if (c & 0x80).zero? # Running status?
239
- error('unexpected running status') if status.zero?
240
- running = true
241
- else
242
- status = c
243
- running = false
244
- end
245
-
246
- needed = NUM_DATA_BYTES[(status >> 4) & 0x0f]
247
-
248
- if needed.nonzero? # i.e., is it a channel message?
249
- c1 = running ? c : (getc() & 0x7f)
250
-
251
- # The "& 0x7f" here may seem unnecessary, but I've seen
252
- # "bad" MIDI files that had, for example, volume bytes
253
- # with the upper bit set. This code should not harm
254
- # proper data.
255
- chan_message(running, status, c1,
256
- (needed > 1) ? (getc() & 0x7f) : 0)
257
- next
258
- end
259
-
260
- case c
261
- when META_EVENT # Meta event
262
- type = getc()
263
- msg_init()
264
- msg_read(read_var_len())
265
- meta_event(type)
266
- when SYSEX # Start of system exclusive
267
- msg_init()
268
- msg_add(SYSEX)
269
- c = msg_read(read_var_len())
270
-
271
- if c == EOX || !@no_merge
272
- handle_sysex(msg())
273
- else
274
- sysex_continue = true
275
- end
276
- when EOX # Sysex continuation or arbitrary stuff
277
- msg_init() if !sysex_continue
278
- c = msg_read(read_var_len())
279
-
280
- if !sysex_continue
281
- handle_arbitrary(msg())
282
- elsif c == EOX
283
- handle_sysex(msg())
284
- sysex_continue = false
285
- end
286
- else
287
- bad_byte(c)
288
- end
289
- end
290
- end_track()
291
- end
43
+ ntrks = read_header
44
+ error('No tracks!') if ntrks <= 0
45
+
46
+ ntrks.times { read_track }
47
+ end
48
+
49
+ # This default getc implementation tries to read a single byte
50
+ # from io and returns it as an integer.
51
+ def getc
52
+ @bytes_to_be_read -= 1
53
+ @io.readbyte
54
+ end
55
+
56
+ # Return the next +n+ bytes from @io as an array.
57
+ def get_bytes(n)
58
+ buf = []
59
+ n.times { buf << getc }
60
+ buf
61
+ end
62
+
63
+ # The default error handler.
64
+ def error(str)
65
+ loc = @io.tell - 1
66
+ raise "#{self.class.name} error at byte #{loc} (0x#{'%02x' % loc}): #{str}"
67
+ end
68
+
69
+ # The rest of these are NOPs by default.
70
+
71
+ # MIDI header.
72
+ def header(format, ntrks, division)
73
+ end
74
+
75
+ def start_track(bytes_to_be_read)
76
+ end
77
+
78
+ def end_track
79
+ end
80
+
81
+ def note_on(chan, note, vel)
82
+ end
83
+
84
+ def note_off(chan, note, vel)
85
+ end
86
+
87
+ def pressure(chan, note, press)
88
+ end
89
+
90
+ def controller(chan, control, value)
91
+ end
92
+
93
+ def pitch_bend(chan, msb, lsb)
94
+ end
95
+
96
+ def program(chan, program)
97
+ end
98
+
99
+ def chan_pressure(chan, press)
100
+ end
101
+
102
+ def sysex(msg)
103
+ end
104
+
105
+ def meta_misc(type, msg)
106
+ end
107
+
108
+ def sequencer_specific(type, msg)
109
+ end
110
+
111
+ def sequence_number(num)
112
+ end
292
113
 
293
- # Handle an unexpected byte.
294
- def bad_byte(c)
295
- error(sprintf("unexpected byte: 0x%02x", c))
296
- end
114
+ def text(type, msg)
115
+ end
116
+
117
+ def eot
118
+ end
297
119
 
298
- # Handle a meta event.
299
- def meta_event(type)
300
- m = msg() # Copy of internal message buffer
301
-
302
- # Create raw data array
303
- @raw_data = []
304
- @raw_data << META_EVENT
305
- @raw_data << type
306
- @raw_data << @raw_var_num_data
307
- @raw_data << m
308
- @raw_data.flatten!
309
-
310
- case type
311
- when META_SEQ_NUM
312
- sequence_number((m[0] << 8) + m[1])
313
- when META_TEXT, META_COPYRIGHT, META_SEQ_NAME, META_INSTRUMENT,
314
- META_LYRIC, META_MARKER, META_CUE, 0x08, 0x09, 0x0a,
315
- 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
316
- text(type, m)
317
- when META_TRACK_END
318
- eot()
319
- when META_SET_TEMPO
320
- tempo((m[0] << 16) + (m[1] << 8) + m[2])
321
- when META_SMPTE
322
- smpte(m[0], m[1], m[2], m[3], m[4])
323
- when META_TIME_SIG
324
- time_signature(m[0], m[1], m[2], m[3])
325
- when META_KEY_SIG
326
- key_signature(m[0], m[1] == 0 ? false : true)
327
- when META_SEQ_SPECIF
328
- sequencer_specific(type, m)
329
- else
330
- meta_misc(type, m)
331
- end
332
- end
120
+ def time_signature(numer, denom, clocks, qnotes)
121
+ end
333
122
 
334
- # Handle a channel message (note on, note off, etc.)
335
- def chan_message(running, status, c1, c2)
336
- @raw_data = []
337
- @raw_data << status unless running
338
- @raw_data << c1
339
- @raw_data << c2
340
-
341
- chan = status & 0x0f
342
-
343
- case (status & 0xf0)
344
- when NOTE_OFF
345
- note_off(chan, c1, c2)
346
- when NOTE_ON
347
- note_on(chan, c1, c2)
348
- when POLY_PRESSURE
349
- pressure(chan, c1, c2)
350
- when CONTROLLER
351
- controller(chan, c1, c2)
352
- when PITCH_BEND
353
- pitch_bend(chan, c1, c2)
354
- when PROGRAM_CHANGE
355
- program(chan, c1)
356
- when CHANNEL_PRESSURE
357
- chan_pressure(chan, c1)
358
- else
359
- error("illegal chan message 0x#{'%02x' % (status & 0xf0)}\n")
360
- end
361
- end
123
+ def smpte(hour, min, sec, frame, fract)
124
+ end
362
125
 
363
- # Copy message into raw data array, then call sysex().
364
- def handle_sysex(msg)
365
- @raw_data = msg.dup()
366
- sysex(msg)
367
- end
126
+ def tempo(microsecs)
127
+ end
368
128
 
369
- # Copy message into raw data array, then call arbitrary().
370
- def handle_arbitrary(msg)
371
- @raw_data = msg.dup()
372
- arbitrary(msg)
373
- end
129
+ def key_signature(sharpflat, is_minor)
130
+ end
131
+
132
+ def arbitrary(msg)
133
+ end
374
134
 
375
- # Read and return a sixteen bit value.
376
- def read16
377
- val = (getc() << 8) + getc()
378
- val = -(val & 0x7fff) if (val & 0x8000).nonzero?
379
- return val
380
- end
135
+ # End of NOPs.
381
136
 
382
- # Read and return a 32-bit value.
383
- def read32
384
- val = (getc() << 24) + (getc() << 16) + (getc() << 8) +
385
- getc()
386
- val = -(val & 0x7fffffff) if (val & 0x80000000).nonzero?
387
- return val
137
+ # Read through 'MThd' or 'MTrk' header string. If skip is true, attempt
138
+ # to skip initial trash. If there is an error, #error is called.
139
+ def read_mt_header_string(bytes, skip)
140
+ b = []
141
+ bytes_to_read = 4
142
+ while true
143
+ data = get_bytes(bytes_to_read)
144
+ b += data
145
+ error("unexpected EOF while trying to read header string #{s}") if b.length < 4
146
+
147
+ # See if we found the bytes we're looking for
148
+ return if b == bytes
149
+
150
+ if skip # Try again with the next char
151
+ i = b[1..-1].index(bytes[0])
152
+ if i.nil?
153
+ b = []
154
+ bytes_to_read = 4
155
+ else
156
+ b = b[i..-1]
157
+ bytes_to_read = 4 - i
158
+ end
159
+ else
160
+ error("header string #{bytes.collect { |b| b.chr }.join} not found")
161
+ end
162
+ end
163
+ end
164
+
165
+ # Read a header chunk.
166
+ def read_header
167
+ @bytes_to_be_read = 0
168
+ read_mt_header_string(MThd_BYTE_ARRAY, @skip_init) # "MThd"
169
+
170
+ @bytes_to_be_read = read32
171
+ format = read16
172
+ ntrks = read16
173
+ division = read16
174
+
175
+ header(format, ntrks, division)
176
+
177
+ # Flush any extra stuff, in case the length of the header is not 6
178
+ if @bytes_to_be_read > 0
179
+ get_bytes(@bytes_to_be_read)
180
+ @bytes_to_be_read = 0
181
+ end
182
+
183
+ ntrks
184
+ end
185
+
186
+ # Read a track chunk.
187
+ def read_track
188
+ c = c1 = type = needed = 0
189
+ sysex_continue = false # True if last msg was unfinished
190
+ running = false # True when running status used
191
+ status = 0 # (Possibly running) status byte
192
+
193
+ @bytes_to_be_read = 0
194
+ read_mt_header_string(MTrk_BYTE_ARRAY, false)
195
+
196
+ @bytes_to_be_read = read32
197
+ @curr_ticks = @ticks_so_far = 0
198
+
199
+ start_track
200
+
201
+ while @bytes_to_be_read > 0
202
+ @curr_ticks = read_var_len # Delta time
203
+ @ticks_so_far += @curr_ticks
204
+
205
+ # Copy raw var num data into raw time stamp data
206
+ @raw_time_stamp_data = @raw_var_num_data.dup
207
+
208
+ c = getc # Read first byte
209
+
210
+ error("didn't find expected continuation of a sysex") if sysex_continue && c != EOX
211
+
212
+ if (c & 0x80).zero? # Running status?
213
+ error('unexpected running status') if status.zero?
214
+ running = true
215
+ else
216
+ status = c
217
+ running = false
218
+ end
219
+
220
+ needed = NUM_DATA_BYTES[(status >> 4) & 0x0f]
221
+
222
+ if needed.nonzero? # i.e., is it a channel message?
223
+ c1 = running ? c : (getc & 0x7f)
224
+
225
+ # The "& 0x7f" here may seem unnecessary, but I've seen
226
+ # "bad" MIDI files that had, for example, volume bytes
227
+ # with the upper bit set. This code should not harm
228
+ # proper data.
229
+ chan_message(running, status, c1,
230
+ needed > 1 ? (getc & 0x7f) : 0)
231
+ next
232
+ end
233
+
234
+ case c
235
+ when META_EVENT # Meta event
236
+ type = getc
237
+ msg_init
238
+ msg_read(read_var_len)
239
+ meta_event(type)
240
+ when SYSEX # Start of system exclusive
241
+ msg_init
242
+ msg_add(SYSEX)
243
+ c = msg_read(read_var_len)
244
+
245
+ if c == EOX || !@no_merge
246
+ handle_sysex(msg)
247
+ else
248
+ sysex_continue = true
249
+ end
250
+ when EOX # Sysex continuation or arbitrary stuff
251
+ msg_init unless sysex_continue
252
+ c = msg_read(read_var_len)
253
+
254
+ if !sysex_continue
255
+ handle_arbitrary(msg)
256
+ elsif c == EOX
257
+ handle_sysex(msg)
258
+ sysex_continue = false
259
+ end
260
+ else
261
+ bad_byte(c)
262
+ end
263
+ end
264
+ end_track
265
+ end
266
+
267
+ # Handle an unexpected byte.
268
+ def bad_byte(c)
269
+ error(format('unexpected byte: 0x%02x', c))
270
+ end
271
+
272
+ # Handle a meta event.
273
+ def meta_event(type)
274
+ m = msg # Copy of internal message buffer
275
+
276
+ # Create raw data array
277
+ @raw_data = []
278
+ @raw_data << META_EVENT
279
+ @raw_data << type
280
+ @raw_data << @raw_var_num_data
281
+ @raw_data << m
282
+ @raw_data.flatten!
283
+
284
+ case type
285
+ when META_SEQ_NUM
286
+ sequence_number((m[0] << 8) + m[1])
287
+ when META_TEXT, META_COPYRIGHT, META_SEQ_NAME, META_INSTRUMENT,
288
+ META_LYRIC, META_MARKER, META_CUE, 0x08, 0x09, 0x0a,
289
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
290
+ text(type, m)
291
+ when META_TRACK_END
292
+ eot
293
+ when META_SET_TEMPO
294
+ tempo((m[0] << 16) + (m[1] << 8) + m[2])
295
+ when META_SMPTE
296
+ smpte(m[0], m[1], m[2], m[3], m[4])
297
+ when META_TIME_SIG
298
+ time_signature(m[0], m[1], m[2], m[3])
299
+ when META_KEY_SIG
300
+ key_signature(m[0], !(m[1] == 0))
301
+ when META_SEQ_SPECIF
302
+ sequencer_specific(type, m)
303
+ else
304
+ meta_misc(type, m)
305
+ end
306
+ end
307
+
308
+ # Handle a channel message (note on, note off, etc.)
309
+ def chan_message(running, status, c1, c2)
310
+ @raw_data = []
311
+ @raw_data << status unless running
312
+ @raw_data << c1
313
+ @raw_data << c2
314
+
315
+ chan = status & 0x0f
316
+
317
+ case (status & 0xf0)
318
+ when NOTE_OFF
319
+ note_off(chan, c1, c2)
320
+ when NOTE_ON
321
+ note_on(chan, c1, c2)
322
+ when POLY_PRESSURE
323
+ pressure(chan, c1, c2)
324
+ when CONTROLLER
325
+ controller(chan, c1, c2)
326
+ when PITCH_BEND
327
+ pitch_bend(chan, c1, c2)
328
+ when PROGRAM_CHANGE
329
+ program(chan, c1)
330
+ when CHANNEL_PRESSURE
331
+ chan_pressure(chan, c1)
332
+ else
333
+ error("illegal chan message 0x#{format('%02x', (status & 0xf0))}\n")
334
+ end
335
+ end
336
+
337
+ # Copy message into raw data array, then call sysex().
338
+ def handle_sysex(msg)
339
+ @raw_data = msg.dup
340
+ sysex(msg)
341
+ end
342
+
343
+ # Copy message into raw data array, then call arbitrary().
344
+ def handle_arbitrary(msg)
345
+ @raw_data = msg.dup
346
+ arbitrary(msg)
347
+ end
348
+
349
+ # Read and return a sixteen bit value.
350
+ def read16
351
+ val = (getc << 8) + getc
352
+ val = -(val & 0x7fff) if (val & 0x8000).nonzero?
353
+ val
354
+ end
355
+
356
+ # Read and return a 32-bit value.
357
+ def read32
358
+ val = (getc << 24) + (getc << 16) + (getc << 8) +
359
+ getc
360
+ val = -(val & 0x7fffffff) if (val & 0x80000000).nonzero?
361
+ val
362
+ end
363
+
364
+ # Read a varlen value.
365
+ def read_var_len
366
+ @raw_var_num_data = []
367
+ c = getc
368
+ @raw_var_num_data << c
369
+ val = c
370
+ if (val & 0x80).nonzero?
371
+ val &= 0x7f
372
+ while true
373
+ c = getc
374
+ @raw_var_num_data << c
375
+ val = (val << 7) + (c & 0x7f)
376
+ break if (c & 0x80).zero?
377
+ end
378
+ end
379
+ val
380
+ end
381
+
382
+ # Write a sixteen-bit value.
383
+ def write16(val)
384
+ val = (-val) | 0x8000 if val < 0
385
+ putc((val >> 8) & 0xff)
386
+ putc(val & 0xff)
387
+ end
388
+
389
+ # Write a 32-bit value.
390
+ def write32(val)
391
+ val = (-val) | 0x80000000 if val < 0
392
+ putc((val >> 24) & 0xff)
393
+ putc((val >> 16) & 0xff)
394
+ putc((val >> 8) & 0xff)
395
+ putc(val & 0xff)
396
+ end
397
+
398
+ # Write a variable length value.
399
+ def write_var_len(val)
400
+ if val.zero?
401
+ putc(0)
402
+ return
403
+ end
404
+
405
+ buf = []
406
+
407
+ buf << (val & 0x7f)
408
+ while (value >>= 7) > 0
409
+ buf << (val & 0x7f) | 0x80
410
+ end
411
+
412
+ buf.reverse.each { |b| putc(b) }
413
+ end
414
+
415
+ # Add a byte to the current message buffer.
416
+ def msg_add(c)
417
+ @msg_buf << c
418
+ end
419
+
420
+ # Read and add a number of bytes to the message buffer. Return
421
+ # the last byte (so we can see if it's an EOX or not).
422
+ def msg_read(n_bytes)
423
+ @msg_buf += get_bytes(n_bytes)
424
+ @msg_buf.flatten!
425
+ @msg_buf[-1]
426
+ end
427
+
428
+ # Initialize the internal message buffer.
429
+ def msg_init
430
+ @msg_buf = []
431
+ end
432
+
433
+ # Return a copy of the internal message buffer.
434
+ def msg
435
+ @msg_buf.dup
436
+ end
388
437
  end
389
-
390
- # Read a varlen value.
391
- def read_var_len
392
- @raw_var_num_data = []
393
- c = getc()
394
- @raw_var_num_data << c
395
- val = c
396
- if (val & 0x80).nonzero?
397
- val &= 0x7f
398
- while true
399
- c = getc()
400
- @raw_var_num_data << c
401
- val = (val << 7) + (c & 0x7f)
402
- break if (c & 0x80).zero?
403
- end
404
- end
405
- return val
406
- end
407
-
408
- # Write a sixteen-bit value.
409
- def write16(val)
410
- val = (-val) | 0x8000 if val < 0
411
- putc((val >> 8) & 0xff)
412
- putc(val & 0xff)
413
- end
414
-
415
- # Write a 32-bit value.
416
- def write32(val)
417
- val = (-val) | 0x80000000 if val < 0
418
- putc((val >> 24) & 0xff)
419
- putc((val >> 16) & 0xff)
420
- putc((val >> 8) & 0xff)
421
- putc(val & 0xff)
422
- end
423
-
424
- # Write a variable length value.
425
- def write_var_len(val)
426
- if val.zero?
427
- putc(0)
428
- return
429
- end
430
-
431
- buf = []
432
-
433
- buf << (val & 0x7f)
434
- while (value >>= 7) > 0
435
- buf << (val & 0x7f) | 0x80
436
- end
437
-
438
- buf.reverse.each { | b | putc(b) }
439
- end
440
-
441
- # Add a byte to the current message buffer.
442
- def msg_add(c)
443
- @msg_buf << c
444
- end
445
-
446
- # Read and add a number of bytes to the message buffer. Return
447
- # the last byte (so we can see if it's an EOX or not).
448
- def msg_read(n_bytes)
449
- @msg_buf += get_bytes(n_bytes)
450
- @msg_buf.flatten!
451
- return @msg_buf[-1]
452
- end
453
-
454
- # Initialize the internal message buffer.
455
- def msg_init
456
- @msg_buf = []
457
- end
458
-
459
- # Return a copy of the internal message buffer.
460
- def msg
461
- return @msg_buf.dup()
462
- end
463
-
464
- end
465
-
466
- end
438
+ end
467
439
  end