amine@403
|
1 import filecmp
|
amine@106
|
2 import os
|
amine@406
|
3 import wave
|
amine@406
|
4 from pathlib import Path
|
amine@133
|
5 from tempfile import NamedTemporaryFile, TemporaryDirectory
|
amine@403
|
6 from unittest.mock import Mock, patch
|
amine@403
|
7
|
amine@405
|
8 import numpy as np
|
amine@400
|
9 import pytest
|
amine@426
|
10 from test_AudioSource import PURE_TONE_DICT, _sample_generator
|
amine@403
|
11
|
amine@426
|
12 import auditok
|
amine@110
|
13 from auditok.io import (
|
amine@121
|
14 AudioIOError,
|
amine@110
|
15 AudioParameterError,
|
amine@126
|
16 BufferAudioSource,
|
amine@162
|
17 RawAudioSource,
|
amine@403
|
18 StdinAudioSource,
|
amine@162
|
19 WaveAudioSource,
|
amine@403
|
20 _get_audio_parameters,
|
amine@143
|
21 _guess_audio_format,
|
amine@126
|
22 _load_raw,
|
amine@129
|
23 _load_wave,
|
amine@131
|
24 _load_with_pydub,
|
amine@111
|
25 _save_raw,
|
amine@110
|
26 _save_wave,
|
amine@141
|
27 _save_with_pydub,
|
amine@403
|
28 check_audio_data,
|
amine@403
|
29 from_file,
|
amine@403
|
30 get_audio_source,
|
amine@135
|
31 to_file,
|
amine@110
|
32 )
|
amine@405
|
33 from auditok.signal import SAMPLE_WIDTH_TO_DTYPE
|
amine@106
|
34
|
amine@405
|
35 AUDIO_PARAMS = {"sampling_rate": 16000, "sample_width": 2, "channels": 1}
|
amine@120
|
36 AUDIO_PARAMS_SHORT = {"sr": 16000, "sw": 2, "ch": 1}
|
amine@106
|
37
|
amine@106
|
38
|
amine@400
|
39 @pytest.mark.parametrize(
|
amine@400
|
40 "data, sample_width, channels, valid",
|
amine@400
|
41 [
|
amine@400
|
42 (b"\0" * 113, 1, 1, True), # valid_mono
|
amine@400
|
43 (b"\0" * 160, 1, 2, True), # valid_stereo
|
amine@400
|
44 (b"\0" * 113, 2, 1, False), # invalid_mono_sw_2
|
amine@400
|
45 (b"\0" * 113, 1, 2, False), # invalid_stereo_sw_1
|
amine@400
|
46 (b"\0" * 158, 2, 2, False), # invalid_stereo_sw_2
|
amine@400
|
47 ],
|
amine@400
|
48 ids=[
|
amine@400
|
49 "valid_mono",
|
amine@400
|
50 "valid_stereo",
|
amine@400
|
51 "invalid_mono_sw_2",
|
amine@400
|
52 "invalid_stereo_sw_1",
|
amine@400
|
53 "invalid_stereo_sw_2",
|
amine@400
|
54 ],
|
amine@400
|
55 )
|
amine@400
|
56 def test_check_audio_data(data, sample_width, channels, valid):
|
amine@400
|
57 if not valid:
|
amine@400
|
58 with pytest.raises(AudioParameterError):
|
amine@400
|
59 check_audio_data(data, sample_width, channels)
|
amine@400
|
60 else:
|
amine@400
|
61 assert check_audio_data(data, sample_width, channels) is None
|
amine@400
|
62
|
amine@400
|
63
|
amine@400
|
64 @pytest.mark.parametrize(
|
amine@406
|
65 "filename, audio_format, expected",
|
amine@400
|
66 [
|
amine@406
|
67 ("filename.wav", "wav", "wav"), # extension_and_format_same
|
amine@406
|
68 ("filename.mp3", "wav", "wav"), # extension_and_format_different
|
amine@406
|
69 ("filename.wav", None, "wav"), # extension_no_format
|
amine@406
|
70 ("filename", "wav", "wav"), # format_no_extension
|
amine@406
|
71 ("filename", None, None), # no_format_no_extension
|
amine@406
|
72 ("filename", "wave", "wav"), # wave_as_wav
|
amine@406
|
73 ("filename.wave", None, "wav"), # wave_as_wav_extension
|
amine@400
|
74 ],
|
amine@400
|
75 ids=[
|
amine@406
|
76 "extension_and_format_same",
|
amine@406
|
77 "extension_and_format_different",
|
amine@406
|
78 "extension_no_format",
|
amine@400
|
79 "format_no_extension",
|
amine@400
|
80 "no_format_no_extension",
|
amine@400
|
81 "wave_as_wav",
|
amine@400
|
82 "wave_as_wav_extension",
|
amine@400
|
83 ],
|
amine@400
|
84 )
|
amine@406
|
85 def test_guess_audio_format(filename, audio_format, expected):
|
amine@406
|
86 result = _guess_audio_format(filename, audio_format)
|
amine@406
|
87 assert result == expected
|
amine@406
|
88
|
amine@406
|
89 result = _guess_audio_format(Path(filename), audio_format)
|
amine@400
|
90 assert result == expected
|
amine@400
|
91
|
amine@400
|
92
|
amine@400
|
93 def test_get_audio_parameters_short_params():
|
amine@400
|
94 expected = (8000, 2, 1)
|
amine@426
|
95 params = dict(zip(("sr", "sw", "ch"), expected))
|
amine@400
|
96 result = _get_audio_parameters(params)
|
amine@400
|
97 assert result == expected
|
amine@400
|
98
|
amine@400
|
99
|
amine@400
|
100 def test_get_audio_parameters_long_params():
|
amine@400
|
101 expected = (8000, 2, 1)
|
amine@426
|
102 params = dict(zip(("sampling_rate", "sample_width", "channels"), expected))
|
amine@426
|
103 result = _get_audio_parameters(params)
|
amine@426
|
104 assert result == expected
|
amine@426
|
105
|
amine@426
|
106
|
amine@426
|
107 def test_get_audio_parameters_long_params_shadow_short_ones():
|
amine@426
|
108 expected = (8000, 2, 1)
|
amine@400
|
109 params = dict(
|
amine@400
|
110 zip(
|
amine@405
|
111 ("sampling_rate", "sample_width", "channels"),
|
amine@400
|
112 expected,
|
amine@400
|
113 )
|
amine@108
|
114 )
|
amine@426
|
115 params.update(
|
amine@426
|
116 dict(
|
amine@426
|
117 zip(
|
amine@426
|
118 ("sr", "sw", "ch"),
|
amine@426
|
119 "xxx",
|
amine@426
|
120 )
|
amine@405
|
121 )
|
amine@405
|
122 )
|
amine@400
|
123 result = _get_audio_parameters(params)
|
amine@400
|
124 assert result == expected
|
amine@143
|
125
|
amine@145
|
126
|
amine@400
|
127 @pytest.mark.parametrize(
|
amine@405
|
128 "missing_param",
|
amine@405
|
129 [
|
amine@405
|
130 "sampling_rate", # missing_sampling_rate
|
amine@405
|
131 "sample_width", # missing_sample_width
|
amine@405
|
132 "channels", # missing_channels
|
amine@405
|
133 ],
|
amine@405
|
134 ids=["missing_sampling_rate", "missing_sample_width", "missing_channels"],
|
amine@405
|
135 )
|
amine@405
|
136 def test_get_audio_parameters_missing_parameter(missing_param):
|
amine@405
|
137 params = AUDIO_PARAMS.copy()
|
amine@405
|
138 del params[missing_param]
|
amine@405
|
139 with pytest.raises(AudioParameterError):
|
amine@405
|
140 _get_audio_parameters(params)
|
amine@405
|
141
|
amine@405
|
142
|
amine@405
|
143 @pytest.mark.parametrize(
|
amine@405
|
144 "missing_param",
|
amine@405
|
145 [
|
amine@405
|
146 "sr", # missing_sampling_rate
|
amine@405
|
147 "sw", # missing_sample_width
|
amine@405
|
148 "ch", # missing_channels
|
amine@405
|
149 ],
|
amine@405
|
150 ids=["missing_sampling_rate", "missing_sample_width", "missing_channels"],
|
amine@405
|
151 )
|
amine@405
|
152 def test_get_audio_parameters_missing_parameter_short(missing_param):
|
amine@405
|
153 params = AUDIO_PARAMS_SHORT.copy()
|
amine@405
|
154 del params[missing_param]
|
amine@405
|
155 with pytest.raises(AudioParameterError):
|
amine@405
|
156 _get_audio_parameters(params)
|
amine@405
|
157
|
amine@405
|
158
|
amine@405
|
159 @pytest.mark.parametrize(
|
amine@400
|
160 "values",
|
amine@400
|
161 [
|
amine@400
|
162 ("x", 2, 1), # str_sampling_rate
|
amine@400
|
163 (-8000, 2, 1), # negative_sampling_rate
|
amine@400
|
164 (8000, "x", 1), # str_sample_width
|
amine@400
|
165 (8000, -2, 1), # negative_sample_width
|
amine@400
|
166 (8000, 2, "x"), # str_channels
|
amine@400
|
167 (8000, 2, -1), # negative_channels
|
amine@400
|
168 ],
|
amine@400
|
169 ids=[
|
amine@400
|
170 "str_sampling_rate",
|
amine@400
|
171 "negative_sampling_rate",
|
amine@400
|
172 "str_sample_width",
|
amine@400
|
173 "negative_sample_width",
|
amine@400
|
174 "str_channels",
|
amine@400
|
175 "negative_channels",
|
amine@400
|
176 ],
|
amine@400
|
177 )
|
amine@400
|
178 def test_get_audio_parameters_invalid(values):
|
amine@405
|
179 params = dict(
|
amine@426
|
180 zip(
|
amine@426
|
181 ("sampling_rate", "sample_width", "channels"),
|
amine@426
|
182 values,
|
amine@426
|
183 )
|
amine@405
|
184 )
|
amine@400
|
185 with pytest.raises(AudioParameterError):
|
amine@400
|
186 _get_audio_parameters(params)
|
amine@145
|
187
|
amine@145
|
188
|
amine@400
|
189 @pytest.mark.parametrize(
|
amine@400
|
190 "filename, audio_format, funtion_name, kwargs",
|
amine@400
|
191 [
|
amine@400
|
192 (
|
amine@120
|
193 "audio",
|
amine@120
|
194 "raw",
|
amine@120
|
195 "_load_raw",
|
amine@120
|
196 AUDIO_PARAMS_SHORT,
|
amine@400
|
197 ), # raw_with_audio_format
|
amine@400
|
198 (
|
amine@120
|
199 "audio.raw",
|
amine@120
|
200 None,
|
amine@120
|
201 "_load_raw",
|
amine@120
|
202 AUDIO_PARAMS_SHORT,
|
amine@400
|
203 ), # raw_with_extension
|
amine@400
|
204 ("audio", "wave", "_load_wave", None), # wave_with_audio_format
|
amine@400
|
205 ("audio", "wave", "_load_wave", None), # wav_with_audio_format
|
amine@400
|
206 ("audio.wav", None, "_load_wave", None), # wav_with_extension
|
amine@400
|
207 (
|
amine@400
|
208 "audio.dat",
|
amine@400
|
209 "wav",
|
amine@400
|
210 "_load_wave",
|
amine@400
|
211 None,
|
amine@400
|
212 ), # format_and_extension_both_given_a
|
amine@400
|
213 (
|
amine@400
|
214 "audio.raw",
|
amine@400
|
215 "wave",
|
amine@400
|
216 "_load_wave",
|
amine@400
|
217 None,
|
amine@400
|
218 ), # format_and_extension_both_given_b
|
amine@400
|
219 ("audio", None, "_load_with_pydub", None), # no_format_nor_extension
|
amine@400
|
220 ("audio.ogg", None, "_load_with_pydub", None), # other_formats_ogg
|
amine@400
|
221 ("audio", "webm", "_load_with_pydub", None), # other_formats_webm
|
amine@400
|
222 ],
|
amine@400
|
223 ids=[
|
amine@400
|
224 "raw_with_audio_format",
|
amine@400
|
225 "raw_with_extension",
|
amine@400
|
226 "wave_with_audio_format",
|
amine@400
|
227 "wav_with_audio_format",
|
amine@400
|
228 "wav_with_extension",
|
amine@400
|
229 "format_and_extension_both_given_a",
|
amine@400
|
230 "format_and_extension_both_given_b",
|
amine@400
|
231 "no_format_nor_extension",
|
amine@400
|
232 "other_formats_ogg",
|
amine@400
|
233 "other_formats_webm",
|
amine@400
|
234 ],
|
amine@400
|
235 )
|
amine@400
|
236 def test_from_file(filename, audio_format, funtion_name, kwargs):
|
amine@400
|
237 funtion_name = "auditok.io." + funtion_name
|
amine@400
|
238 if kwargs is None:
|
amine@400
|
239 kwargs = {}
|
amine@400
|
240 with patch(funtion_name) as patch_function:
|
amine@400
|
241 from_file(filename, audio_format, **kwargs)
|
amine@400
|
242 assert patch_function.called
|
amine@400
|
243
|
amine@400
|
244
|
amine@406
|
245 @pytest.mark.parametrize(
|
amine@406
|
246 "large_file, cls, size, use_pathlib",
|
amine@406
|
247 [
|
amine@406
|
248 (False, BufferAudioSource, -1, False), # large_file_false_negative_size
|
amine@406
|
249 (False, BufferAudioSource, None, False), # large_file_false_None_size
|
amine@407
|
250 (
|
amine@407
|
251 False,
|
amine@407
|
252 BufferAudioSource,
|
amine@407
|
253 None,
|
amine@407
|
254 True,
|
amine@407
|
255 ), # large_file_false_None_size_Path
|
amine@406
|
256 (True, RawAudioSource, -1, False), # large_file_true_negative_size
|
amine@406
|
257 (True, RawAudioSource, None, False), # large_file_true_None_size
|
amine@406
|
258 (True, RawAudioSource, -1, True), # large_file_true_negative_size_Path
|
amine@406
|
259 ],
|
amine@406
|
260 ids=[
|
amine@406
|
261 "large_file_false_negative_size",
|
amine@406
|
262 "large_file_false_None_size",
|
amine@407
|
263 "large_file_false_None_size_Path",
|
amine@406
|
264 "large_file_true_negative_size",
|
amine@406
|
265 "large_file_true_None_size",
|
amine@406
|
266 "large_file_true_negative_size_Path",
|
amine@406
|
267 ],
|
amine@406
|
268 )
|
amine@406
|
269 def test_from_file_raw_read_all(large_file, cls, size, use_pathlib):
|
amine@406
|
270 filename = Path("tests/data/test_16KHZ_mono_400Hz.raw")
|
amine@406
|
271 if use_pathlib:
|
amine@406
|
272 filename = Path(filename)
|
amine@400
|
273 audio_source = from_file(
|
amine@400
|
274 filename,
|
amine@406
|
275 large_file=large_file,
|
amine@400
|
276 sampling_rate=16000,
|
amine@400
|
277 sample_width=2,
|
amine@400
|
278 channels=1,
|
amine@120
|
279 )
|
amine@406
|
280 assert isinstance(audio_source, cls)
|
amine@120
|
281
|
amine@406
|
282 with open(filename, "rb") as fp:
|
amine@406
|
283 expected = fp.read()
|
amine@406
|
284 audio_source.open()
|
amine@406
|
285 data = audio_source.read(size)
|
amine@406
|
286 audio_source.close()
|
amine@406
|
287 assert data == expected
|
amine@162
|
288
|
amine@406
|
289
|
amine@406
|
290 @pytest.mark.parametrize(
|
amine@406
|
291 "large_file, cls, size, use_pathlib",
|
amine@406
|
292 [
|
amine@406
|
293 (False, BufferAudioSource, -1, False), # large_file_false_negative_size
|
amine@406
|
294 (False, BufferAudioSource, None, False), # large_file_false_None_size
|
amine@407
|
295 (
|
amine@407
|
296 False,
|
amine@407
|
297 BufferAudioSource,
|
amine@407
|
298 None,
|
amine@407
|
299 True,
|
amine@407
|
300 ), # large_file_false_None_size_Path
|
amine@406
|
301 (True, WaveAudioSource, -1, False), # large_file_true_negative_size
|
amine@406
|
302 (True, WaveAudioSource, None, False), # large_file_true_None_size
|
amine@406
|
303 (True, WaveAudioSource, -1, True), # large_file_true_negative_size_Path
|
amine@406
|
304 ],
|
amine@406
|
305 ids=[
|
amine@406
|
306 "large_file_false_negative_size",
|
amine@406
|
307 "large_file_false_None_size",
|
amine@407
|
308 "large_file_false_None_size_Path",
|
amine@406
|
309 "large_file_true_negative_size",
|
amine@406
|
310 "large_file_true_None_size",
|
amine@406
|
311 "large_file_true_negative_size_Path",
|
amine@406
|
312 ],
|
amine@406
|
313 )
|
amine@406
|
314 def test_from_file_wave_read_all(large_file, cls, size, use_pathlib):
|
amine@400
|
315 filename = "tests/data/test_16KHZ_mono_400Hz.wav"
|
amine@406
|
316 if use_pathlib:
|
amine@406
|
317 filename = Path(filename)
|
amine@406
|
318 audio_source = from_file(
|
amine@406
|
319 filename,
|
amine@406
|
320 large_file=large_file,
|
amine@406
|
321 sampling_rate=16000,
|
amine@406
|
322 sample_width=2,
|
amine@406
|
323 channels=1,
|
amine@406
|
324 )
|
amine@406
|
325 assert isinstance(audio_source, cls)
|
amine@406
|
326
|
amine@406
|
327 with wave.open(str(filename)) as fp:
|
amine@406
|
328 expected = fp.readframes(-1)
|
amine@406
|
329 audio_source.open()
|
amine@406
|
330 data = audio_source.read(size)
|
amine@406
|
331 audio_source.close()
|
amine@406
|
332 assert data == expected
|
amine@163
|
333
|
amine@162
|
334
|
amine@400
|
335 def test_from_file_large_file_compressed():
|
amine@400
|
336 filename = "tests/data/test_16KHZ_mono_400Hz.ogg"
|
amine@400
|
337 with pytest.raises(AudioIOError):
|
amine@400
|
338 from_file(filename, large_file=True)
|
amine@137
|
339
|
amine@121
|
340
|
amine@400
|
341 @pytest.mark.parametrize(
|
amine@400
|
342 "missing_param",
|
amine@400
|
343 [
|
amine@400
|
344 "sr", # missing_sampling_rate
|
amine@400
|
345 "sw", # missing_sample_width
|
amine@400
|
346 "ch", # missing_channels
|
amine@400
|
347 ],
|
amine@400
|
348 ids=["missing_sampling_rate", "missing_sample_width", "missing_channels"],
|
amine@400
|
349 )
|
amine@400
|
350 def test_from_file_missing_audio_param(missing_param):
|
amine@405
|
351 params = AUDIO_PARAMS_SHORT.copy()
|
amine@405
|
352 del params[missing_param]
|
amine@400
|
353 with pytest.raises(AudioParameterError):
|
amine@400
|
354 from_file("audio", audio_format="raw", **params)
|
amine@240
|
355
|
amine@400
|
356
|
amine@400
|
357 def test_from_file_no_pydub():
|
amine@400
|
358 with patch("auditok.io._WITH_PYDUB", False):
|
amine@400
|
359 with pytest.raises(AudioIOError):
|
amine@400
|
360 from_file("audio", "mp3")
|
amine@400
|
361
|
amine@400
|
362
|
amine@400
|
363 @pytest.mark.parametrize(
|
amine@400
|
364 "audio_format, function",
|
amine@400
|
365 [
|
amine@400
|
366 ("ogg", "from_ogg"), # ogg_first_channel
|
amine@400
|
367 ("ogg", "from_ogg"), # ogg_second_channel
|
amine@400
|
368 ("ogg", "from_ogg"), # ogg_mix
|
amine@400
|
369 ("ogg", "from_ogg"), # ogg_default
|
amine@400
|
370 ("mp3", "from_mp3"), # mp3_left_channel
|
amine@400
|
371 ("mp3", "from_mp3"), # mp3_right_channel
|
amine@400
|
372 ("flac", "from_file"), # flac_first_channel
|
amine@400
|
373 ("flac", "from_file"), # flac_second_channel
|
amine@400
|
374 ("flv", "from_flv"), # flv_left_channel
|
amine@400
|
375 ("webm", "from_file"), # webm_right_channel
|
amine@400
|
376 ],
|
amine@400
|
377 ids=[
|
amine@400
|
378 "ogg_first_channel",
|
amine@400
|
379 "ogg_second_channel",
|
amine@400
|
380 "ogg_mix",
|
amine@400
|
381 "ogg_default",
|
amine@400
|
382 "mp3_left_channel",
|
amine@400
|
383 "mp3_right_channel",
|
amine@400
|
384 "flac_first_channel",
|
amine@400
|
385 "flac_second_channel",
|
amine@400
|
386 "flv_left_channel",
|
amine@400
|
387 "webm_right_channel",
|
amine@400
|
388 ],
|
amine@400
|
389 )
|
amine@400
|
390 @patch("auditok.io._WITH_PYDUB", True)
|
amine@400
|
391 @patch("auditok.io.BufferAudioSource")
|
amine@400
|
392 def test_from_file_multichannel_audio_compressed(
|
amine@400
|
393 mock_buffer_audio_source, audio_format, function
|
amine@400
|
394 ):
|
amine@400
|
395 filename = "audio.{}".format(audio_format)
|
amine@400
|
396 segment_mock = Mock()
|
amine@400
|
397 segment_mock.sample_width = 2
|
amine@400
|
398 segment_mock.channels = 2
|
amine@400
|
399 segment_mock._data = b"abcd"
|
amine@400
|
400 with patch("auditok.io.AudioSegment.{}".format(function)) as open_func:
|
amine@400
|
401 open_func.return_value = segment_mock
|
amine@400
|
402 from_file(filename)
|
amine@400
|
403 assert open_func.called
|
amine@400
|
404
|
amine@400
|
405
|
amine@400
|
406 @pytest.mark.parametrize(
|
amine@400
|
407 "file_id, frequencies, large_file",
|
amine@400
|
408 [
|
amine@400
|
409 ("mono_400", (400,), False), # mono
|
amine@400
|
410 ("3channel_400-800-1600", (400, 800, 1600), False), # three_channel
|
amine@400
|
411 ("mono_400", (400,), True), # mono_large_file
|
amine@400
|
412 (
|
amine@313
|
413 "3channel_400-800-1600",
|
amine@313
|
414 (400, 800, 1600),
|
amine@313
|
415 True,
|
amine@400
|
416 ), # three_channel_large_file
|
amine@400
|
417 ],
|
amine@400
|
418 ids=[
|
amine@400
|
419 "mono",
|
amine@400
|
420 "three_channel",
|
amine@400
|
421 "mono_large_file",
|
amine@400
|
422 "three_channel_large_file",
|
amine@400
|
423 ],
|
amine@400
|
424 )
|
amine@400
|
425 def test_load_raw(file_id, frequencies, large_file):
|
amine@400
|
426 filename = "tests/data/test_16KHZ_{}Hz.raw".format(file_id)
|
amine@400
|
427 audio_source = _load_raw(
|
amine@400
|
428 filename, 16000, 2, len(frequencies), large_file=large_file
|
amine@126
|
429 )
|
amine@400
|
430 audio_source.open()
|
amine@400
|
431 data = audio_source.read(-1)
|
amine@400
|
432 audio_source.close()
|
amine@400
|
433 expected_class = RawAudioSource if large_file else BufferAudioSource
|
amine@400
|
434 assert isinstance(audio_source, expected_class)
|
amine@400
|
435 assert audio_source.sampling_rate == 16000
|
amine@400
|
436 assert audio_source.sample_width == 2
|
amine@400
|
437 assert audio_source.channels == len(frequencies)
|
amine@400
|
438 mono_channels = [PURE_TONE_DICT[freq] for freq in frequencies]
|
amine@405
|
439 dtype = SAMPLE_WIDTH_TO_DTYPE[audio_source.sample_width]
|
amine@405
|
440 expected = np.fromiter(
|
amine@405
|
441 _sample_generator(*mono_channels), dtype=dtype
|
amine@405
|
442 ).tobytes()
|
amine@400
|
443 assert data == expected
|
amine@126
|
444
|
amine@128
|
445
|
amine@405
|
446 def test_load_raw_missing_audio_param():
|
amine@400
|
447 with pytest.raises(AudioParameterError):
|
amine@405
|
448 _load_raw("audio", sampling_rate=None, sample_width=1, channels=1)
|
amine@405
|
449
|
amine@405
|
450 with pytest.raises(AudioParameterError):
|
amine@405
|
451 _load_raw("audio", sampling_rate=16000, sample_width=None, channels=1)
|
amine@405
|
452
|
amine@405
|
453 with pytest.raises(AudioParameterError):
|
amine@405
|
454 _load_raw("audio", sampling_rate=16000, sample_width=1, channels=None)
|
amine@400
|
455
|
amine@400
|
456
|
amine@400
|
457 @pytest.mark.parametrize(
|
amine@400
|
458 "file_id, frequencies, large_file",
|
amine@400
|
459 [
|
amine@400
|
460 ("mono_400", (400,), False), # mono
|
amine@400
|
461 ("3channel_400-800-1600", (400, 800, 1600), False), # three_channel
|
amine@400
|
462 ("mono_400", (400,), True), # mono_large_file
|
amine@400
|
463 (
|
amine@313
|
464 "3channel_400-800-1600",
|
amine@313
|
465 (400, 800, 1600),
|
amine@313
|
466 True,
|
amine@400
|
467 ), # three_channel_large_file
|
amine@400
|
468 ],
|
amine@400
|
469 ids=[
|
amine@400
|
470 "mono",
|
amine@400
|
471 "three_channel",
|
amine@400
|
472 "mono_large_file",
|
amine@400
|
473 "three_channel_large_file",
|
amine@400
|
474 ],
|
amine@400
|
475 )
|
amine@400
|
476 def test_load_wave(file_id, frequencies, large_file):
|
amine@400
|
477 filename = "tests/data/test_16KHZ_{}Hz.wav".format(file_id)
|
amine@400
|
478 audio_source = _load_wave(filename, large_file=large_file)
|
amine@400
|
479 audio_source.open()
|
amine@400
|
480 data = audio_source.read(-1)
|
amine@400
|
481 audio_source.close()
|
amine@400
|
482 expected_class = WaveAudioSource if large_file else BufferAudioSource
|
amine@400
|
483 assert isinstance(audio_source, expected_class)
|
amine@400
|
484 assert audio_source.sampling_rate == 16000
|
amine@400
|
485 assert audio_source.sample_width == 2
|
amine@400
|
486 assert audio_source.channels == len(frequencies)
|
amine@400
|
487 mono_channels = [PURE_TONE_DICT[freq] for freq in frequencies]
|
amine@405
|
488 dtype = SAMPLE_WIDTH_TO_DTYPE[audio_source.sample_width]
|
amine@405
|
489 expected = np.fromiter(
|
amine@405
|
490 _sample_generator(*mono_channels), dtype=dtype
|
amine@405
|
491 ).tobytes()
|
amine@400
|
492 assert data == expected
|
amine@400
|
493
|
amine@400
|
494
|
amine@400
|
495 @pytest.mark.parametrize(
|
amine@400
|
496 "audio_format, channels, function",
|
amine@400
|
497 [
|
amine@400
|
498 ("ogg", 2, "from_ogg"), # ogg_default_first_channel
|
amine@400
|
499 ("ogg", 1, "from_ogg"), # ogg_first_channel
|
amine@400
|
500 ("ogg", 2, "from_ogg"), # ogg_second_channel
|
amine@400
|
501 ("ogg", 3, "from_ogg"), # ogg_mix_channels
|
amine@400
|
502 ("mp3", 1, "from_mp3"), # mp3_left_channel
|
amine@400
|
503 ("mp3", 2, "from_mp3"), # mp3_right_channel
|
amine@400
|
504 ("mp3", 3, "from_mp3"), # mp3_mix_channels
|
amine@400
|
505 ("flac", 2, "from_file"), # flac_first_channel
|
amine@400
|
506 ("flac", 2, "from_file"), # flac_second_channel
|
amine@400
|
507 ("flv", 1, "from_flv"), # flv_left_channel
|
amine@400
|
508 ("webm", 2, "from_file"), # webm_right_channel
|
amine@400
|
509 ("webm", 4, "from_file"), # webm_mix_channels
|
amine@400
|
510 ],
|
amine@400
|
511 ids=[
|
amine@400
|
512 "ogg_default_first_channel",
|
amine@400
|
513 "ogg_first_channel",
|
amine@400
|
514 "ogg_second_channel",
|
amine@400
|
515 "ogg_mix_channels",
|
amine@400
|
516 "mp3_left_channel",
|
amine@400
|
517 "mp3_right_channel",
|
amine@400
|
518 "mp3_mix_channels",
|
amine@400
|
519 "flac_first_channel",
|
amine@400
|
520 "flac_second_channel",
|
amine@400
|
521 "flv_left_channel",
|
amine@400
|
522 "webm_right_channel",
|
amine@400
|
523 "webm_mix_channels",
|
amine@400
|
524 ],
|
amine@400
|
525 )
|
amine@400
|
526 @patch("auditok.io._WITH_PYDUB", True)
|
amine@400
|
527 @patch("auditok.io.BufferAudioSource")
|
amine@400
|
528 def test_load_with_pydub(
|
amine@400
|
529 mock_buffer_audio_source, audio_format, channels, function
|
amine@400
|
530 ):
|
amine@400
|
531 filename = "audio.{}".format(audio_format)
|
amine@400
|
532 segment_mock = Mock()
|
amine@400
|
533 segment_mock.sample_width = 2
|
amine@400
|
534 segment_mock.channels = channels
|
amine@400
|
535 segment_mock._data = b"abcdefgh"
|
amine@400
|
536 with patch("auditok.io.AudioSegment.{}".format(function)) as open_func:
|
amine@400
|
537 open_func.return_value = segment_mock
|
amine@400
|
538 _load_with_pydub(filename, audio_format)
|
amine@400
|
539 assert open_func.called
|
amine@400
|
540
|
amine@400
|
541
|
amine@400
|
542 @pytest.mark.parametrize(
|
amine@406
|
543 "filename, frequencies, use_pathlib",
|
amine@400
|
544 [
|
amine@406
|
545 ("mono_400Hz.raw", (400,), False), # mono
|
amine@406
|
546 ("mono_400Hz.raw", (400,), True), # mono_pathlib
|
amine@406
|
547 (
|
amine@406
|
548 "3channel_400-800-1600Hz.raw",
|
amine@406
|
549 (400, 800, 1600),
|
amine@406
|
550 False,
|
amine@406
|
551 ), # three_channel
|
amine@400
|
552 ],
|
amine@406
|
553 ids=["mono", "three_channel", "use_pathlib"],
|
amine@400
|
554 )
|
amine@406
|
555 def test_save_raw(filename, frequencies, use_pathlib):
|
amine@400
|
556 filename = "tests/data/test_16KHZ_{}".format(filename)
|
amine@406
|
557 if use_pathlib:
|
amine@406
|
558 filename = Path(filename)
|
amine@400
|
559 sample_width = 2
|
amine@405
|
560 dtype = SAMPLE_WIDTH_TO_DTYPE[sample_width]
|
amine@400
|
561 mono_channels = [PURE_TONE_DICT[freq] for freq in frequencies]
|
amine@405
|
562 data = np.fromiter(_sample_generator(*mono_channels), dtype=dtype).tobytes()
|
amine@400
|
563 tmpfile = NamedTemporaryFile()
|
amine@400
|
564 _save_raw(data, tmpfile.name)
|
amine@400
|
565 assert filecmp.cmp(tmpfile.name, filename, shallow=False)
|
amine@400
|
566
|
amine@400
|
567
|
amine@400
|
568 @pytest.mark.parametrize(
|
amine@406
|
569 "filename, frequencies, use_pathlib",
|
amine@400
|
570 [
|
amine@406
|
571 ("mono_400Hz.wav", (400,), False), # mono
|
amine@406
|
572 ("mono_400Hz.wav", (400,), True), # mono_pathlib
|
amine@406
|
573 (
|
amine@406
|
574 "3channel_400-800-1600Hz.wav",
|
amine@406
|
575 (400, 800, 1600),
|
amine@406
|
576 False,
|
amine@406
|
577 ), # three_channel
|
amine@400
|
578 ],
|
amine@406
|
579 ids=["mono", "mono_pathlib", "three_channel"],
|
amine@400
|
580 )
|
amine@406
|
581 def test_save_wave(filename, frequencies, use_pathlib):
|
amine@400
|
582 filename = "tests/data/test_16KHZ_{}".format(filename)
|
amine@406
|
583 if use_pathlib:
|
amine@406
|
584 filename = str(filename)
|
amine@400
|
585 sampling_rate = 16000
|
amine@400
|
586 sample_width = 2
|
amine@400
|
587 channels = len(frequencies)
|
amine@400
|
588 mono_channels = [PURE_TONE_DICT[freq] for freq in frequencies]
|
amine@405
|
589 dtype = SAMPLE_WIDTH_TO_DTYPE[sample_width]
|
amine@405
|
590 data = np.fromiter(_sample_generator(*mono_channels), dtype=dtype).tobytes()
|
amine@400
|
591 tmpfile = NamedTemporaryFile()
|
amine@400
|
592 _save_wave(data, tmpfile.name, sampling_rate, sample_width, channels)
|
amine@400
|
593 assert filecmp.cmp(tmpfile.name, filename, shallow=False)
|
amine@400
|
594
|
amine@400
|
595
|
amine@400
|
596 @pytest.mark.parametrize(
|
amine@400
|
597 "missing_param",
|
amine@400
|
598 [
|
amine@400
|
599 "sr", # missing_sampling_rate
|
amine@400
|
600 "sw", # missing_sample_width
|
amine@400
|
601 "ch", # missing_channels
|
amine@400
|
602 ],
|
amine@400
|
603 ids=["missing_sampling_rate", "missing_sample_width", "missing_channels"],
|
amine@400
|
604 )
|
amine@400
|
605 def test_save_wave_missing_audio_param(missing_param):
|
amine@400
|
606 with pytest.raises(AudioParameterError):
|
amine@405
|
607 _save_wave(
|
amine@405
|
608 b"\0\0", "audio", sampling_rate=None, sample_width=1, channels=1
|
amine@405
|
609 )
|
amine@405
|
610
|
amine@405
|
611 with pytest.raises(AudioParameterError):
|
amine@405
|
612 _save_wave(
|
amine@405
|
613 b"\0\0", "audio", sampling_rate=16000, sample_width=None, channels=1
|
amine@405
|
614 )
|
amine@405
|
615
|
amine@405
|
616 with pytest.raises(AudioParameterError):
|
amine@405
|
617 _save_wave(
|
amine@405
|
618 b"\0\0", "audio", sampling_rate=16000, sample_width=1, channels=None
|
amine@405
|
619 )
|
amine@400
|
620
|
amine@400
|
621
|
amine@400
|
622 def test_save_with_pydub():
|
amine@400
|
623 with patch("auditok.io.AudioSegment.export") as export:
|
amine@400
|
624 tmpdir = TemporaryDirectory()
|
amine@400
|
625 filename = os.path.join(tmpdir.name, "audio.ogg")
|
amine@400
|
626 _save_with_pydub(b"\0\0", filename, "ogg", 16000, 2, 1)
|
amine@400
|
627 assert export.called
|
amine@400
|
628 tmpdir.cleanup()
|
amine@400
|
629
|
amine@400
|
630
|
amine@400
|
631 @pytest.mark.parametrize(
|
amine@400
|
632 "filename, audio_format",
|
amine@400
|
633 [
|
amine@400
|
634 ("audio", "raw"), # raw_with_audio_format
|
amine@400
|
635 ("audio.raw", None), # raw_with_extension
|
amine@400
|
636 ("audio.mp3", "raw"), # raw_with_audio_format_and_extension
|
amine@400
|
637 ("audio", None), # raw_no_audio_format_nor_extension
|
amine@400
|
638 ],
|
amine@400
|
639 ids=[
|
amine@400
|
640 "raw_with_audio_format",
|
amine@400
|
641 "raw_with_extension",
|
amine@400
|
642 "raw_with_audio_format_and_extension",
|
amine@400
|
643 "raw_no_audio_format_nor_extension",
|
amine@400
|
644 ],
|
amine@400
|
645 )
|
amine@400
|
646 def test_to_file_raw(filename, audio_format):
|
amine@400
|
647 exp_filename = "tests/data/test_16KHZ_mono_400Hz.raw"
|
amine@400
|
648 tmpdir = TemporaryDirectory()
|
amine@400
|
649 filename = os.path.join(tmpdir.name, filename)
|
amine@400
|
650 data = PURE_TONE_DICT[400].tobytes()
|
amine@400
|
651 to_file(data, filename, audio_format=audio_format)
|
amine@400
|
652 assert filecmp.cmp(filename, exp_filename, shallow=False)
|
amine@400
|
653 tmpdir.cleanup()
|
amine@400
|
654
|
amine@400
|
655
|
amine@400
|
656 @pytest.mark.parametrize(
|
amine@400
|
657 "filename, audio_format",
|
amine@400
|
658 [
|
amine@400
|
659 ("audio", "wav"), # wav_with_audio_format
|
amine@400
|
660 ("audio.wav", None), # wav_with_extension
|
amine@400
|
661 ("audio.mp3", "wav"), # wav_with_audio_format_and_extension
|
amine@400
|
662 ("audio", "wave"), # wave_with_audio_format
|
amine@400
|
663 ("audio.wave", None), # wave_with_extension
|
amine@400
|
664 ("audio.mp3", "wave"), # wave_with_audio_format_and_extension
|
amine@400
|
665 ],
|
amine@400
|
666 ids=[
|
amine@400
|
667 "wav_with_audio_format",
|
amine@400
|
668 "wav_with_extension",
|
amine@400
|
669 "wav_with_audio_format_and_extension",
|
amine@400
|
670 "wave_with_audio_format",
|
amine@400
|
671 "wave_with_extension",
|
amine@400
|
672 "wave_with_audio_format_and_extension",
|
amine@400
|
673 ],
|
amine@400
|
674 )
|
amine@400
|
675 def test_to_file_wave(filename, audio_format):
|
amine@400
|
676 exp_filename = "tests/data/test_16KHZ_mono_400Hz.wav"
|
amine@400
|
677 tmpdir = TemporaryDirectory()
|
amine@400
|
678 filename = os.path.join(tmpdir.name, filename)
|
amine@400
|
679 data = PURE_TONE_DICT[400].tobytes()
|
amine@400
|
680 to_file(
|
amine@400
|
681 data,
|
amine@400
|
682 filename,
|
amine@400
|
683 audio_format=audio_format,
|
amine@400
|
684 sampling_rate=16000,
|
amine@400
|
685 sample_width=2,
|
amine@400
|
686 channels=1,
|
amine@129
|
687 )
|
amine@400
|
688 assert filecmp.cmp(filename, exp_filename, shallow=False)
|
amine@400
|
689 tmpdir.cleanup()
|
amine@129
|
690
|
amine@240
|
691
|
amine@400
|
692 @pytest.mark.parametrize(
|
amine@400
|
693 "missing_param",
|
amine@400
|
694 [
|
amine@400
|
695 "sr", # missing_sampling_rate
|
amine@400
|
696 "sw", # missing_sample_width
|
amine@400
|
697 "ch", # missing_channels
|
amine@400
|
698 ],
|
amine@400
|
699 ids=["missing_sampling_rate", "missing_sample_width", "missing_channels"],
|
amine@400
|
700 )
|
amine@400
|
701 def test_to_file_missing_audio_param(missing_param):
|
amine@400
|
702 params = AUDIO_PARAMS_SHORT.copy()
|
amine@400
|
703 del params[missing_param]
|
amine@400
|
704 with pytest.raises(AudioParameterError):
|
amine@400
|
705 to_file(b"\0\0", "audio", audio_format="wav", **params)
|
amine@400
|
706 with pytest.raises(AudioParameterError):
|
amine@400
|
707 to_file(b"\0\0", "audio", audio_format="mp3", **params)
|
amine@132
|
708
|
amine@132
|
709
|
amine@400
|
710 def test_to_file_no_pydub():
|
amine@400
|
711 with patch("auditok.io._WITH_PYDUB", False):
|
amine@400
|
712 with pytest.raises(AudioIOError):
|
amine@400
|
713 to_file("audio", b"", "mp3")
|
amine@133
|
714
|
amine@141
|
715
|
amine@400
|
716 @pytest.mark.parametrize(
|
amine@400
|
717 "filename, audio_format",
|
amine@400
|
718 [
|
amine@400
|
719 ("audio.ogg", None), # ogg_with_extension
|
amine@400
|
720 ("audio", "ogg"), # ogg_with_audio_format
|
amine@400
|
721 ("audio.wav", "ogg"), # ogg_format_with_wrong_extension
|
amine@400
|
722 ],
|
amine@400
|
723 ids=[
|
amine@400
|
724 "ogg_with_extension",
|
amine@400
|
725 "ogg_with_audio_format",
|
amine@400
|
726 "ogg_format_with_wrong_extension",
|
amine@400
|
727 ],
|
amine@400
|
728 )
|
amine@400
|
729 @patch("auditok.io._WITH_PYDUB", True)
|
amine@400
|
730 def test_to_file_compressed(filename, audio_format):
|
amine@400
|
731 with patch("auditok.io.AudioSegment.export") as export:
|
amine@133
|
732 tmpdir = TemporaryDirectory()
|
amine@133
|
733 filename = os.path.join(tmpdir.name, filename)
|
amine@400
|
734 to_file(b"\0\0", filename, audio_format, **AUDIO_PARAMS_SHORT)
|
amine@400
|
735 assert export.called
|
amine@133
|
736 tmpdir.cleanup()
|
amine@134
|
737
|
amine@138
|
738
|
amine@400
|
739 @pytest.mark.parametrize(
|
amine@400
|
740 "input, expected_type, extra_args",
|
amine@400
|
741 [
|
amine@400
|
742 (
|
amine@190
|
743 "tests/data/test_16KHZ_mono_400Hz.wav",
|
amine@190
|
744 BufferAudioSource,
|
amine@400
|
745 None,
|
amine@400
|
746 ), # string_wave
|
amine@400
|
747 (
|
amine@190
|
748 "tests/data/test_16KHZ_mono_400Hz.wav",
|
amine@190
|
749 WaveAudioSource,
|
amine@190
|
750 {"large_file": True},
|
amine@400
|
751 ), # string_wave_large_file
|
amine@400
|
752 ("-", StdinAudioSource, None), # stdin
|
amine@400
|
753 (
|
amine@400
|
754 "tests/data/test_16KHZ_mono_400Hz.raw",
|
amine@400
|
755 BufferAudioSource,
|
amine@400
|
756 None,
|
amine@400
|
757 ), # string_raw
|
amine@400
|
758 (
|
amine@190
|
759 "tests/data/test_16KHZ_mono_400Hz.raw",
|
amine@190
|
760 RawAudioSource,
|
amine@190
|
761 {"large_file": True},
|
amine@400
|
762 ), # string_raw_large_file
|
amine@400
|
763 (b"0" * 8000, BufferAudioSource, None), # bytes_
|
amine@400
|
764 ],
|
amine@400
|
765 ids=[
|
amine@400
|
766 "string_wave",
|
amine@400
|
767 "string_wave_large_file",
|
amine@400
|
768 "stdin",
|
amine@400
|
769 "string_raw",
|
amine@400
|
770 "string_raw_large_file",
|
amine@400
|
771 "bytes_",
|
amine@400
|
772 ],
|
amine@400
|
773 )
|
amine@400
|
774 def test_get_audio_source(input, expected_type, extra_args):
|
amine@400
|
775 kwargs = {"sampling_rate": 16000, "sample_width": 2, "channels": 1}
|
amine@400
|
776 if extra_args is not None:
|
amine@400
|
777 kwargs.update(extra_args)
|
amine@400
|
778 audio_source = get_audio_source(input, **kwargs)
|
amine@400
|
779 assert isinstance(audio_source, expected_type)
|
amine@403
|
780 assert audio_source.sampling_rate == 16000, (
|
amine@403
|
781 "Unexpected sampling rate: audio_source.sampling_rate = "
|
amine@403
|
782 + f"{audio_source.sampling_rate} instead of 16000"
|
amine@403
|
783 )
|
amine@403
|
784 assert audio_source.sr == 16000, (
|
amine@403
|
785 "Unexpected sampling rate: audio_source.sr = "
|
amine@403
|
786 + f"{audio_source.sr} instead of 16000"
|
amine@403
|
787 )
|
amine@403
|
788 assert audio_source.sample_width == 2, (
|
amine@403
|
789 "Unexpected sample width: audio_source.sample_width = "
|
amine@403
|
790 + f"{audio_source.sample_width} instead of 2"
|
amine@403
|
791 )
|
amine@403
|
792 assert audio_source.sw == 2, (
|
amine@403
|
793 "Unexpected sample width: audio_source.sw = "
|
amine@403
|
794 + f"{audio_source.sw} instead of 2"
|
amine@403
|
795 )
|
amine@403
|
796 assert audio_source.channels == 1, (
|
amine@403
|
797 "Unexpected number of channels: audio_source.channels = "
|
amine@403
|
798 + f"{audio_source.channels} instead of 1"
|
amine@403
|
799 )
|
amine@403
|
800 assert audio_source.ch == 1, (
|
amine@403
|
801 "Unexpected number of channels: audio_source.ch = "
|
amine@403
|
802 + f"{audio_source.ch} instead of 1"
|
amine@403
|
803 )
|
amine@403
|
804
|
amine@403
|
805
|
amine@403
|
806 def test_get_audio_source_alias_prams():
|
amine@403
|
807 audio_source = get_audio_source(b"0" * 1600, sr=16000, sw=2, ch=1)
|
amine@403
|
808 assert audio_source.sampling_rate == 16000, (
|
amine@403
|
809 "Unexpected sampling rate: audio_source.sampling_rate = "
|
amine@403
|
810 + f"{audio_source.sampling_rate} instead of 16000"
|
amine@403
|
811 )
|
amine@403
|
812 assert audio_source.sr == 16000, (
|
amine@403
|
813 "Unexpected sampling rate: audio_source.sr = "
|
amine@403
|
814 + f"{audio_source.sr} instead of 16000"
|
amine@403
|
815 )
|
amine@403
|
816 assert audio_source.sample_width == 2, (
|
amine@403
|
817 "Unexpected sample width: audio_source.sample_width = "
|
amine@403
|
818 + f"{audio_source.sample_width} instead of 2"
|
amine@403
|
819 )
|
amine@403
|
820 assert audio_source.sw == 2, (
|
amine@403
|
821 "Unexpected sample width: audio_source.sw = "
|
amine@403
|
822 + f"{audio_source.sw} instead of 2"
|
amine@403
|
823 )
|
amine@403
|
824 assert audio_source.channels == 1, (
|
amine@403
|
825 "Unexpected number of channels: audio_source.channels = "
|
amine@403
|
826 + f"{audio_source.channels} instead of 1"
|
amine@403
|
827 )
|
amine@403
|
828 assert audio_source.ch == 1, (
|
amine@403
|
829 "Unexpected number of channels: audio_source.ch = "
|
amine@403
|
830 + f"{audio_source.ch} instead of 1"
|
amine@403
|
831 )
|