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