wavefile 0.8.1 → 1.0.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.
Files changed (117) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE +1 -1
  3. data/README.markdown +33 -28
  4. data/Rakefile +2 -2
  5. data/lib/wavefile.rb +5 -5
  6. data/lib/wavefile/chunk_readers/format_chunk_reader.rb +1 -1
  7. data/lib/wavefile/duration.rb +1 -1
  8. data/lib/wavefile/format.rb +120 -29
  9. data/lib/wavefile/reader.rb +18 -16
  10. data/lib/wavefile/unvalidated_format.rb +51 -3
  11. data/lib/wavefile/writer.rb +74 -21
  12. data/test/buffer_test.rb +13 -10
  13. data/test/chunk_readers/format_chunk_reader_test.rb +40 -40
  14. data/test/fixtures/{invalid → wave/invalid}/bad_riff_header.wav +0 -0
  15. data/test/fixtures/{invalid → wave/invalid}/bad_wavefile_format.wav +0 -0
  16. data/test/fixtures/{invalid → wave/invalid}/empty.wav +0 -0
  17. data/test/fixtures/{invalid → wave/invalid}/empty_format_chunk.wav +0 -0
  18. data/test/fixtures/{invalid → wave/invalid}/incomplete_riff_header.wav +0 -0
  19. data/test/fixtures/{invalid → wave/invalid}/insufficient_format_chunk.wav +0 -0
  20. data/test/fixtures/{invalid → wave/invalid}/no_data_chunk.wav +0 -0
  21. data/test/fixtures/{invalid → wave/invalid}/no_format_chunk.wav +0 -0
  22. data/test/fixtures/{unsupported → wave/unsupported}/bad_audio_format.wav +0 -0
  23. data/test/fixtures/{unsupported → wave/unsupported}/bad_channel_count.wav +0 -0
  24. data/test/fixtures/{unsupported → wave/unsupported}/bad_sample_rate.wav +0 -0
  25. data/test/fixtures/{unsupported → wave/unsupported}/extensible_container_size_bigger_than_sample_size.wav +0 -0
  26. data/test/fixtures/wave/unsupported/extensible_unsupported_subformat_guid.wav +0 -0
  27. data/test/fixtures/{unsupported → wave/unsupported}/unsupported_audio_format.wav +0 -0
  28. data/test/fixtures/{unsupported → wave/unsupported}/unsupported_bits_per_sample.wav +0 -0
  29. data/test/fixtures/{valid → wave/valid}/no_samples.wav +0 -0
  30. data/test/fixtures/wave/valid/valid_extensible_20_pcm_16_44100_speaker_mapping_overflow.wav +0 -0
  31. data/test/fixtures/{valid → wave/valid}/valid_extensible_mono_float_32_44100.wav +0 -0
  32. data/test/fixtures/{valid → wave/valid}/valid_extensible_mono_float_64_44100.wav +0 -0
  33. data/test/fixtures/wave/valid/valid_extensible_mono_pcm_16_44100.wav +0 -0
  34. data/test/fixtures/wave/valid/valid_extensible_mono_pcm_16_44100_non_default_speaker_mapping.wav +0 -0
  35. data/test/fixtures/wave/valid/valid_extensible_mono_pcm_24_44100.wav +0 -0
  36. data/test/fixtures/wave/valid/valid_extensible_mono_pcm_32_44100.wav +0 -0
  37. data/test/fixtures/wave/valid/valid_extensible_mono_pcm_8_44100.wav +0 -0
  38. data/test/fixtures/{valid → wave/valid}/valid_extensible_stereo_float_32_44100.wav +0 -0
  39. data/test/fixtures/{valid → wave/valid}/valid_extensible_stereo_float_64_44100.wav +0 -0
  40. data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_16_44100.wav +0 -0
  41. data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_16_44100_center_right_speakers.wav +0 -0
  42. data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_16_44100_more_speakers_than_channels.wav +0 -0
  43. data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_16_44100_more_speakers_than_defined_by_spec.wav +0 -0
  44. data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_16_44100_only_undefined_high_bit_speakers.wav +0 -0
  45. data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_24_44100.wav +0 -0
  46. data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_24_44100_incomplete_speaker_mapping.wav +0 -0
  47. data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_24_44100_no_speaker_mapping.wav +0 -0
  48. data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_32_44100.wav +0 -0
  49. data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_8_44100.wav +0 -0
  50. data/test/fixtures/{valid → wave/valid}/valid_extensible_tri_float_32_44100.wav +0 -0
  51. data/test/fixtures/{valid → wave/valid}/valid_extensible_tri_float_64_44100.wav +0 -0
  52. data/test/fixtures/wave/valid/valid_extensible_tri_pcm_16_44100.wav +0 -0
  53. data/test/fixtures/wave/valid/valid_extensible_tri_pcm_16_44100_custom_speaker_mapping.wav +0 -0
  54. data/test/fixtures/wave/valid/valid_extensible_tri_pcm_24_44100.wav +0 -0
  55. data/test/fixtures/wave/valid/valid_extensible_tri_pcm_32_44100.wav +0 -0
  56. data/test/fixtures/wave/valid/valid_extensible_tri_pcm_8_44100.wav +0 -0
  57. data/test/fixtures/{valid → wave/valid}/valid_mono_float_32_44100.wav +0 -0
  58. data/test/fixtures/{valid → wave/valid}/valid_mono_float_64_44100.wav +0 -0
  59. data/test/fixtures/{valid → wave/valid}/valid_mono_pcm_16_44100.wav +0 -0
  60. data/test/fixtures/{valid → wave/valid}/valid_mono_pcm_16_44100_junk_chunk_with_padding_byte.wav +0 -0
  61. data/test/fixtures/wave/valid/valid_mono_pcm_16_44100_with_extension.wav +0 -0
  62. data/test/fixtures/{valid → wave/valid}/valid_mono_pcm_24_44100.wav +0 -0
  63. data/test/fixtures/{valid → wave/valid}/valid_mono_pcm_32_44100.wav +0 -0
  64. data/test/fixtures/{valid → wave/valid}/valid_mono_pcm_8_44100.wav +0 -0
  65. data/test/fixtures/{valid → wave/valid}/valid_mono_pcm_8_44100_with_padding_byte.wav +0 -0
  66. data/test/fixtures/{valid → wave/valid}/valid_stereo_float_32_44100.wav +0 -0
  67. data/test/fixtures/{valid → wave/valid}/valid_stereo_float_64_44100.wav +0 -0
  68. data/test/fixtures/{valid → wave/valid}/valid_stereo_pcm_16_44100.wav +0 -0
  69. data/test/fixtures/{valid → wave/valid}/valid_stereo_pcm_24_44100.wav +0 -0
  70. data/test/fixtures/{valid → wave/valid}/valid_stereo_pcm_32_44100.wav +0 -0
  71. data/test/fixtures/{valid → wave/valid}/valid_stereo_pcm_8_44100.wav +0 -0
  72. data/test/fixtures/{valid → wave/valid}/valid_tri_float_32_44100.wav +0 -0
  73. data/test/fixtures/{valid → wave/valid}/valid_tri_float_64_44100.wav +0 -0
  74. data/test/fixtures/{valid → wave/valid}/valid_tri_pcm_16_44100.wav +0 -0
  75. data/test/fixtures/{valid → wave/valid}/valid_tri_pcm_24_44100.wav +0 -0
  76. data/test/fixtures/{valid → wave/valid}/valid_tri_pcm_32_44100.wav +0 -0
  77. data/test/fixtures/{valid → wave/valid}/valid_tri_pcm_8_44100.wav +0 -0
  78. data/test/format_test.rb +189 -3
  79. data/test/reader_test.rb +179 -4
  80. data/test/unvalidated_format_test.rb +181 -6
  81. data/test/wavefile_io_test_helper.rb +11 -1
  82. data/test/writer_test.rb +246 -25
  83. metadata +70 -80
  84. data/test/fixtures/actual_output/total_duration_mono_float_32_44100.wav +0 -0
  85. data/test/fixtures/actual_output/total_duration_mono_float_64_44100.wav +0 -0
  86. data/test/fixtures/actual_output/total_duration_mono_pcm_16_44100.wav +0 -0
  87. data/test/fixtures/actual_output/total_duration_mono_pcm_24_44100.wav +0 -0
  88. data/test/fixtures/actual_output/total_duration_mono_pcm_32_44100.wav +0 -0
  89. data/test/fixtures/actual_output/total_duration_mono_pcm_8_44100.wav +0 -0
  90. data/test/fixtures/actual_output/total_duration_stereo_float_32_44100.wav +0 -0
  91. data/test/fixtures/actual_output/total_duration_stereo_float_64_44100.wav +0 -0
  92. data/test/fixtures/actual_output/total_duration_stereo_pcm_16_44100.wav +0 -0
  93. data/test/fixtures/actual_output/total_duration_stereo_pcm_24_44100.wav +0 -0
  94. data/test/fixtures/actual_output/total_duration_stereo_pcm_32_44100.wav +0 -0
  95. data/test/fixtures/actual_output/total_duration_stereo_pcm_8_44100.wav +0 -0
  96. data/test/fixtures/actual_output/total_duration_tri_float_32_44100.wav +0 -0
  97. data/test/fixtures/actual_output/total_duration_tri_float_64_44100.wav +0 -0
  98. data/test/fixtures/actual_output/total_duration_tri_pcm_16_44100.wav +0 -0
  99. data/test/fixtures/actual_output/total_duration_tri_pcm_24_44100.wav +0 -0
  100. data/test/fixtures/actual_output/total_duration_tri_pcm_32_44100.wav +0 -0
  101. data/test/fixtures/actual_output/total_duration_tri_pcm_8_44100.wav +0 -0
  102. data/test/fixtures/invalid/README.markdown +0 -10
  103. data/test/fixtures/unsupported/README.markdown +0 -6
  104. data/test/fixtures/unsupported/extensible_unsupported_subformat_guid.wav +0 -0
  105. data/test/fixtures/valid/README.markdown +0 -3
  106. data/test/fixtures/valid/valid_extensible_mono_pcm_16_44100.wav +0 -0
  107. data/test/fixtures/valid/valid_extensible_mono_pcm_24_44100.wav +0 -0
  108. data/test/fixtures/valid/valid_extensible_mono_pcm_32_44100.wav +0 -0
  109. data/test/fixtures/valid/valid_extensible_mono_pcm_8_44100.wav +0 -0
  110. data/test/fixtures/valid/valid_extensible_stereo_pcm_16_44100.wav +0 -0
  111. data/test/fixtures/valid/valid_extensible_stereo_pcm_24_44100.wav +0 -0
  112. data/test/fixtures/valid/valid_extensible_stereo_pcm_32_44100.wav +0 -0
  113. data/test/fixtures/valid/valid_extensible_stereo_pcm_8_44100.wav +0 -0
  114. data/test/fixtures/valid/valid_extensible_tri_pcm_16_44100.wav +0 -0
  115. data/test/fixtures/valid/valid_extensible_tri_pcm_24_44100.wav +0 -0
  116. data/test/fixtures/valid/valid_extensible_tri_pcm_32_44100.wav +0 -0
  117. data/test/fixtures/valid/valid_extensible_tri_pcm_8_44100.wav +0 -0
