annotate tests/test_io.py @ 420:f874558779b9

Refactor workers
author Amine Sehili <amine.sehili@gmail.com>
date Fri, 18 Oct 2024 23:32:41 +0200
parents 6c33626d0bff
children c5b4178aa80f
rev   line source
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 )