wavefile 1.0.1 → 1.1.0
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 +4 -4
- data/LICENSE +21 -23
- data/README.markdown +27 -43
- data/lib/wavefile.rb +4 -1
- data/lib/wavefile/buffer.rb +7 -5
- data/lib/wavefile/chunk_readers.rb +1 -2
- data/lib/wavefile/chunk_readers/base_chunk_reader.rb +13 -0
- data/lib/wavefile/chunk_readers/data_chunk_reader.rb +12 -3
- data/lib/wavefile/chunk_readers/format_chunk_reader.rb +2 -10
- data/lib/wavefile/chunk_readers/riff_reader.rb +73 -26
- data/lib/wavefile/chunk_readers/sample_chunk_reader.rb +74 -0
- data/lib/wavefile/format.rb +13 -9
- data/lib/wavefile/reader.rb +15 -7
- data/lib/wavefile/sampler_info.rb +162 -0
- data/lib/wavefile/sampler_loop.rb +142 -0
- data/lib/wavefile/smpte_timecode.rb +61 -0
- data/lib/wavefile/writer.rb +7 -1
- data/test/fixtures/wave/invalid/data_chunk_ends_after_chunk_id.wav +0 -0
- data/test/fixtures/wave/invalid/data_chunk_has_incomplete_chunk_size.wav +0 -0
- data/test/fixtures/wave/invalid/data_chunk_truncated.wav +0 -0
- data/test/fixtures/wave/invalid/format_chunk_after_data_chunk.wav +0 -0
- data/test/fixtures/wave/invalid/incomplete_riff_format.wav +0 -0
- data/test/fixtures/wave/invalid/incomplete_riff_header.wav +1 -1
- data/test/fixtures/wave/invalid/no_format_chunk_size.wav +0 -0
- data/test/fixtures/wave/invalid/no_riff_format.wav +0 -0
- data/test/fixtures/wave/invalid/riff_chunk_has_incomplete_chunk_size.wav +1 -0
- data/test/fixtures/wave/invalid/smpl_chunk_empty.wav +0 -0
- data/test/fixtures/wave/invalid/smpl_chunk_fields_out_of_range.wav +0 -0
- data/test/fixtures/wave/invalid/smpl_chunk_loop_count_too_high.wav +0 -0
- data/test/fixtures/wave/invalid/smpl_chunk_truncated_sampler_specific_data.wav +0 -0
- data/test/fixtures/wave/invalid/truncated_smpl_chunk.wav +0 -0
- data/test/fixtures/wave/unsupported/bad_audio_format.wav +0 -0
- data/test/fixtures/wave/unsupported/bad_sample_rate.wav +0 -0
- data/test/fixtures/wave/unsupported/extensible_unsupported_subformat_guid.wav +0 -0
- data/test/fixtures/wave/unsupported/unsupported_audio_format.wav +0 -0
- data/test/fixtures/wave/unsupported/unsupported_bits_per_sample.wav +0 -0
- data/test/fixtures/wave/valid/valid_mono_pcm_16_44100_junk_chunk_final_chunk_missing_padding_byte.wav +0 -0
- data/test/fixtures/wave/valid/valid_mono_pcm_16_44100_junk_chunk_with_padding_byte.wav +0 -0
- data/test/fixtures/wave/valid/valid_mono_pcm_8_44100_with_padding_byte.wav +0 -0
- data/test/fixtures/wave/valid/valid_with_sample_chunk_after_data_chunk.wav +0 -0
- data/test/fixtures/wave/valid/valid_with_sample_chunk_after_data_chunk_and_data_chunk_has_padding_byte.wav +0 -0
- data/test/fixtures/wave/valid/valid_with_sample_chunk_before_data_chunk.wav +0 -0
- data/test/fixtures/wave/valid/valid_with_sample_chunk_no_loops.wav +0 -0
- data/test/fixtures/wave/valid/valid_with_sample_chunk_with_extra_unused_bytes.wav +0 -0
- data/test/fixtures/wave/valid/valid_with_sample_chunk_with_sampler_specific_data.wav +0 -0
- data/test/format_test.rb +4 -4
- data/test/reader_test.rb +266 -8
- data/test/sampler_info_test.rb +314 -0
- data/test/sampler_loop_test.rb +215 -0
- data/test/smpte_timecode_test.rb +103 -0
- data/test/writer_test.rb +1 -1
- metadata +30 -6
- data/lib/wavefile/chunk_readers/generic_chunk_reader.rb +0 -15
- data/lib/wavefile/chunk_readers/riff_chunk_reader.rb +0 -19
data/lib/wavefile/writer.rb
CHANGED
@@ -93,7 +93,9 @@ module WaveFile
|
|
93
93
|
# writer.write(buffer)
|
94
94
|
#
|
95
95
|
# Returns the number of sample frames that have been written to the file so far.
|
96
|
+
#
|
96
97
|
# Raises WriterClosedError if the Writer has been closed.
|
98
|
+
#
|
97
99
|
# Raises BufferConversionError if the Buffer can't be converted to the Writer's format.
|
98
100
|
def write(buffer)
|
99
101
|
if @closed
|
@@ -287,10 +289,14 @@ module WaveFile
|
|
287
289
|
requires_fact_chunk = (format_code != :pcm)
|
288
290
|
|
289
291
|
sample_data_byte_count = sample_frame_count * @format.block_align
|
292
|
+
riff_chunk_size = CANONICAL_HEADER_BYTE_LENGTH[format_code] + sample_data_byte_count
|
293
|
+
if sample_data_byte_count.odd?
|
294
|
+
riff_chunk_size += 1
|
295
|
+
end
|
290
296
|
|
291
297
|
# Write the header for the RIFF chunk
|
292
298
|
header = CHUNK_IDS[:riff]
|
293
|
-
header += [
|
299
|
+
header += [riff_chunk_size].pack(UNSIGNED_INT_32)
|
294
300
|
header += WAVEFILE_FORMAT_CODE
|
295
301
|
|
296
302
|
# Write the format chunk
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -1 +1 @@
|
|
1
|
-
RIFF
|
1
|
+
RIFF
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
RIFFaa
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/test/format_test.rb
CHANGED
@@ -14,7 +14,7 @@ class FormatTest < Minitest::Test
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def test_invalid_channels
|
17
|
-
["dsfsfsdf", :foo, 0, -1, 65536, 2.5, 2.0].each do |invalid_channels|
|
17
|
+
["dsfsfsdf", "1", :foo, 0, -1, 65536, 2.5, 2.0].each do |invalid_channels|
|
18
18
|
assert_raises(InvalidFormatError) { Format.new(invalid_channels, :pcm_16, 44100) }
|
19
19
|
end
|
20
20
|
end
|
@@ -30,7 +30,7 @@ class FormatTest < Minitest::Test
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def test_invalid_sample_format
|
33
|
-
["dsfsfsdf", :foo, :pcm, 0, 12, :pcm_14, :pcm_abc, :float_40].each do |invalid_sample_format|
|
33
|
+
["dsfsfsdf", "pcm_16", :foo, :pcm, 0, 12, :pcm_14, :pcm_abc, :float_40].each do |invalid_sample_format|
|
34
34
|
assert_raises(InvalidFormatError) { Format.new(:mono, invalid_sample_format, 44100) }
|
35
35
|
end
|
36
36
|
end
|
@@ -46,13 +46,13 @@ class FormatTest < Minitest::Test
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def test_valid_sample_rate
|
49
|
-
[1, 44100,
|
49
|
+
[1, 44100, 4294967295].each do |valid_sample_rate|
|
50
50
|
assert_equal(valid_sample_rate, Format.new(:mono, :pcm_16, valid_sample_rate).sample_rate)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
54
|
def test_invalid_sample_rate
|
55
|
-
["dsfsfsdf", :foo, 0, -1,
|
55
|
+
["dsfsfsdf", "44100", :foo, 0, -1, 4294967296, 44100.5, 44100.0].each do |invalid_sample_rate|
|
56
56
|
assert_raises(InvalidFormatError) { Format.new(:mono, :pcm_16, invalid_sample_rate) }
|
57
57
|
end
|
58
58
|
end
|
data/test/reader_test.rb
CHANGED
@@ -22,15 +22,27 @@ class ReaderTest < Minitest::Test
|
|
22
22
|
# File consists of "RIFF" and nothing else
|
23
23
|
"invalid/incomplete_riff_header.wav",
|
24
24
|
|
25
|
+
# The RIFF header chunk size field ends prematurely
|
26
|
+
"invalid/riff_chunk_has_incomplete_chunk_size.wav",
|
27
|
+
|
25
28
|
# First 4 bytes in the file are not "RIFF"
|
26
29
|
"invalid/bad_riff_header.wav",
|
27
30
|
|
31
|
+
# The format code in the RIFF header is missing
|
32
|
+
"invalid/no_riff_format.wav",
|
33
|
+
|
34
|
+
# The format code in the RIFF header is truncated; i.e. not a full 4 bytes
|
35
|
+
"invalid/incomplete_riff_format.wav",
|
36
|
+
|
28
37
|
# The format code in the RIFF header is not "WAVE"
|
29
38
|
"invalid/bad_wavefile_format.wav",
|
30
39
|
|
31
40
|
# The file consists of just a valid RIFF header
|
32
41
|
"invalid/no_format_chunk.wav",
|
33
42
|
|
43
|
+
# The format chunk only includes the chunk ID
|
44
|
+
"invalid/no_format_chunk_size.wav",
|
45
|
+
|
34
46
|
# The format chunk has 0 bytes in it (despite the chunk size being 16)
|
35
47
|
"invalid/empty_format_chunk.wav",
|
36
48
|
|
@@ -39,6 +51,27 @@ class ReaderTest < Minitest::Test
|
|
39
51
|
|
40
52
|
# The RIFF header and format chunk are OK, but there is no data chunk
|
41
53
|
"invalid/no_data_chunk.wav",
|
54
|
+
|
55
|
+
# The data chunk only contains the chunk ID, nothing else
|
56
|
+
"invalid/data_chunk_ends_after_chunk_id.wav",
|
57
|
+
|
58
|
+
# The data chunk size field ends prematurely
|
59
|
+
"invalid/data_chunk_has_incomplete_chunk_size.wav",
|
60
|
+
|
61
|
+
# The format chunk comes after the data chunk; it must come before
|
62
|
+
"invalid/format_chunk_after_data_chunk.wav",
|
63
|
+
|
64
|
+
# Contains a `smpl` chunk that has a size of 0 and no data
|
65
|
+
"invalid/smpl_chunk_empty.wav",
|
66
|
+
|
67
|
+
# Contains a `smpl` chunk that doesn't have enough bytes to match the chunk's size
|
68
|
+
"invalid/truncated_smpl_chunk.wav",
|
69
|
+
|
70
|
+
# `smpl` chunk does not contain as many loops as the 'loop count' field indicates
|
71
|
+
"invalid/smpl_chunk_loop_count_too_high.wav",
|
72
|
+
|
73
|
+
# `smpl` chunk does not contain as bytes as 'sampler specific data size' field indicates
|
74
|
+
"invalid/smpl_chunk_truncated_sampler_specific_data.wav",
|
42
75
|
]
|
43
76
|
|
44
77
|
invalid_fixtures.each do |fixture_name|
|
@@ -66,8 +99,9 @@ class ReaderTest < Minitest::Test
|
|
66
99
|
assert_nil(reader.format.speaker_mapping)
|
67
100
|
assert_equal(false, reader.closed?)
|
68
101
|
assert_equal(0, reader.current_sample_frame)
|
69
|
-
assert_equal(
|
102
|
+
assert_equal(0, reader.total_sample_frames)
|
70
103
|
assert_equal(false, reader.readable_format?)
|
104
|
+
assert_nil(reader.sampler_info)
|
71
105
|
reader.close
|
72
106
|
end
|
73
107
|
|
@@ -84,8 +118,9 @@ class ReaderTest < Minitest::Test
|
|
84
118
|
assert_equal([:front_center], reader.format.speaker_mapping)
|
85
119
|
assert_equal(false, reader.closed?)
|
86
120
|
assert_equal(0, reader.current_sample_frame)
|
87
|
-
assert_equal(
|
121
|
+
assert_equal(0, reader.total_sample_frames)
|
88
122
|
assert_equal(false, reader.readable_format?)
|
123
|
+
assert_nil(reader.sampler_info)
|
89
124
|
reader.close
|
90
125
|
end
|
91
126
|
end
|
@@ -146,6 +181,7 @@ class ReaderTest < Minitest::Test
|
|
146
181
|
assert_equal(0, reader.current_sample_frame)
|
147
182
|
assert_equal(2240, reader.total_sample_frames)
|
148
183
|
assert_equal(true, reader.readable_format?)
|
184
|
+
assert_nil(reader.sampler_info)
|
149
185
|
reader.close
|
150
186
|
end
|
151
187
|
|
@@ -162,6 +198,7 @@ class ReaderTest < Minitest::Test
|
|
162
198
|
assert_equal(0, reader.current_sample_frame)
|
163
199
|
assert_equal(2240, reader.total_sample_frames)
|
164
200
|
assert_equal(true, reader.readable_format?)
|
201
|
+
assert_nil(reader.sampler_info)
|
165
202
|
reader.close
|
166
203
|
end
|
167
204
|
|
@@ -178,6 +215,7 @@ class ReaderTest < Minitest::Test
|
|
178
215
|
assert_equal(1024, reader.current_sample_frame)
|
179
216
|
assert_equal(2240, reader.total_sample_frames)
|
180
217
|
assert_equal(true, reader.readable_format?)
|
218
|
+
assert_nil(reader.sampler_info)
|
181
219
|
end
|
182
220
|
end
|
183
221
|
end
|
@@ -211,6 +249,7 @@ class ReaderTest < Minitest::Test
|
|
211
249
|
assert_equal(0, reader.current_sample_frame)
|
212
250
|
assert_equal(2240, reader.total_sample_frames)
|
213
251
|
assert_equal(true, reader.readable_format?)
|
252
|
+
assert_nil(reader.sampler_info)
|
214
253
|
reader.close
|
215
254
|
|
216
255
|
buffers = read_file("valid/valid_extensible_stereo_pcm_16_44100.wav", 1024)
|
@@ -237,6 +276,7 @@ class ReaderTest < Minitest::Test
|
|
237
276
|
assert_equal(0, reader.current_sample_frame)
|
238
277
|
assert_equal(2240, reader.total_sample_frames)
|
239
278
|
assert_equal(true, reader.readable_format?)
|
279
|
+
assert_nil(reader.sampler_info)
|
240
280
|
reader.close
|
241
281
|
|
242
282
|
buffers = read_file("valid/valid_extensible_stereo_pcm_24_44100_no_speaker_mapping.wav", 1024)
|
@@ -263,6 +303,7 @@ class ReaderTest < Minitest::Test
|
|
263
303
|
assert_equal(0, reader.current_sample_frame)
|
264
304
|
assert_equal(2240, reader.total_sample_frames)
|
265
305
|
assert_equal(true, reader.readable_format?)
|
306
|
+
assert_nil(reader.sampler_info)
|
266
307
|
reader.close
|
267
308
|
|
268
309
|
buffers = read_file("valid/valid_extensible_stereo_pcm_16_44100_more_speakers_than_channels.wav", 1024)
|
@@ -307,6 +348,7 @@ class ReaderTest < Minitest::Test
|
|
307
348
|
assert_equal(0, reader.current_sample_frame)
|
308
349
|
assert_equal(2240, reader.total_sample_frames)
|
309
350
|
assert_equal(true, reader.readable_format?)
|
351
|
+
assert_nil(reader.sampler_info)
|
310
352
|
reader.close
|
311
353
|
|
312
354
|
buffers = read_file("valid/valid_extensible_stereo_pcm_16_44100_more_speakers_than_defined_by_spec.wav", 1024)
|
@@ -333,6 +375,7 @@ class ReaderTest < Minitest::Test
|
|
333
375
|
assert_equal(0, reader.current_sample_frame)
|
334
376
|
assert_equal(2240, reader.total_sample_frames)
|
335
377
|
assert_equal(true, reader.readable_format?)
|
378
|
+
assert_nil(reader.sampler_info)
|
336
379
|
reader.close
|
337
380
|
|
338
381
|
buffers = read_file("valid/valid_extensible_stereo_pcm_16_44100_only_undefined_high_bit_speakers.wav", 1024)
|
@@ -362,6 +405,7 @@ class ReaderTest < Minitest::Test
|
|
362
405
|
assert_equal(0, reader.current_sample_frame)
|
363
406
|
assert_equal(2240, reader.total_sample_frames)
|
364
407
|
assert_equal(true, reader.readable_format?)
|
408
|
+
assert_nil(reader.sampler_info)
|
365
409
|
reader.close
|
366
410
|
|
367
411
|
buffers = read_file("valid/valid_mono_pcm_16_44100_with_extension.wav", 1024)
|
@@ -387,13 +431,34 @@ class ReaderTest < Minitest::Test
|
|
387
431
|
buffers = read_file("valid/valid_mono_pcm_8_44100_with_padding_byte.wav", 1024)
|
388
432
|
|
389
433
|
assert_equal(3, buffers.length)
|
390
|
-
assert_equal([1024, 1024,
|
434
|
+
assert_equal([1024, 1024, 193], buffers.map {|buffer| buffer.samples.length })
|
391
435
|
assert_equal(SQUARE_WAVE_CYCLE[:mono][:pcm_8] * 128, buffers[0].samples)
|
392
436
|
assert_equal(SQUARE_WAVE_CYCLE[:mono][:pcm_8] * 128, buffers[1].samples)
|
393
|
-
assert_equal((SQUARE_WAVE_CYCLE[:mono][:pcm_8] *
|
437
|
+
assert_equal((SQUARE_WAVE_CYCLE[:mono][:pcm_8] * 24) + [88],
|
394
438
|
buffers[2].samples)
|
395
439
|
end
|
396
440
|
|
441
|
+
def test_read_truncated_file
|
442
|
+
reader = Reader.new(fixture("invalid/data_chunk_truncated.wav"), Format.new(:mono, :pcm_8, 44100))
|
443
|
+
|
444
|
+
# The chunk does not actually contain this many sample frames, it actually has 2240
|
445
|
+
assert_equal(100000, reader.total_sample_frames)
|
446
|
+
assert_equal(0, reader.current_sample_frame)
|
447
|
+
|
448
|
+
# First set of requested sample frames should be read correctly
|
449
|
+
buffer = reader.read(2000)
|
450
|
+
assert_equal(2000, buffer.samples.length)
|
451
|
+
assert_equal(2000, reader.current_sample_frame)
|
452
|
+
|
453
|
+
# All of the remaining sample frames are returned, which is fewer than were requested.
|
454
|
+
buffer = reader.read(2000)
|
455
|
+
assert_equal(240, buffer.samples.length)
|
456
|
+
assert_equal(2240, reader.current_sample_frame)
|
457
|
+
|
458
|
+
# Since there are no more sample frames, an end-of-file error should be raised
|
459
|
+
assert_raises(EOFError) { reader.read(2000) }
|
460
|
+
end
|
461
|
+
|
397
462
|
def test_each_buffer_no_block_given
|
398
463
|
reader = Reader.new(fixture("valid/valid_mono_pcm_16_44100.wav"))
|
399
464
|
assert_raises(LocalJumpError) { reader.each_buffer(1024) }
|
@@ -457,13 +522,13 @@ class ReaderTest < Minitest::Test
|
|
457
522
|
reader.each_buffer(1024) {|buffer| buffers << buffer }
|
458
523
|
|
459
524
|
assert_equal(3, buffers.length)
|
460
|
-
assert_equal([1024, 1024,
|
525
|
+
assert_equal([1024, 1024, 193], buffers.map {|buffer| buffer.samples.length })
|
461
526
|
assert_equal(SQUARE_WAVE_CYCLE[:mono][:pcm_8] * 128, buffers[0].samples)
|
462
527
|
assert_equal(SQUARE_WAVE_CYCLE[:mono][:pcm_8] * 128, buffers[1].samples)
|
463
|
-
assert_equal((SQUARE_WAVE_CYCLE[:mono][:pcm_8] *
|
528
|
+
assert_equal((SQUARE_WAVE_CYCLE[:mono][:pcm_8] * 24) + [88],
|
464
529
|
buffers[2].samples)
|
465
|
-
assert_equal(
|
466
|
-
assert_equal(
|
530
|
+
assert_equal(2241, reader.current_sample_frame)
|
531
|
+
assert_equal(2241, reader.total_sample_frames)
|
467
532
|
end
|
468
533
|
|
469
534
|
def test_each_buffer_inside_reader_block
|
@@ -492,6 +557,17 @@ class ReaderTest < Minitest::Test
|
|
492
557
|
assert_equal(2240, reader.total_sample_frames)
|
493
558
|
end
|
494
559
|
|
560
|
+
def test_read_non_data_chunk_is_final_chunk_without_padding_byte
|
561
|
+
# This fixture file contains a JUNK chunk with an odd size, but no padding byte. When a chunk
|
562
|
+
# is the final chunk in the file, a missing padding byte won't cause an error as long as the
|
563
|
+
# RIFF chunk size field matches the actual number of bytes in the file.
|
564
|
+
reader = Reader.new(fixture("valid/valid_mono_pcm_16_44100_junk_chunk_final_chunk_missing_padding_byte.wav"))
|
565
|
+
buffer = reader.read(1024)
|
566
|
+
assert_equal(buffer.samples, SQUARE_WAVE_CYCLE[:mono][:pcm_16] * 128)
|
567
|
+
assert_equal(1024, reader.current_sample_frame)
|
568
|
+
assert_equal(2240, reader.total_sample_frames)
|
569
|
+
end
|
570
|
+
|
495
571
|
def test_closed?
|
496
572
|
reader = Reader.new(fixture("valid/valid_mono_pcm_16_44100.wav"))
|
497
573
|
assert_equal(false, reader.closed?)
|
@@ -600,6 +676,188 @@ class ReaderTest < Minitest::Test
|
|
600
676
|
end
|
601
677
|
end
|
602
678
|
|
679
|
+
def test_smpl_chunk
|
680
|
+
file_name = fixture("valid/valid_with_sample_chunk_before_data_chunk.wav")
|
681
|
+
sampler_info = Reader.new(file_name).sampler_info
|
682
|
+
|
683
|
+
assert_equal(0, sampler_info.manufacturer_id)
|
684
|
+
assert_equal(0, sampler_info.product_id)
|
685
|
+
assert_equal(0, sampler_info.sample_nanoseconds)
|
686
|
+
assert_equal(60, sampler_info.midi_note)
|
687
|
+
assert_equal(50.0, sampler_info.fine_tuning_cents)
|
688
|
+
assert_equal(0, sampler_info.smpte_format)
|
689
|
+
assert_equal(0, sampler_info.smpte_offset.hours)
|
690
|
+
assert_equal(0, sampler_info.smpte_offset.minutes)
|
691
|
+
assert_equal(0, sampler_info.smpte_offset.seconds)
|
692
|
+
assert_equal(0, sampler_info.smpte_offset.frames)
|
693
|
+
assert_equal(1, sampler_info.loops.length)
|
694
|
+
assert_equal(0, sampler_info.loops[0].id)
|
695
|
+
assert_equal(:backward, sampler_info.loops[0].type)
|
696
|
+
assert_equal(0, sampler_info.loops[0].start_sample_frame)
|
697
|
+
assert_equal(0, sampler_info.loops[0].end_sample_frame)
|
698
|
+
assert_equal(0.5, sampler_info.loops[0].fraction)
|
699
|
+
assert_equal(1, sampler_info.loops[0].play_count)
|
700
|
+
assert_equal("", sampler_info.sampler_specific_data)
|
701
|
+
end
|
702
|
+
|
703
|
+
# Several field values are out of the expected range, but the file should be successfully
|
704
|
+
# read anyway because the sample chunk has the correct structure
|
705
|
+
def test_smpl_chunk_field_values_out_of_range
|
706
|
+
file_name = fixture("invalid/smpl_chunk_fields_out_of_range.wav")
|
707
|
+
sampler_info = Reader.new(file_name).sampler_info
|
708
|
+
|
709
|
+
assert_equal(0, sampler_info.manufacturer_id)
|
710
|
+
assert_equal(0, sampler_info.product_id)
|
711
|
+
assert_equal(0, sampler_info.sample_nanoseconds)
|
712
|
+
assert_equal(10000, sampler_info.midi_note)
|
713
|
+
assert_equal(50.0, sampler_info.fine_tuning_cents)
|
714
|
+
assert_equal(99999, sampler_info.smpte_format)
|
715
|
+
assert_equal(-128, sampler_info.smpte_offset.hours)
|
716
|
+
assert_equal(128, sampler_info.smpte_offset.minutes)
|
717
|
+
assert_equal(8, sampler_info.smpte_offset.seconds)
|
718
|
+
assert_equal(1, sampler_info.smpte_offset.frames)
|
719
|
+
assert_equal(1, sampler_info.loops.length)
|
720
|
+
assert_equal(0, sampler_info.loops[0].id)
|
721
|
+
assert_equal(88888, sampler_info.loops[0].type)
|
722
|
+
assert_equal(9999999, sampler_info.loops[0].start_sample_frame)
|
723
|
+
assert_equal(9999999, sampler_info.loops[0].end_sample_frame)
|
724
|
+
assert_equal(0.5, sampler_info.loops[0].fraction)
|
725
|
+
assert_equal(1, sampler_info.loops[0].play_count)
|
726
|
+
assert_equal("", sampler_info.sampler_specific_data)
|
727
|
+
end
|
728
|
+
|
729
|
+
def test_smpl_chunk_after_data_chunk
|
730
|
+
file_name = fixture("valid/valid_with_sample_chunk_after_data_chunk.wav")
|
731
|
+
sampler_info = Reader.new(file_name).sampler_info
|
732
|
+
|
733
|
+
assert_equal(0, sampler_info.manufacturer_id)
|
734
|
+
assert_equal(0, sampler_info.product_id)
|
735
|
+
assert_equal(0, sampler_info.sample_nanoseconds)
|
736
|
+
assert_equal(60, sampler_info.midi_note)
|
737
|
+
assert_equal(50.0, sampler_info.fine_tuning_cents)
|
738
|
+
assert_equal(0, sampler_info.smpte_format)
|
739
|
+
assert_equal(0, sampler_info.smpte_offset.hours)
|
740
|
+
assert_equal(0, sampler_info.smpte_offset.minutes)
|
741
|
+
assert_equal(0, sampler_info.smpte_offset.seconds)
|
742
|
+
assert_equal(0, sampler_info.smpte_offset.frames)
|
743
|
+
assert_equal(1, sampler_info.loops.length)
|
744
|
+
assert_equal(0, sampler_info.loops[0].id)
|
745
|
+
assert_equal(:backward, sampler_info.loops[0].type)
|
746
|
+
assert_equal(0, sampler_info.loops[0].start_sample_frame)
|
747
|
+
assert_equal(0, sampler_info.loops[0].end_sample_frame)
|
748
|
+
assert_equal(0.5, sampler_info.loops[0].fraction)
|
749
|
+
assert_equal(1, sampler_info.loops[0].play_count)
|
750
|
+
assert_equal("", sampler_info.sampler_specific_data)
|
751
|
+
end
|
752
|
+
|
753
|
+
def test_smpl_chunk_after_data_chunk_and_data_chunk_has_padding_byte
|
754
|
+
file_name = fixture("valid/valid_with_sample_chunk_after_data_chunk_and_data_chunk_has_padding_byte.wav")
|
755
|
+
|
756
|
+
reader = Reader.new(file_name)
|
757
|
+
sampler_info = reader.sampler_info
|
758
|
+
|
759
|
+
# Should correctly deal with data chunk with odd number of bytes, followed
|
760
|
+
# by a padding byte.
|
761
|
+
assert_equal(2241, reader.total_sample_frames)
|
762
|
+
# Test that data chunk read is correctly queued up to start of data chunk
|
763
|
+
assert_equal([88, 88, 88, 88, 167, 167, 167, 167], reader.read(8).samples)
|
764
|
+
|
765
|
+
# Sample chunk should be correctly located despite the padding byte following
|
766
|
+
# the data chunk.
|
767
|
+
assert_equal(0, sampler_info.manufacturer_id)
|
768
|
+
assert_equal(0, sampler_info.product_id)
|
769
|
+
assert_equal(0, sampler_info.sample_nanoseconds)
|
770
|
+
assert_equal(60, sampler_info.midi_note)
|
771
|
+
assert_equal(50.0, sampler_info.fine_tuning_cents)
|
772
|
+
assert_equal(0, sampler_info.smpte_format)
|
773
|
+
assert_equal(0, sampler_info.smpte_offset.hours)
|
774
|
+
assert_equal(0, sampler_info.smpte_offset.minutes)
|
775
|
+
assert_equal(0, sampler_info.smpte_offset.seconds)
|
776
|
+
assert_equal(0, sampler_info.smpte_offset.frames)
|
777
|
+
assert_equal(1, sampler_info.loops.length)
|
778
|
+
assert_equal(0, sampler_info.loops[0].id)
|
779
|
+
assert_equal(:backward, sampler_info.loops[0].type)
|
780
|
+
assert_equal(0, sampler_info.loops[0].start_sample_frame)
|
781
|
+
assert_equal(0, sampler_info.loops[0].end_sample_frame)
|
782
|
+
assert_equal(0.5, sampler_info.loops[0].fraction)
|
783
|
+
assert_equal(1, sampler_info.loops[0].play_count)
|
784
|
+
assert_equal("", sampler_info.sampler_specific_data)
|
785
|
+
end
|
786
|
+
|
787
|
+
def test_smpl_chunk_with_sampler_specific_data
|
788
|
+
file_name = fixture("valid/valid_with_sample_chunk_with_sampler_specific_data.wav")
|
789
|
+
sampler_info = Reader.new(file_name).sampler_info
|
790
|
+
|
791
|
+
assert_equal(0, sampler_info.manufacturer_id)
|
792
|
+
assert_equal(0, sampler_info.product_id)
|
793
|
+
assert_equal(0, sampler_info.sample_nanoseconds)
|
794
|
+
assert_equal(60, sampler_info.midi_note)
|
795
|
+
assert_equal(50.0, sampler_info.fine_tuning_cents)
|
796
|
+
assert_equal(0, sampler_info.smpte_format)
|
797
|
+
assert_equal(0, sampler_info.smpte_offset.hours)
|
798
|
+
assert_equal(0, sampler_info.smpte_offset.minutes)
|
799
|
+
assert_equal(0, sampler_info.smpte_offset.seconds)
|
800
|
+
assert_equal(0, sampler_info.smpte_offset.frames)
|
801
|
+
assert_equal(1, sampler_info.loops.length)
|
802
|
+
assert_equal(0, sampler_info.loops[0].id)
|
803
|
+
assert_equal(:backward, sampler_info.loops[0].type)
|
804
|
+
assert_equal(0, sampler_info.loops[0].start_sample_frame)
|
805
|
+
assert_equal(0, sampler_info.loops[0].end_sample_frame)
|
806
|
+
assert_equal(0.5, sampler_info.loops[0].fraction)
|
807
|
+
assert_equal(Float::INFINITY, sampler_info.loops[0].play_count)
|
808
|
+
assert_equal("\x04\x01\x03\x02", sampler_info.sampler_specific_data)
|
809
|
+
assert_equal(Encoding::ASCII_8BIT, sampler_info.sampler_specific_data.encoding)
|
810
|
+
end
|
811
|
+
|
812
|
+
def test_smpl_chunk_with_extra_unused_bytes
|
813
|
+
file_name = fixture("valid/valid_with_sample_chunk_with_extra_unused_bytes.wav")
|
814
|
+
reader = Reader.new(file_name)
|
815
|
+
sampler_info = reader.sampler_info
|
816
|
+
|
817
|
+
assert_equal(0, sampler_info.manufacturer_id)
|
818
|
+
assert_equal(0, sampler_info.product_id)
|
819
|
+
assert_equal(0, sampler_info.sample_nanoseconds)
|
820
|
+
assert_equal(60, sampler_info.midi_note)
|
821
|
+
assert_equal(50.0, sampler_info.fine_tuning_cents)
|
822
|
+
assert_equal(0, sampler_info.smpte_format)
|
823
|
+
assert_equal(0, sampler_info.smpte_offset.hours)
|
824
|
+
assert_equal(0, sampler_info.smpte_offset.minutes)
|
825
|
+
assert_equal(0, sampler_info.smpte_offset.seconds)
|
826
|
+
assert_equal(0, sampler_info.smpte_offset.frames)
|
827
|
+
assert_equal(1, sampler_info.loops.length)
|
828
|
+
assert_equal(0, sampler_info.loops[0].id)
|
829
|
+
assert_equal(:backward, sampler_info.loops[0].type)
|
830
|
+
assert_equal(0, sampler_info.loops[0].start_sample_frame)
|
831
|
+
assert_equal(0, sampler_info.loops[0].end_sample_frame)
|
832
|
+
assert_equal(0.5, sampler_info.loops[0].fraction)
|
833
|
+
assert_equal(1, sampler_info.loops[0].play_count)
|
834
|
+
assert_equal("\x04\x01\x05", sampler_info.sampler_specific_data)
|
835
|
+
assert_equal(Encoding::ASCII_8BIT, sampler_info.sampler_specific_data.encoding)
|
836
|
+
|
837
|
+
# Data chunk should be queued correctly and not raise an error, despite extra bytes
|
838
|
+
# at end of `smpl` chunk.
|
839
|
+
buffer = reader.read(1)
|
840
|
+
assert_equal([[-10000, -10000]], buffer.samples)
|
841
|
+
end
|
842
|
+
|
843
|
+
def test_smpl_chunk_no_loops
|
844
|
+
file_name = fixture("valid/valid_with_sample_chunk_no_loops.wav")
|
845
|
+
sampler_info = Reader.new(file_name).sampler_info
|
846
|
+
|
847
|
+
assert_equal(0, sampler_info.manufacturer_id)
|
848
|
+
assert_equal(0, sampler_info.product_id)
|
849
|
+
assert_equal(0, sampler_info.sample_nanoseconds)
|
850
|
+
assert_equal(60, sampler_info.midi_note)
|
851
|
+
assert_equal(50.0, sampler_info.fine_tuning_cents)
|
852
|
+
assert_equal(0, sampler_info.smpte_format)
|
853
|
+
assert_equal(0, sampler_info.smpte_offset.hours)
|
854
|
+
assert_equal(0, sampler_info.smpte_offset.minutes)
|
855
|
+
assert_equal(0, sampler_info.smpte_offset.seconds)
|
856
|
+
assert_equal(0, sampler_info.smpte_offset.frames)
|
857
|
+
assert_equal([], sampler_info.loops)
|
858
|
+
assert_equal("", sampler_info.sampler_specific_data)
|
859
|
+
end
|
860
|
+
|
603
861
|
private
|
604
862
|
|
605
863
|
def read_file(file_name, buffer_size, format=nil)
|