@@ -12,7 +12,8 @@ class UnvalidatedFormatTest < Minitest::Test
12
12
  :byte_rate => 176400,
13
13
  :block_align => 4,
14
14
  :bits_per_sample => 16,
15
- :valid_bits_per_sample => 14})
15
+ :valid_bits_per_sample => 14,
16
+ :speaker_mapping => 3}) # Bit field '11'
16
17
 
17
18
  assert_equal(65534, format.audio_format)
18
19
  assert_equal(SUB_FORMAT_GUID_PCM, format.sub_audio_format_guid)
@@ -24,6 +25,7 @@ class UnvalidatedFormatTest < Minitest::Test
24
25
  assert_equal(4, format.block_align)
25
26
  assert_equal(16, format.bits_per_sample)
26
27
  assert_equal(14, format.valid_bits_per_sample)
28
+ assert_equal([:front_left, :front_right], format.speaker_mapping)
27
29
  end
28
30
 
29
31
  def test_to_validated_format_pcm
@@ -41,6 +43,7 @@ class UnvalidatedFormatTest < Minitest::Test
41
43
  assert_equal(44100, validated_format.sample_rate)
42
44
  assert_equal(176400, validated_format.byte_rate)
43
45
  assert_equal(4, validated_format.block_align)
46
+ assert_equal([:front_left, :front_right], validated_format.speaker_mapping)
44
47
  end
