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.
@@ -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