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.
- checksums.yaml +5 -5
- data/LICENSE +1 -1
- data/README.markdown +33 -28
- data/Rakefile +2 -2
- data/lib/wavefile.rb +5 -5
- data/lib/wavefile/chunk_readers/format_chunk_reader.rb +1 -1
- data/lib/wavefile/duration.rb +1 -1
- data/lib/wavefile/format.rb +120 -29
- data/lib/wavefile/reader.rb +18 -16
- data/lib/wavefile/unvalidated_format.rb +51 -3
- data/lib/wavefile/writer.rb +74 -21
- data/test/buffer_test.rb +13 -10
- data/test/chunk_readers/format_chunk_reader_test.rb +40 -40
- data/test/fixtures/{invalid → wave/invalid}/bad_riff_header.wav +0 -0
- data/test/fixtures/{invalid → wave/invalid}/bad_wavefile_format.wav +0 -0
- data/test/fixtures/{invalid → wave/invalid}/empty.wav +0 -0
- data/test/fixtures/{invalid → wave/invalid}/empty_format_chunk.wav +0 -0
- data/test/fixtures/{invalid → wave/invalid}/incomplete_riff_header.wav +0 -0
- data/test/fixtures/{invalid → wave/invalid}/insufficient_format_chunk.wav +0 -0
- data/test/fixtures/{invalid → wave/invalid}/no_data_chunk.wav +0 -0
- data/test/fixtures/{invalid → wave/invalid}/no_format_chunk.wav +0 -0
- data/test/fixtures/{unsupported → wave/unsupported}/bad_audio_format.wav +0 -0
- data/test/fixtures/{unsupported → wave/unsupported}/bad_channel_count.wav +0 -0
- data/test/fixtures/{unsupported → wave/unsupported}/bad_sample_rate.wav +0 -0
- data/test/fixtures/{unsupported → wave/unsupported}/extensible_container_size_bigger_than_sample_size.wav +0 -0
- data/test/fixtures/wave/unsupported/extensible_unsupported_subformat_guid.wav +0 -0
- data/test/fixtures/{unsupported → wave/unsupported}/unsupported_audio_format.wav +0 -0
- data/test/fixtures/{unsupported → wave/unsupported}/unsupported_bits_per_sample.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/no_samples.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_20_pcm_16_44100_speaker_mapping_overflow.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_extensible_mono_float_32_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_extensible_mono_float_64_44100.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_mono_pcm_16_44100.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_mono_pcm_16_44100_non_default_speaker_mapping.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_mono_pcm_24_44100.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_mono_pcm_32_44100.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_mono_pcm_8_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_extensible_stereo_float_32_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_extensible_stereo_float_64_44100.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_16_44100.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_16_44100_center_right_speakers.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_16_44100_more_speakers_than_channels.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_16_44100_more_speakers_than_defined_by_spec.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_16_44100_only_undefined_high_bit_speakers.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_24_44100.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_24_44100_incomplete_speaker_mapping.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_24_44100_no_speaker_mapping.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_32_44100.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_stereo_pcm_8_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_extensible_tri_float_32_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_extensible_tri_float_64_44100.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_tri_pcm_16_44100.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_tri_pcm_16_44100_custom_speaker_mapping.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_tri_pcm_24_44100.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_tri_pcm_32_44100.wav +0 -0
- data/test/fixtures/wave/valid/valid_extensible_tri_pcm_8_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_mono_float_32_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_mono_float_64_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_mono_pcm_16_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_mono_pcm_16_44100_junk_chunk_with_padding_byte.wav +0 -0
- data/test/fixtures/wave/valid/valid_mono_pcm_16_44100_with_extension.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_mono_pcm_24_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_mono_pcm_32_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_mono_pcm_8_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_mono_pcm_8_44100_with_padding_byte.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_stereo_float_32_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_stereo_float_64_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_stereo_pcm_16_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_stereo_pcm_24_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_stereo_pcm_32_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_stereo_pcm_8_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_tri_float_32_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_tri_float_64_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_tri_pcm_16_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_tri_pcm_24_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_tri_pcm_32_44100.wav +0 -0
- data/test/fixtures/{valid → wave/valid}/valid_tri_pcm_8_44100.wav +0 -0
- data/test/format_test.rb +189 -3
- data/test/reader_test.rb +179 -4
- data/test/unvalidated_format_test.rb +181 -6
- data/test/wavefile_io_test_helper.rb +11 -1
- data/test/writer_test.rb +246 -25
- metadata +70 -80
- data/test/fixtures/actual_output/total_duration_mono_float_32_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_mono_float_64_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_mono_pcm_16_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_mono_pcm_24_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_mono_pcm_32_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_mono_pcm_8_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_stereo_float_32_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_stereo_float_64_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_stereo_pcm_16_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_stereo_pcm_24_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_stereo_pcm_32_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_stereo_pcm_8_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_tri_float_32_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_tri_float_64_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_tri_pcm_16_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_tri_pcm_24_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_tri_pcm_32_44100.wav +0 -0
- data/test/fixtures/actual_output/total_duration_tri_pcm_8_44100.wav +0 -0
- data/test/fixtures/invalid/README.markdown +0 -10
- data/test/fixtures/unsupported/README.markdown +0 -6
- data/test/fixtures/unsupported/extensible_unsupported_subformat_guid.wav +0 -0
- data/test/fixtures/valid/README.markdown +0 -3
- data/test/fixtures/valid/valid_extensible_mono_pcm_16_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_mono_pcm_24_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_mono_pcm_32_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_mono_pcm_8_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_stereo_pcm_16_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_stereo_pcm_24_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_stereo_pcm_32_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_stereo_pcm_8_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_tri_pcm_16_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_tri_pcm_24_44100.wav +0 -0
- data/test/fixtures/valid/valid_extensible_tri_pcm_32_44100.wav +0 -0
- 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
|
-
|
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
|
data/test/writer_test.rb
CHANGED
@@ -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
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
46
|
-
|
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
|
-
|
53
|
-
|
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
|