45
48
 
46
49
  def test_to_validated_format_float
@@ -58,6 +61,7 @@ class UnvalidatedFormatTest < Minitest::Test
58
61
  assert_equal(44100, validated_format.sample_rate)
59
62
  assert_equal(352800, validated_format.byte_rate)
60
63
  assert_equal(8, validated_format.block_align)
64
+ assert_equal([:front_left, :front_right], validated_format.speaker_mapping)
61
65
  end
62
66
 
63
67
  def test_to_validated_format_unsupported
@@ -79,7 +83,8 @@ class UnvalidatedFormatTest < Minitest::Test
79
83
  :byte_rate => 176400,
80
84
  :block_align => 4,
81
85
  :bits_per_sample => 16,
82
- :valid_bits_per_sample => 16})
86
+ :valid_bits_per_sample => 16,
87
+ :speaker_mapping => 3})
83
88
 
84
89
  validated_format = unvalidated_format.to_validated_format
85
90
  assert_equal(:pcm, validated_format.sample_format)
@@ -88,6 +93,7 @@ class UnvalidatedFormatTest < Minitest::Test
88
93
  assert_equal(44100, validated_format.sample_rate)
89
94
  assert_equal(176400, validated_format.byte_rate)
90
95
  assert_equal(4, validated_format.block_align)
96
+ assert_equal([:front_left, :front_right], validated_format.speaker_mapping)
91
97
  end
92
98
 
93
99
  def test_to_validated_format_wave_format_extensible_float
@@ -98,7 +104,8 @@ class UnvalidatedFormatTest < Minitest::Test
98
104
  :byte_rate => 352800,
99
105
  :block_align => 8,
100
106
  :bits_per_sample => 32,
101
- :valid_bits_per_sample => 32})
107
+ :valid_bits_per_sample => 32,
108
+ :speaker_mapping => 5})
102
109
 
103
110
  validated_format = unvalidated_format.to_validated_format
104
111
  assert_equal(:float, validated_format.sample_format)
@@ -107,6 +114,7 @@ class UnvalidatedFormatTest < Minitest::Test
107
114
  assert_equal(44100, validated_format.sample_rate)
108
115
  assert_equal(352800, validated_format.byte_rate)
109
116
  assert_equal(8, validated_format.block_align)
117
+ assert_equal([:front_left, :front_center], validated_format.speaker_mapping)
110
118
  end
111
119
 
112
120
  def test_to_validated_format_wave_format_extensible_unsupported_sub_format
@@ -117,7 +125,8 @@ class UnvalidatedFormatTest < Minitest::Test
117
125
  :byte_rate => 176400,
118
126
  :block_align => 4,
119
127
  :bits_per_sample => 16,
120
- :valid_bits_per_sample => 16})
128
+ :valid_bits_per_sample => 16,
129
+ :speaker_mapping => 3})
121
130
 
122
131
  assert_raises(InvalidFormatError) { unvalidated_format.to_validated_format }
123
132
  end
@@ -130,7 +139,8 @@ class UnvalidatedFormatTest < Minitest::Test
130
139
  :byte_rate => 176400,
131
140
  :block_align => 4,
132
141
  :bits_per_sample => 14,
133
- :valid_bits_per_sample => 14})
142
+ :valid_bits_per_sample => 14,
143
+ :speaker_mapping => 3})
134
144
 
135
145
  assert_raises(InvalidFormatError) { unvalidated_format.to_validated_format }
136
146
  end
@@ -143,8 +153,173 @@ class UnvalidatedFormatTest < Minitest::Test
143
153
  :byte_rate => 176400,
144
154
  :block_align => 4,
145
155
  :bits_per_sample => 16,
146
- :valid_bits_per_sample => 14})
156
+ :valid_bits_per_sample => 14,
157
+ :speaker_mapping => 3})
147
158
 
148
159
  assert_raises(UnsupportedFormatError) { unvalidated_format.to_validated_format }
149
160
  end
