annotate tests/test_io.py @ 455:7dae98b84cdd tip master

Merge branch 'master' of https://github.com/amsehili/auditok
author www-data <www-data@c4dm-xenserv-virt2.eecs.qmul.ac.uk>
date Tue, 03 Dec 2024 09:18:01 +0000
parents c5b4178aa80f
children
rev   line source
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 )