wavefile 0.8.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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