161
+
162
+ def test_speaker_mapping_no_speakers_defined
163
+ unvalided_format = UnvalidatedFormat.new({:audio_format => 65534,
164
+ :sub_audio_format_guid => SUB_FORMAT_GUID_PCM,
165
+ :channels => 3,
166
+ :sample_rate => 44100,
167
+ :byte_rate => 264600,
168
+ :block_align => 6,
169
+ :bits_per_sample => 16,
170
+ :valid_bits_per_sample => 16,
171
+ :speaker_mapping => 0 }) # According to spec, 0 means speakers
172
+ # are explicitly undefined for all channels
173
+
174
+ assert_equal(3, unvalided_format.channels)
175
+ assert_equal([:undefined, :undefined, :undefined], unvalided_format.speaker_mapping)
176
+ end
177
+
178
+ def test_speaker_mapping_more_speakers_defined_than_channels
179
+ unvalided_format = UnvalidatedFormat.new({:audio_format => 65534,
180
+ :sub_audio_format_guid => SUB_FORMAT_GUID_PCM,
181
+ :channels => 3,
182
+ :sample_rate => 44100,
183
+ :byte_rate => 264600,
184
+ :block_align => 6,
185
+ :bits_per_sample => 16,
186
+ :valid_bits_per_sample => 16,
187
+ :speaker_mapping => 214 }) # Bit field '11010110'
188
+
189
+ assert_equal(3, unvalided_format.channels)
190
+
191
+ # All 5 channel->speaker mappings are present, even though files only has 3 channels
192
+ assert_equal([:front_right, :front_center, :back_left, :front_left_of_center, :front_right_of_center], unvalided_format.speaker_mapping)
193
+ end
194
+
195
+ def test_speaker_mapping_more_speakers_than_are_defined
196
+ expected_speaker_mapping = [
197
+ :front_left,
198
+ :front_right,
199
+ :front_center,
200
+ :low_frequency,
201
+ :back_left,
202
+ :back_right,
203
+ :front_left_of_center,
204
+ :front_right_of_center,
205
+ :back_center,
206
+ :side_left,
207
+ :side_right,
208
+ :top_center,
209
+ :top_front_left,
210
+ :top_front_center,
211
+ :top_front_right,
212
+ :top_back_left,
213
+ :top_back_center,
214
+ :top_back_right,
215
+ ]
216
+
217
+ unvalided_format = UnvalidatedFormat.new({:audio_format => 65534,
218
+ :sub_audio_format_guid => SUB_FORMAT_GUID_PCM,
219
+ :channels => 3,
220
+ :sample_rate => 44100,
221
+ :byte_rate => 264600,
222
+ :block_align => 6,
223
+ :bits_per_sample => 16,
224
+ :valid_bits_per_sample => 16,
225
+ :speaker_mapping => 1048575 }) # Bit field '1111_1111_1111_1111_1111'
226
+
227
+ assert_equal(3, unvalided_format.channels)
228
+
229
+ # All channel->speaker mappings are present, even though file doesn't have this many channels
230
+ assert_equal(expected_speaker_mapping, unvalided_format.speaker_mapping)
231
+ end
232
+
233
+ def test_speaker_mapping_more_channels_than_mapped_speakers
234
+ unvalided_format = UnvalidatedFormat.new({:audio_format => 65534,
235
+ :sub_audio_format_guid => SUB_FORMAT_GUID_PCM,
236
+ :channels => 3,
237
+ :sample_rate => 44100,
238
+ :byte_rate => 264600,
239
+ :block_align => 6,
240
+ :bits_per_sample => 16,
241
+ :valid_bits_per_sample => 16,
242
+ :speaker_mapping => 2 }) # Bit field '10'
243
+
244
+ assert_equal(3, unvalided_format.channels)
245
+ assert_equal([:front_right, :undefined, :undefined], unvalided_format.speaker_mapping)
246
+ end
247
+
248
+ def test_speaker_mapping_more_channels_than_defined_speakers
249
+ expected_speaker_mapping = [
250
+ :front_left,
251
+ :front_right,
252
+ :front_center,
253
+ :low_frequency,
254
+ :back_left,
255
+ :back_right,
256
+ :front_left_of_center,
257
+ :front_right_of_center,
258
+ :back_center,
259
+ :side_left,
260
+ :side_right,
261
+ :top_center,
262
+ :top_front_left,
263
+ :top_front_center,
264
+ :top_front_right,
265
+ :top_back_left,
266
+ :top_back_center,
267
+ :top_back_right,
268
+ :undefined, # Spec only defines first 18 speakers, any subsequent ones are undefined
269
+ :undefined, # Spec only defines first 18 speakers, any subsequent ones are undefined
270
+ ]
271
+
272
+ unvalided_format = UnvalidatedFormat.new({:audio_format => 65534,
273
+ :sub_audio_format_guid => SUB_FORMAT_GUID_PCM,
274
+ :channels => 20,
275
+ :sample_rate => 44100,
276
+ :byte_rate => 1764000,
277
+ :block_align => 40,
278
+ :bits_per_sample => 16,
279
+ :valid_bits_per_sample => 16,
280
+ :speaker_mapping => 262143 }) # Bit field '11_1111_1111_1111_1111'
281
+
282
+ assert_equal(20, unvalided_format.channels)
283
+ assert_equal(expected_speaker_mapping, unvalided_format.speaker_mapping)
284
+ end
285
+
286
+ def test_speaker_mapping_more_channels_than_defined_speakers_2
287
+ expected_speaker_mapping = [
288
+ :front_left,
289
+ :front_right,
290
+ :front_center,
291
+ :low_frequency,
292
+ :back_left,
293
+ :back_right,
294
+ :front_left_of_center,
295
+ :front_right_of_center,
296
+ :back_center,
297
+ :side_left,
298
+ :side_right,
299
+ :top_center,
300
+ :top_front_left,
301
+ :top_front_center,
302
+ :top_front_right,
303
+ :top_back_left,
304
+ :top_back_center,
305
+ :top_back_right,
306
+ :undefined,
307
+ :undefined,
308
+ ]
309
+
310
+ unvalided_format = UnvalidatedFormat.new({:audio_format => 65534,
311
+ :sub_audio_format_guid => SUB_FORMAT_GUID_PCM,
312
+ :channels => 20,
313
+ :sample_rate => 44100,
314
+ :byte_rate => 264600,
315
+ :block_align => 6,
316
+ :bits_per_sample => 16,
317
+ :valid_bits_per_sample => 16,
318
+ :speaker_mapping => 1048575 }) # Bit field '1111_1111_1111_1111_1111'
319
+
320
+ assert_equal(20, unvalided_format.channels)
321
+
322
+ # All channel->speaker mappings are present, even though file doesn't have this many channels
323
+ assert_equal(expected_speaker_mapping, unvalided_format.speaker_mapping)
324
+ end
150
325
  end
