wavefile 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +21 -23
  3. data/README.markdown +27 -43
  4. data/lib/wavefile.rb +4 -1
  5. data/lib/wavefile/buffer.rb +7 -5
  6. data/lib/wavefile/chunk_readers.rb +1 -2
  7. data/lib/wavefile/chunk_readers/base_chunk_reader.rb +13 -0
  8. data/lib/wavefile/chunk_readers/data_chunk_reader.rb +12 -3
  9. data/lib/wavefile/chunk_readers/format_chunk_reader.rb +2 -10
  10. data/lib/wavefile/chunk_readers/riff_reader.rb +73 -26
  11. data/lib/wavefile/chunk_readers/sample_chunk_reader.rb +74 -0
  12. data/lib/wavefile/format.rb +13 -9
  13. data/lib/wavefile/reader.rb +15 -7
  14. data/lib/wavefile/sampler_info.rb +162 -0
  15. data/lib/wavefile/sampler_loop.rb +142 -0
  16. data/lib/wavefile/smpte_timecode.rb +61 -0
  17. data/lib/wavefile/writer.rb +7 -1
  18. data/test/fixtures/wave/invalid/data_chunk_ends_after_chunk_id.wav +0 -0
  19. data/test/fixtures/wave/invalid/data_chunk_has_incomplete_chunk_size.wav +0 -0
  20. data/test/fixtures/wave/invalid/data_chunk_truncated.wav +0 -0
  21. data/test/fixtures/wave/invalid/format_chunk_after_data_chunk.wav +0 -0
  22. data/test/fixtures/wave/invalid/incomplete_riff_format.wav +0 -0
  23. data/test/fixtures/wave/invalid/incomplete_riff_header.wav +1 -1
  24. data/test/fixtures/wave/invalid/no_format_chunk_size.wav +0 -0
  25. data/test/fixtures/wave/invalid/no_riff_format.wav +0 -0
  26. data/test/fixtures/wave/invalid/riff_chunk_has_incomplete_chunk_size.wav +1 -0
  27. data/test/fixtures/wave/invalid/smpl_chunk_empty.wav +0 -0
  28. data/test/fixtures/wave/invalid/smpl_chunk_fields_out_of_range.wav +0 -0
  29. data/test/fixtures/wave/invalid/smpl_chunk_loop_count_too_high.wav +0 -0
  30. data/test/fixtures/wave/invalid/smpl_chunk_truncated_sampler_specific_data.wav +0 -0
  31. data/test/fixtures/wave/invalid/truncated_smpl_chunk.wav +0 -0
  32. data/test/fixtures/wave/unsupported/bad_audio_format.wav +0 -0
  33. data/test/fixtures/wave/unsupported/bad_sample_rate.wav +0 -0
  34. data/test/fixtures/wave/unsupported/extensible_unsupported_subformat_guid.wav +0 -0
  35. data/test/fixtures/wave/unsupported/unsupported_audio_format.wav +0 -0
  36. data/test/fixtures/wave/unsupported/unsupported_bits_per_sample.wav +0 -0
  37. data/test/fixtures/wave/valid/valid_mono_pcm_16_44100_junk_chunk_final_chunk_missing_padding_byte.wav +0 -0
  38. data/test/fixtures/wave/valid/valid_mono_pcm_16_44100_junk_chunk_with_padding_byte.wav +0 -0
  39. data/test/fixtures/wave/valid/valid_mono_pcm_8_44100_with_padding_byte.wav +0 -0
  40. data/test/fixtures/wave/valid/valid_with_sample_chunk_after_data_chunk.wav +0 -0
  41. data/test/fixtures/wave/valid/valid_with_sample_chunk_after_data_chunk_and_data_chunk_has_padding_byte.wav +0 -0
  42. data/test/fixtures/wave/valid/valid_with_sample_chunk_before_data_chunk.wav +0 -0
  43. data/test/fixtures/wave/valid/valid_with_sample_chunk_no_loops.wav +0 -0
  44. data/test/fixtures/wave/valid/valid_with_sample_chunk_with_extra_unused_bytes.wav +0 -0
  45. data/test/fixtures/wave/valid/valid_with_sample_chunk_with_sampler_specific_data.wav +0 -0
  46. data/test/format_test.rb +4 -4
  47. data/test/reader_test.rb +266 -8
  48. data/test/sampler_info_test.rb +314 -0
  49. data/test/sampler_loop_test.rb +215 -0
  50. data/test/smpte_timecode_test.rb +103 -0
  51. data/test/writer_test.rb +1 -1
  52. metadata +30 -6
  53. data/lib/wavefile/chunk_readers/generic_chunk_reader.rb +0 -15
  54. data/lib/wavefile/chunk_readers/riff_chunk_reader.rb +0 -19
@@ -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 += [CANONICAL_HEADER_BYTE_LENGTH[format_code] + sample_data_byte_count].pack(UNSIGNED_INT_32)
299
+ header += [riff_chunk_size].pack(UNSIGNED_INT_32)
294
300
  header += WAVEFILE_FORMAT_CODE
295
301
 
296
302
  # Write the format chunk
@@ -1 +1 @@
1
- RIFF
1
+ RIFF
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, 4294967296].each do |valid_sample_rate|
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, 4294967297, 44100.5, 44100.0].each do |invalid_sample_rate|
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(2240, reader.total_sample_frames)
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(2240, reader.total_sample_frames)
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, 191], buffers.map {|buffer| buffer.samples.length })
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] * 23) + [88, 88, 88, 88, 167, 167, 167],
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, 191], buffers.map {|buffer| buffer.samples.length })
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] * 23) + [88, 88, 88, 88, 167, 167, 167],
528
+ assert_equal((SQUARE_WAVE_CYCLE[:mono][:pcm_8] * 24) + [88],
464
529
  buffers[2].samples)
465
- assert_equal(2239, reader.current_sample_frame)
466
- assert_equal(2239, reader.total_sample_frames)
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)