@@ -1,6 +1,11 @@
1
1
  module WaveFileIOTestHelper
2
2
  CHANNEL_ALIAS = { :mono => 1, :stereo => 2, :tri => 3}
3
3
 
4
+ SUPPORTED_BITS_PER_SAMPLE = {
5
+ :pcm => [8, 16, 24, 32].freeze,
6
+ :float => [32, 64].freeze,
7
+ }.freeze
8
+
4
9
  SQUARE_WAVE_CYCLE = {}
5
10
  SQUARE_WAVE_CYCLE[:mono] = {}
6
11
 
@@ -9,6 +14,7 @@ module WaveFileIOTestHelper
9
14
  SQUARE_WAVE_CYCLE[:mono][:pcm_24] = [-1000000, -1000000, -1000000, -1000000, 1000000, 1000000, 1000000, 1000000]
10
15
  SQUARE_WAVE_CYCLE[:mono][:pcm_32] = [-1000000000, -1000000000, -1000000000, -1000000000,
11
16
  1000000000, 1000000000, 1000000000, 1000000000]
17
+ SQUARE_WAVE_CYCLE[:mono][:float] = [-0.5, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5]
12
18
  SQUARE_WAVE_CYCLE[:mono][:float_32] = [-0.5, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5]
13
19
  SQUARE_WAVE_CYCLE[:mono][:float_64] = SQUARE_WAVE_CYCLE[:mono][:float_32]
14
20
 
@@ -23,6 +29,8 @@ module WaveFileIOTestHelper
23
29
  [-1000000000, -1000000000], [-1000000000, -1000000000],
24
30
  [ 1000000000, 1000000000], [ 1000000000, 1000000000],
25
31
  [ 1000000000, 1000000000], [ 1000000000, 1000000000]]
32
+ SQUARE_WAVE_CYCLE[:stereo][:float] = [[-0.5, -0.5], [-0.5, -0.5], [-0.5, -0.5], [-0.5, -0.5],
33
+ [0.5, 0.5], [0.5, 0.5], [0.5, 0.5], [0.5, 0.5]]
26
34
  SQUARE_WAVE_CYCLE[:stereo][:float_32] = [[-0.5, -0.5], [-0.5, -0.5], [-0.5, -0.5], [-0.5, -0.5],
27
35
  [0.5, 0.5], [0.5, 0.5], [0.5, 0.5], [0.5, 0.5]]
28
36
  SQUARE_WAVE_CYCLE[:stereo][:float_64] = SQUARE_WAVE_CYCLE[:stereo][:float_32]
@@ -46,6 +54,8 @@ module WaveFileIOTestHelper
46
54
  [ 1000000000, 1000000000, 1000000000],
47
55
  [ 1000000000, 1000000000, 1000000000],
48
56
  [ 1000000000, 1000000000, 1000000000]]
57
+ SQUARE_WAVE_CYCLE[:tri][:float] = [[-0.5, -0.5, -0.5], [-0.5, -0.5, -0.5], [-0.5, -0.5, -0.5], [-0.5, -0.5, -0.5],
58
+ [0.5, 0.5, 0.5], [0.5, 0.5, 0.5], [0.5, 0.5, 0.5], [0.5, 0.5, 0.5]]
49
59
  SQUARE_WAVE_CYCLE[:tri][:float_32] = [[-0.5, -0.5, -0.5], [-0.5, -0.5, -0.5], [-0.5, -0.5, -0.5], [-0.5, -0.5, -0.5],
50
60
  [0.5, 0.5, 0.5], [0.5, 0.5, 0.5], [0.5, 0.5, 0.5], [0.5, 0.5, 0.5]]
51
61
  SQUARE_WAVE_CYCLE[:tri][:float_64] = SQUARE_WAVE_CYCLE[:tri][:float_32]
@@ -56,7 +66,7 @@ module WaveFileIOTestHelper
56
66
  ["", "extensible_"].each do |format_chunk_format|
57
67
  [:mono, :stereo, :tri].each do |channels|
58
68
  [:pcm, :float].each do |sample_format|
59
- Format::SUPPORTED_BITS_PER_SAMPLE[sample_format].each do |bits_per_sample|
69
+ SUPPORTED_BITS_PER_SAMPLE[sample_format].each do |bits_per_sample|
60
70
  yield(format_chunk_format, channels, "#{sample_format}_#{bits_per_sample}".to_sym)
61
71
  end
62
72
  end
@@ -10,6 +10,11 @@ class WriterTest < Minitest::Test
10
10
  OUTPUT_FOLDER = "test/fixtures/actual_output"
11
11
 
12
12
  def setup
13
+ # Make the output folder if it doesn't already exist
14
+ Dir.mkdir(OUTPUT_FOLDER) unless File.exist?(OUTPUT_FOLDER)
15
+ end
16
+
17
+ def teardown
13
18
  clean_output_folder
14
19
  end
15
20
 
@@ -21,36 +26,115 @@ class WriterTest < Minitest::Test
21
26
  end
22
27
 
23
28
  def test_write_basic_file
24
- exhaustively_test do |format_chunk_format, channels, sample_format|
25
- file_name = "valid_#{channels}_#{sample_format}_44100.wav"
26
- format = Format.new(CHANNEL_ALIAS[channels], sample_format, 44100)
29
+ [:mono, :stereo].each do |channels|
30
+ [:pcm_8, :pcm_16, :float, :float_32, :float_64].each do |sample_format|
31
+ file_name = "valid_#{channels}_#{sample_format === :float ? :float_32 : sample_format}_44100.wav"
32
+ format = Format.new(CHANNEL_ALIAS[channels], sample_format, 44100)
27
33
 
28
- ["#{OUTPUT_FOLDER}/#{file_name}", StringIO.new].each do |io_or_file_name|
29
- writer = Writer.new(io_or_file_name, format)
30
- writer.write(Buffer.new(SQUARE_WAVE_CYCLE[channels][sample_format] * 128, format))
31
- writer.write(Buffer.new(SQUARE_WAVE_CYCLE[channels][sample_format] * 128, format))
32
- writer.write(Buffer.new(SQUARE_WAVE_CYCLE[channels][sample_format] * 24, format))
33
- writer.close
34
+ ["#{OUTPUT_FOLDER}/#{file_name}", StringIO.new].each do |io_or_file_name|
35
+ write_file(io_or_file_name, file_name, format)
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ def test_write_extensible_file
42
+ # Otherwise non-extensible, but speaker mapping is not the default for 1 channel
43
+ file_name = "valid_extensible_mono_pcm_16_44100_non_default_speaker_mapping.wav"
44
+ format = Format.new(:mono, :pcm_16, 44100, speaker_mapping: [:front_left])
45
+
46
+ ["#{OUTPUT_FOLDER}/#{file_name}", StringIO.new].each do |io_or_file_name|
47
+ write_file(io_or_file_name, file_name, format)
48
+ end
49
+
50
+
51
+ # Otherwise non-extensible, but speaker mapping is not the default for 2 channels
52
+ file_name = "valid_extensible_stereo_pcm_16_44100_center_right_speakers.wav"
53
+ format = Format.new(:stereo, :pcm_16, 44100, speaker_mapping: [:front_right, :front_center])
54
+
55
+ ["#{OUTPUT_FOLDER}/#{file_name}", StringIO.new].each do |io_or_file_name|
56
+ write_file(io_or_file_name, file_name, format)
57
+ end
58
+
59
+
60
+ [:mono].each do |channels|
61
+ [:pcm_24, :pcm_32].each do |sample_format|
62
+ file_name = "valid_extensible_#{channels}_#{sample_format}_44100.wav"
63
+ format = Format.new(CHANNEL_ALIAS[channels], sample_format, 44100, speaker_mapping: [:front_center])
64
+
65
+ ["#{OUTPUT_FOLDER}/#{file_name}", StringIO.new].each do |io_or_file_name|
66
+ write_file(io_or_file_name, file_name, format)
67
+ end
68
+ end
69
+ end
34
70
 
35
- assert_equal(read_file(:expected, file_name), read_file(:actual, file_name))
71
+ [:stereo].each do |channels|
72
+ [:pcm_24, :pcm_32].each do |sample_format|
73
+ file_name = "valid_extensible_#{channels}_#{sample_format}_44100.wav"
74
+ format = Format.new(CHANNEL_ALIAS[channels], sample_format, 44100, speaker_mapping: [:front_left, :front_right])
75
+
76
+ ["#{OUTPUT_FOLDER}/#{file_name}", StringIO.new].each do |io_or_file_name|
77
+ write_file(io_or_file_name, file_name, format)
78
+ end
79
+ end
80
+ end
81
+
82
+ [:tri].each do |channels|
83
+ [:pcm_8, :pcm_16, :pcm_24, :pcm_32, :float, :float_32, :float_64].each do |sample_format|
84
+ file_name = "valid_extensible_#{channels}_#{sample_format === :float ? :float_32 : sample_format}_44100.wav"
85
+ format = Format.new(CHANNEL_ALIAS[channels], sample_format, 44100, speaker_mapping: [:front_left, :front_right, :front_center])
86
+
87
+ ["#{OUTPUT_FOLDER}/#{file_name}", StringIO.new].each do |io_or_file_name|
88
+ write_file(io_or_file_name, file_name, format)
89
+ end
36
90
  end
37
91
  end
38
92
  end
39
93
 
40
94
  def test_write_basic_file_with_a_block
41
- exhaustively_test do |format_chunk_format, channels, sample_format|
42
- file_name = "valid_#{channels}_#{sample_format}_44100.wav"
43
- format = Format.new(CHANNEL_ALIAS[channels], sample_format, 44100)
95
+ [:mono, :stereo].each do |channels|
96
+ [:pcm_8, :pcm_16, :float, :float_32, :float_64].each do |sample_format|
97
+ file_name = "valid_#{channels}_#{sample_format === :float ? :float_32 : sample_format}_44100.wav"
98
+ format = Format.new(CHANNEL_ALIAS[channels], sample_format, 44100)
99
+
100
+ ["#{OUTPUT_FOLDER}/#{file_name}", StringIO.new].each do |io_or_file_name|
101
+ write_file_with_a_block(io_or_file_name, file_name, format)
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ def test_write_extensible_file_with_a_block
108
+ [:mono].each do |channels|
109
+ [:pcm_24, :pcm_32].each do |sample_format|
110
+ file_name = "valid_extensible_#{channels}_#{sample_format}_44100.wav"
111
+ format = Format.new(CHANNEL_ALIAS[channels], sample_format, 44100, speaker_mapping: [:front_center])
44
112
 
45
- ["#{OUTPUT_FOLDER}/#{file_name}", StringIO.new].each do |io_or_file_name|
46
- writer = Writer.new(io_or_file_name, format) do |w|
47
- 4.times do
48
- w.write(Buffer.new(SQUARE_WAVE_CYCLE[channels][sample_format] * 70, format))
49
- end
113
+ ["#{OUTPUT_FOLDER}/#{file_name}", StringIO.new].each do |io_or_file_name|
114
+ write_file_with_a_block(io_or_file_name, file_name, format)
50
115
  end
116
+ end
117
+ end
51
118
 
52
- assert_equal(read_file(:expected, file_name), read_file(:actual, file_name))
53
- assert(writer.closed?)
119
+ [:stereo].each do |channels|
120
+ [:pcm_24, :pcm_32].each do |sample_format|
121
+ file_name = "valid_extensible_#{channels}_#{sample_format}_44100.wav"
122
+ format = Format.new(CHANNEL_ALIAS[channels], sample_format, 44100, speaker_mapping: [:front_left, :front_right])
123
+
124
+ ["#{OUTPUT_FOLDER}/#{file_name}", StringIO.new].each do |io_or_file_name|
125
+ write_file_with_a_block(io_or_file_name, file_name, format)
126
+ end
127
+ end
128
+ end
129
+
130
+ [:tri].each do |channels|
131
+ [:pcm_8, :pcm_16, :pcm_24, :pcm_32, :float, :float_32, :float_64].each do |sample_format|
132
+ file_name = "valid_extensible_#{channels}_#{sample_format === :float ? :float_32 : sample_format}_44100.wav"
133
+ format = Format.new(CHANNEL_ALIAS[channels], sample_format, 44100, speaker_mapping: [:front_left, :front_right, :front_center])
134
+
135
+ ["#{OUTPUT_FOLDER}/#{file_name}", StringIO.new].each do |io_or_file_name|
136
+ write_file_with_a_block(io_or_file_name, file_name, format)
137
+ end
54
138
  end
55
139
  end
56
140
  end
@@ -87,6 +171,75 @@ class WriterTest < Minitest::Test
87
171
  end
88
172
  end
89
173
 
174
+ def test_write_no_speaker_mapping
175
+ file_name = "valid_extensible_stereo_pcm_24_44100_no_speaker_mapping.wav"
176
+ format = Format.new(:stereo, :pcm_24, 44100, speaker_mapping: [:undefined, :undefined])
177
+
178
+ ["#{OUTPUT_FOLDER}/#{file_name}", StringIO.new].each do |io_or_file_name|
179
+ write_file(io_or_file_name, file_name, format)
180
+ end
181
+ end
182
+
183
+ def test_write_incomplete_speaker_mapping
184
+ file_name = "valid_extensible_stereo_pcm_24_44100_incomplete_speaker_mapping.wav"
185
+ format = Format.new(:stereo, :pcm_24, 44100, speaker_mapping: [:front_center])
186
+
187
+ ["#{OUTPUT_FOLDER}/#{file_name}", StringIO.new].each do |io_or_file_name|
188
+ write_file(io_or_file_name, file_name, format)
189
+ end
190
+ end
191
+
192
+ def test_write_speaker_mapping_overflow
193
+ speaker_mapping = [
194
+ :front_left,
195
+ :front_right,
196
+ :front_center,
197
+ :low_frequency,
198
+ :back_left,
199
+ :back_right,
200
+ :front_left_of_center,
201
+ :front_right_of_center,
202
+ :back_center,
203
+ :side_left,
204
+ :side_right,
205
+ :top_center,
206
+ :top_front_left,
207
+ :top_front_center,
208
+ :top_front_right,
209
+ :top_back_left,
210
+ :top_back_center,
211
+ :top_back_right,
212
+ ]
213
+
214
+ negative = [-10000] * 20
215
+ positive = [10000] * 20
216
+ square_wave_cycle = [negative, negative, negative, negative, positive, positive, positive, positive]
217
+
218
+ file_name = "valid_extensible_20_pcm_16_44100_speaker_mapping_overflow.wav"
219
+ format = Format.new(20, :pcm_16, 44100, speaker_mapping: speaker_mapping)
220
+
221
+ ["#{OUTPUT_FOLDER}/#{file_name}", StringIO.new].each do |io_or_file_name|
222
+ writer = Writer.new(io_or_file_name, format)
223
+ writer.write(Buffer.new(square_wave_cycle * 128, format))
224
+ writer.write(Buffer.new(square_wave_cycle * 128, format))
225
+ writer.write(Buffer.new(square_wave_cycle * 24, format))
226
+ writer.close
227
+
228
+ assert_equal(read_file(:expected, file_name),
229
+ read_file(:actual, file_name),
230
+ "Written file doesn't match expected output: #{file_name}")
231
+ end
232
+ end
233
+
234
+ def test_write_custom_speaker_mapping
235
+ file_name = "valid_extensible_tri_pcm_16_44100_custom_speaker_mapping.wav"
236
+ format = Format.new(3, :pcm_16, 44100, speaker_mapping: [:back_left, :side_right, :top_back_left])
237
+
238
+ ["#{OUTPUT_FOLDER}/#{file_name}", StringIO.new].each do |io_or_file_name|
239
+ write_file(io_or_file_name, file_name, format)
240
+ end
241
+ end
242
+
90
243
  def test_close_when_constructed_from_file_name
91
244
  writer = Writer.new("#{OUTPUT_FOLDER}/closed_test.wav", Format.new(:mono, :pcm_16, 44100))
92
245
  assert_equal(false, writer.closed?)
@@ -108,7 +261,7 @@ class WriterTest < Minitest::Test
108
261
  assert(writer.closed?)
109
262
 
110
263
  assert_equal(false, io.closed?)
111
- assert_equal(2092, io.pos) # 44 bytes for header, plus 2 bytes per sample, with 8 * 128 samples
264
+ assert_equal(2092, io.pos) # 44 bytes for header, plus 2 bytes per sample, with 8 * 128 samples
112
265
  io.close
113
266
  end
114
267
 
@@ -124,6 +277,18 @@ class WriterTest < Minitest::Test
124
277
  end
125
278
  end
126
279
 
280
+ def test_double_close
281
+ format = Format.new(:mono, :pcm_8, 44100)
282
+
283
+ ["#{OUTPUT_FOLDER}/double_close.wav", StringIO.new].each do |io_or_file_name|
284
+ writer = Writer.new(io_or_file_name, format)
285
+ writer.write(Buffer.new([1, 2, 3, 4], format))
286
+ writer.close
287
+ # The test is that this doesn't raise an error
288
+ writer.close
289
+ end
290
+ end
291
+
127
292
  def test_total_duration
128
293
  exhaustively_test do |format_chunk_format, channels, sample_format|
129
294
  format = Format.new(CHANNEL_ALIAS[channels], sample_format, 44100)
@@ -202,7 +367,7 @@ private
202
367
 
203
368
  def read_file(type, file_name)
204
369
  if type == :expected
205
- fixture_folder = 'valid'
370
+ fixture_folder = 'wave/valid'
206
371
  elsif type == :actual
207
372
  fixture_folder = 'actual_output'
208
373
  else
@@ -214,9 +379,6 @@ private
214
379
  end
215
380
 
216
381
  def clean_output_folder
217
- # Make the folder if it doesn't already exist
218
- Dir.mkdir(OUTPUT_FOLDER) unless File.exist?(OUTPUT_FOLDER)
219
-
220
382
  dir = Dir.new(OUTPUT_FOLDER)
221
383
  file_names = dir.entries
222
384
  file_names.each do |file_name|
@@ -225,4 +387,63 @@ private
225
387
  end
226
388
  end
227
389
  end
390
+
391
+ def write_file(io_or_file_name, file_name, format)
392
+ if format.channels == 1
393
+ channel_label = :mono
394
+ elsif format.channels == 2
395
+ channel_label = :stereo
396
+ elsif format.channels == 3
397
+ channel_label = :tri
398
+ end
399
+ sample_format = "#{format.sample_format}_#{format.bits_per_sample}".to_sym
400
+
401
+ writer = Writer.new(io_or_file_name, format)
402
+ writer.write(Buffer.new(SQUARE_WAVE_CYCLE[channel_label][sample_format] * 128, format))
403
+ writer.write(Buffer.new(SQUARE_WAVE_CYCLE[channel_label][sample_format] * 128, format))
404
+ writer.write(Buffer.new(SQUARE_WAVE_CYCLE[channel_label][sample_format] * 24, format))
405
+ writer.close
406
+
407
+ if io_or_file_name.is_a?(StringIO)
408
+ io_or_file_name.rewind
409
+ actual_file = io_or_file_name.read.force_encoding("ASCII-8BIT")
410
+ else
411
+ actual_file = read_file(:actual, file_name)
412
+ end
413
+ expected_file = read_file(:expected, file_name)
414
+
415
+ assert_equal(expected_file,
416
+ actual_file,
417
+ "Written file doesn't match expected output: #{file_name}")
418
+ end
419
+
420
+ def write_file_with_a_block(io_or_file_name, file_name, format)
421
+ if format.channels == 1
422
+ channel_label = :mono
423
+ elsif format.channels == 2
424
+ channel_label = :stereo
425
+ elsif format.channels == 3
426
+ channel_label = :tri
427
+ end
428
+ sample_format = "#{format.sample_format}_#{format.bits_per_sample}".to_sym
429
+
430
+ writer = Writer.new(io_or_file_name, format) do |w|
431
+ 4.times do
432
+ w.write(Buffer.new(SQUARE_WAVE_CYCLE[channel_label][sample_format] * 70, format))
433
+ end
434
+ end
435
+
436
+ if io_or_file_name.is_a?(StringIO)
437
+ io_or_file_name.rewind
438
+ actual_file = io_or_file_name.read.force_encoding("ASCII-8BIT")
439
+ else
440
+ actual_file = read_file(:actual, file_name)
441
+ end
442
+ expected_file = read_file(:expected, file_name)
443
+
444
+ assert_equal(actual_file,
445
+ expected_file,
446
+ "Written file doesn't match expected output: #{file_name}")
447
+ assert(writer.closed?)
448
+ end
228
449
  end