annotate tests/test_core.py @ 397:c89c0977db47

Update README.rst
author Amine SEHILI <amsehili@users.noreply.github.com>
date Thu, 30 Mar 2023 10:31:43 +0200
parents 8d3e2b492c6f
children 323d59b404a2
rev   line source
amine@192 1 import os
amine@221 2 import math
amine@88 3 from random import random
amine@192 4 from tempfile import TemporaryDirectory
amine@252 5 from array import array as array_
amine@337 6 import unittest
amine@324 7 from unittest import TestCase, mock
amine@323 8 from unittest.mock import patch
amine@86 9 from genty import genty, genty_dataset
amine@371 10 from auditok import load, split, AudioRegion, AudioParameterError
amine@323 11 from auditok.core import (
amine@323 12 _duration_to_nb_windows,
amine@323 13 _make_audio_region,
amine@323 14 _read_chunks_online,
amine@323 15 _read_offline,
amine@323 16 )
amine@212 17 from auditok.util import AudioDataSource
amine@315 18 from auditok.io import get_audio_source
amine@86 19
amine@324 20 mock._magics.add("__round__")
amine@324 21
amine@86 22
amine@299 23 def _make_random_length_regions(
amine@299 24 byte_seq, sampling_rate, sample_width, channels
amine@299 25 ):
amine@88 26 regions = []
amine@88 27 for b in byte_seq:
amine@88 28 duration = round(random() * 10, 6)
amine@95 29 data = b * int(duration * sampling_rate) * sample_width * channels
amine@244 30 region = AudioRegion(data, sampling_rate, sample_width, channels)
amine@88 31 regions.append(region)
amine@88 32 return regions
amine@88 33
amine@88 34
amine@86 35 @genty
amine@215 36 class TestFunctions(TestCase):
amine@215 37 @genty_dataset(
amine@371 38 no_skip_read_all=(0, -1),
amine@371 39 no_skip_read_all_stereo=(0, -1, 2),
amine@371 40 skip_2_read_all=(2, -1),
amine@371 41 skip_2_read_all_None=(2, None),
amine@371 42 skip_2_read_3=(2, 3),
amine@371 43 skip_2_read_3_5_stereo=(2, 3.5, 2),
amine@371 44 skip_2_4_read_3_5_stereo=(2.4, 3.5, 2),
amine@371 45 )
amine@371 46 def test_load(self, skip, max_read, channels=1):
amine@371 47 sampling_rate = 10
amine@371 48 sample_width = 2
amine@371 49 filename = "tests/data/test_split_10HZ_{}.raw"
amine@371 50 filename = filename.format("mono" if channels == 1 else "stereo")
amine@371 51 region = load(
amine@371 52 filename,
amine@371 53 skip=skip,
amine@371 54 max_read=max_read,
amine@371 55 sr=sampling_rate,
amine@371 56 sw=sample_width,
amine@371 57 ch=channels,
amine@371 58 )
amine@371 59 with open(filename, "rb") as fp:
amine@371 60 fp.read(round(skip * sampling_rate * sample_width * channels))
amine@371 61 if max_read is None or max_read < 0:
amine@371 62 to_read = -1
amine@371 63 else:
amine@371 64 to_read = round(
amine@371 65 max_read * sampling_rate * sample_width * channels
amine@371 66 )
amine@371 67 expected = fp.read(to_read)
amine@371 68 self.assertEqual(bytes(region), expected)
amine@371 69
amine@371 70 @genty_dataset(
amine@221 71 zero_duration=(0, 1, None, 0),
amine@221 72 multiple=(0.3, 0.1, round, 3),
amine@221 73 not_multiple_ceil=(0.35, 0.1, math.ceil, 4),
amine@221 74 not_multiple_floor=(0.35, 0.1, math.floor, 3),
amine@221 75 small_duration=(0.05, 0.1, round, 0),
amine@221 76 small_duration_ceil=(0.05, 0.1, math.ceil, 1),
amine@233 77 with_round_error=(0.3, 0.1, math.floor, 3, {"epsilon": 1e-6}),
amine@221 78 negative_duration=(-0.5, 0.1, math.ceil, ValueError),
amine@221 79 negative_analysis_window=(0.5, -0.1, math.ceil, ValueError),
amine@215 80 )
amine@221 81 def test_duration_to_nb_windows(
amine@232 82 self, duration, analysis_window, round_fn, expected, kwargs=None
amine@221 83 ):
amine@221 84 if expected == ValueError:
amine@215 85 with self.assertRaises(expected):
amine@221 86 _duration_to_nb_windows(duration, analysis_window, round_fn)
amine@215 87 else:
amine@232 88 if kwargs is None:
amine@232 89 kwargs = {}
amine@221 90 result = _duration_to_nb_windows(
amine@232 91 duration, analysis_window, round_fn, **kwargs
amine@221 92 )
amine@215 93 self.assertEqual(result, expected)
amine@215 94
amine@323 95 @genty_dataset(
amine@323 96 mono_skip_0_max_read_None=(1, 0, None),
amine@323 97 mono_skip_3_max_read_None=(1, 3, None),
amine@323 98 mono_skip_2_max_read_negative=(1, 2, -1),
amine@323 99 mono_skip_2_max_read_3=(1, 2, 3),
amine@323 100 stereo_skip_0_max_read_None=(2, 0, None),
amine@323 101 stereo_skip_3_max_read_None=(2, 3, None),
amine@323 102 stereo_skip_2_max_read_negative=(2, 2, -1),
amine@323 103 stereo_skip_2_max_read_3=(2, 2, 3),
amine@323 104 )
amine@323 105 def test_read_offline(self, channels, skip, max_read=None):
amine@323 106 sampling_rate = 10
amine@323 107 sample_width = 2
amine@323 108 mono_or_stereo = "mono" if channels == 1 else "stereo"
amine@323 109 filename = "tests/data/test_split_10HZ_{}.raw".format(mono_or_stereo)
amine@323 110 with open(filename, "rb") as fp:
amine@323 111 data = fp.read()
amine@323 112 onset = round(skip * sampling_rate * sample_width * channels)
amine@323 113 if max_read in (-1, None):
amine@323 114 offset = len(data) + 1
amine@323 115 else:
amine@323 116 offset = onset + round(
amine@323 117 max_read * sampling_rate * sample_width * channels
amine@323 118 )
amine@323 119 expected_data = data[onset:offset]
amine@323 120 read_data, *audio_params = _read_offline(
amine@323 121 filename,
amine@323 122 skip=skip,
amine@323 123 max_read=max_read,
amine@323 124 sr=sampling_rate,
amine@323 125 sw=sample_width,
amine@323 126 ch=channels,
amine@323 127 )
amine@323 128 self.assertEqual(read_data, expected_data)
amine@323 129 self.assertEqual(
amine@323 130 tuple(audio_params), (sampling_rate, sample_width, channels)
amine@323 131 )
amine@323 132
amine@215 133
amine@215 134 @genty
amine@207 135 class TestSplit(TestCase):
amine@207 136 @genty_dataset(
amine@299 137 simple=(
amine@299 138 0.2,
amine@299 139 5,
amine@299 140 0.2,
amine@299 141 False,
amine@299 142 False,
amine@299 143 {"eth": 50},
amine@299 144 [(2, 16), (17, 31), (34, 76)],
amine@299 145 ),
amine@214 146 short_max_dur=(
amine@214 147 0.3,
amine@214 148 2,
amine@214 149 0.2,
amine@214 150 False,
amine@214 151 False,
amine@214 152 {"eth": 50},
amine@214 153 [(2, 16), (17, 31), (34, 54), (54, 74), (74, 76)],
amine@214 154 ),
amine@214 155 long_min_dur=(3, 5, 0.2, False, False, {"eth": 50}, [(34, 76)]),
amine@214 156 long_max_silence=(0.2, 80, 10, False, False, {"eth": 50}, [(2, 76)]),
amine@214 157 zero_max_silence=(
amine@214 158 0.2,
amine@214 159 5,
amine@214 160 0.0,
amine@214 161 False,
amine@214 162 False,
amine@214 163 {"eth": 50},
amine@214 164 [(2, 14), (17, 24), (26, 29), (34, 76)],
amine@214 165 ),
amine@207 166 low_energy_threshold=(
amine@207 167 0.2,
amine@207 168 5,
amine@207 169 0.2,
amine@207 170 False,
amine@207 171 False,
amine@207 172 {"energy_threshold": 40},
amine@207 173 [(0, 50), (50, 76)],
amine@207 174 ),
amine@299 175 high_energy_threshold=(
amine@299 176 0.2,
amine@299 177 5,
amine@299 178 0.2,
amine@299 179 False,
amine@299 180 False,
amine@299 181 {"energy_threshold": 60},
amine@299 182 [],
amine@299 183 ),
amine@207 184 trim_leading_and_trailing_silence=(
amine@207 185 0.2,
amine@207 186 10, # use long max_dur
amine@207 187 0.5, # and a max_silence longer than any inter-region silence
amine@207 188 True,
amine@207 189 False,
amine@207 190 {"eth": 50},
amine@207 191 [(2, 76)],
amine@207 192 ),
amine@207 193 drop_trailing_silence=(
amine@207 194 0.2,
amine@207 195 5,
amine@207 196 0.2,
amine@207 197 True,
amine@207 198 False,
amine@207 199 {"eth": 50},
amine@207 200 [(2, 14), (17, 29), (34, 76)],
amine@207 201 ),
amine@299 202 drop_trailing_silence_2=(
amine@299 203 1.5,
amine@299 204 5,
amine@299 205 0.2,
amine@299 206 True,
amine@299 207 False,
amine@299 208 {"eth": 50},
amine@299 209 [(34, 76)],
amine@299 210 ),
amine@207 211 strict_min_dur=(
amine@207 212 0.3,
amine@207 213 2,
amine@207 214 0.2,
amine@207 215 False,
amine@207 216 True,
amine@207 217 {"eth": 50},
amine@207 218 [(2, 16), (17, 31), (34, 54), (54, 74)],
amine@207 219 ),
amine@207 220 )
amine@207 221 def test_split_params(
amine@207 222 self,
amine@207 223 min_dur,
amine@207 224 max_dur,
amine@207 225 max_silence,
amine@207 226 drop_trailing_silence,
amine@207 227 strict_min_dur,
amine@207 228 kwargs,
amine@207 229 expected,
amine@207 230 ):
amine@207 231 with open("tests/data/test_split_10HZ_mono.raw", "rb") as fp:
amine@207 232 data = fp.read()
amine@207 233
amine@207 234 regions = split(
amine@207 235 data,
amine@207 236 min_dur,
amine@207 237 max_dur,
amine@207 238 max_silence,
amine@207 239 drop_trailing_silence,
amine@207 240 strict_min_dur,
amine@207 241 analysis_window=0.1,
amine@207 242 sr=10,
amine@207 243 sw=2,
amine@207 244 ch=1,
amine@207 245 **kwargs
amine@207 246 )
amine@255 247
amine@255 248 region = AudioRegion(data, 10, 2, 1)
amine@255 249 regions_ar = region.split(
amine@255 250 min_dur,
amine@255 251 max_dur,
amine@255 252 max_silence,
amine@255 253 drop_trailing_silence,
amine@255 254 strict_min_dur,
amine@255 255 analysis_window=0.1,
amine@255 256 **kwargs
amine@255 257 )
amine@255 258
amine@207 259 regions = list(regions)
amine@255 260 regions_ar = list(regions_ar)
amine@207 261 err_msg = "Wrong number of regions after split, expected: "
amine@210 262 err_msg += "{}, found: {}".format(len(expected), len(regions))
amine@207 263 self.assertEqual(len(regions), len(expected), err_msg)
amine@255 264 err_msg = "Wrong number of regions after AudioRegion.split, expected: "
amine@255 265 err_msg += "{}, found: {}".format(len(expected), len(regions_ar))
amine@255 266 self.assertEqual(len(regions_ar), len(expected), err_msg)
amine@207 267
amine@207 268 sample_width = 2
amine@255 269 for reg, reg_ar, exp in zip(regions, regions_ar, expected):
amine@207 270 onset, offset = exp
amine@207 271 exp_data = data[onset * sample_width : offset * sample_width]
amine@207 272 self.assertEqual(bytes(reg), exp_data)
amine@255 273 self.assertEqual(reg, reg_ar)
amine@299 274
amine@211 275 @genty_dataset(
amine@241 276 stereo_all_default=(2, {}, [(2, 32), (34, 76)]),
amine@213 277 mono_max_read=(1, {"max_read": 5}, [(2, 16), (17, 31), (34, 50)]),
amine@213 278 mono_max_read_short_name=(1, {"mr": 5}, [(2, 16), (17, 31), (34, 50)]),
amine@211 279 mono_use_channel_1=(
amine@211 280 1,
amine@241 281 {"eth": 50, "use_channel": 0},
amine@211 282 [(2, 16), (17, 31), (34, 76)],
amine@211 283 ),
amine@211 284 mono_uc_1=(1, {"eth": 50, "uc": 1}, [(2, 16), (17, 31), (34, 76)]),
amine@211 285 mono_use_channel_None=(
amine@211 286 1,
amine@211 287 {"eth": 50, "use_channel": None},
amine@211 288 [(2, 16), (17, 31), (34, 76)],
amine@211 289 ),
amine@211 290 stereo_use_channel_1=(
amine@211 291 2,
amine@241 292 {"eth": 50, "use_channel": 0},
amine@211 293 [(2, 16), (17, 31), (34, 76)],
amine@211 294 ),
amine@299 295 stereo_use_channel_no_use_channel_given=(
amine@299 296 2,
amine@299 297 {"eth": 50},
amine@299 298 [(2, 32), (34, 76)],
amine@299 299 ),
amine@211 300 stereo_use_channel_minus_2=(
amine@211 301 2,
amine@211 302 {"eth": 50, "use_channel": -2},
amine@211 303 [(2, 16), (17, 31), (34, 76)],
amine@211 304 ),
amine@241 305 stereo_uc_2=(2, {"eth": 50, "uc": 1}, [(10, 32), (36, 76)]),
amine@211 306 stereo_uc_minus_1=(2, {"eth": 50, "uc": -1}, [(10, 32), (36, 76)]),
amine@299 307 mono_uc_mix=(
amine@299 308 1,
amine@299 309 {"eth": 50, "uc": "mix"},
amine@299 310 [(2, 16), (17, 31), (34, 76)],
amine@299 311 ),
amine@213 312 stereo_use_channel_mix=(
amine@213 313 2,
amine@213 314 {"energy_threshold": 53.5, "use_channel": "mix"},
amine@213 315 [(54, 76)],
amine@213 316 ),
amine@213 317 stereo_uc_mix=(2, {"eth": 52, "uc": "mix"}, [(17, 26), (54, 76)]),
amine@299 318 stereo_uc_mix_default_eth=(
amine@299 319 2,
amine@299 320 {"uc": "mix"},
amine@299 321 [(10, 16), (17, 31), (36, 76)],
amine@299 322 ),
amine@211 323 )
amine@211 324 def test_split_kwargs(self, channels, kwargs, expected):
amine@211 325
amine@211 326 mono_or_stereo = "mono" if channels == 1 else "stereo"
amine@211 327 filename = "tests/data/test_split_10HZ_{}.raw".format(mono_or_stereo)
amine@211 328 with open(filename, "rb") as fp:
amine@211 329 data = fp.read()
amine@211 330
amine@211 331 regions = split(
amine@211 332 data,
amine@211 333 min_dur=0.2,
amine@211 334 max_dur=5,
amine@211 335 max_silence=0.2,
amine@211 336 drop_trailing_silence=False,
amine@211 337 strict_min_dur=False,
amine@211 338 analysis_window=0.1,
amine@211 339 sr=10,
amine@211 340 sw=2,
amine@211 341 ch=channels,
amine@211 342 **kwargs
amine@211 343 )
amine@255 344
amine@255 345 region = AudioRegion(data, 10, 2, channels)
amine@306 346 max_read = kwargs.get("max_read", kwargs.get("mr"))
amine@306 347 if max_read is not None:
amine@306 348 region = region.sec[:max_read]
amine@306 349 kwargs.pop("max_read", None)
amine@306 350 kwargs.pop("mr", None)
amine@306 351
amine@255 352 regions_ar = region.split(
amine@255 353 min_dur=0.2,
amine@255 354 max_dur=5,
amine@255 355 max_silence=0.2,
amine@255 356 drop_trailing_silence=False,
amine@255 357 strict_min_dur=False,
amine@255 358 analysis_window=0.1,
amine@255 359 **kwargs
amine@255 360 )
amine@255 361
amine@212 362 regions = list(regions)
amine@255 363 regions_ar = list(regions_ar)
amine@211 364 err_msg = "Wrong number of regions after split, expected: "
amine@241 365 err_msg += "{}, found: {}".format(len(expected), len(regions))
amine@211 366 self.assertEqual(len(regions), len(expected), err_msg)
amine@255 367 err_msg = "Wrong number of regions after AudioRegion.split, expected: "
amine@255 368 err_msg += "{}, found: {}".format(len(expected), len(regions_ar))
amine@255 369 self.assertEqual(len(regions_ar), len(expected), err_msg)
amine@255 370
amine@255 371 sample_width = 2
amine@241 372 sample_size_bytes = sample_width * channels
amine@255 373 for reg, reg_ar, exp in zip(regions, regions_ar, expected):
amine@212 374 onset, offset = exp
amine@299 375 exp_data = data[
amine@299 376 onset * sample_size_bytes : offset * sample_size_bytes
amine@299 377 ]
amine@241 378 self.assertEqual(len(bytes(reg)), len(exp_data))
amine@255 379 self.assertEqual(reg, reg_ar)
amine@211 380
amine@212 381 @genty_dataset(
amine@299 382 mono_aw_0_2_max_silence_0_2=(
amine@299 383 0.2,
amine@299 384 5,
amine@299 385 0.2,
amine@299 386 1,
amine@299 387 {"aw": 0.2},
amine@299 388 [(2, 30), (34, 76)],
amine@299 389 ),
amine@299 390 mono_aw_0_2_max_silence_0_3=(
amine@299 391 0.2,
amine@299 392 5,
amine@299 393 0.3,
amine@299 394 1,
amine@299 395 {"aw": 0.2},
amine@299 396 [(2, 30), (34, 76)],
amine@299 397 ),
amine@299 398 mono_aw_0_2_max_silence_0_4=(
amine@299 399 0.2,
amine@299 400 5,
amine@299 401 0.4,
amine@299 402 1,
amine@299 403 {"aw": 0.2},
amine@299 404 [(2, 32), (34, 76)],
amine@299 405 ),
amine@231 406 mono_aw_0_2_max_silence_0=(
amine@231 407 0.2,
amine@231 408 5,
amine@231 409 0,
amine@231 410 1,
amine@241 411 {"aw": 0.2},
amine@231 412 [(2, 14), (16, 24), (26, 28), (34, 76)],
amine@231 413 ),
amine@241 414 mono_aw_0_2=(0.2, 5, 0.2, 1, {"aw": 0.2}, [(2, 30), (34, 76)]),
amine@231 415 mono_aw_0_3_max_silence_0=(
amine@231 416 0.3,
amine@231 417 5,
amine@231 418 0,
amine@231 419 1,
amine@241 420 {"aw": 0.3},
amine@231 421 [(3, 12), (15, 24), (36, 76)],
amine@231 422 ),
amine@299 423 mono_aw_0_3_max_silence_0_3=(
amine@299 424 0.3,
amine@299 425 5,
amine@299 426 0.3,
amine@299 427 1,
amine@299 428 {"aw": 0.3},
amine@299 429 [(3, 27), (36, 76)],
amine@299 430 ),
amine@299 431 mono_aw_0_3_max_silence_0_5=(
amine@299 432 0.3,
amine@299 433 5,
amine@299 434 0.5,
amine@299 435 1,
amine@299 436 {"aw": 0.3},
amine@299 437 [(3, 27), (36, 76)],
amine@299 438 ),
amine@299 439 mono_aw_0_3_max_silence_0_6=(
amine@299 440 0.3,
amine@299 441 5,
amine@299 442 0.6,
amine@299 443 1,
amine@299 444 {"aw": 0.3},
amine@299 445 [(3, 30), (36, 76)],
amine@299 446 ),
amine@231 447 mono_aw_0_4_max_silence_0=(
amine@231 448 0.2,
amine@231 449 5,
amine@232 450 0,
amine@231 451 1,
amine@241 452 {"aw": 0.4},
amine@231 453 [(4, 12), (16, 24), (36, 76)],
amine@231 454 ),
amine@231 455 mono_aw_0_4_max_silence_0_3=(
amine@231 456 0.2,
amine@231 457 5,
amine@231 458 0.3,
amine@231 459 1,
amine@241 460 {"aw": 0.4},
amine@231 461 [(4, 12), (16, 24), (36, 76)],
amine@231 462 ),
amine@299 463 mono_aw_0_4_max_silence_0_4=(
amine@299 464 0.2,
amine@299 465 5,
amine@299 466 0.4,
amine@299 467 1,
amine@299 468 {"aw": 0.4},
amine@299 469 [(4, 28), (36, 76)],
amine@299 470 ),
amine@316 471 stereo_uc_None_analysis_window_0_2=(
amine@316 472 0.2,
amine@316 473 5,
amine@316 474 0.2,
amine@316 475 2,
amine@316 476 {"analysis_window": 0.2},
amine@316 477 [(2, 32), (34, 76)],
amine@316 478 ),
amine@316 479 stereo_uc_any_analysis_window_0_2=(
amine@316 480 0.2,
amine@316 481 5,
amine@316 482 0.2,
amine@316 483 2,
amine@316 484 {"uc": None, "analysis_window": 0.2},
amine@316 485 [(2, 32), (34, 76)],
amine@316 486 ),
amine@316 487 stereo_use_channel_None_aw_0_3_max_silence_0_2=(
amine@316 488 0.2,
amine@316 489 5,
amine@316 490 0.2,
amine@316 491 2,
amine@316 492 {"use_channel": None, "analysis_window": 0.3},
amine@316 493 [(3, 30), (36, 76)],
amine@316 494 ),
amine@316 495 stereo_use_channel_any_aw_0_3_max_silence_0_3=(
amine@316 496 0.2,
amine@316 497 5,
amine@316 498 0.3,
amine@316 499 2,
amine@316 500 {"use_channel": "any", "analysis_window": 0.3},
amine@316 501 [(3, 33), (36, 76)],
amine@316 502 ),
amine@316 503 stereo_use_channel_None_aw_0_4_max_silence_0_2=(
amine@316 504 0.2,
amine@316 505 5,
amine@316 506 0.2,
amine@316 507 2,
amine@316 508 {"use_channel": None, "analysis_window": 0.4},
amine@316 509 [(4, 28), (36, 76)],
amine@316 510 ),
amine@316 511 stereo_use_channel_any_aw_0_3_max_silence_0_4=(
amine@316 512 0.2,
amine@316 513 5,
amine@316 514 0.4,
amine@316 515 2,
amine@316 516 {"use_channel": "any", "analysis_window": 0.4},
amine@316 517 [(4, 32), (36, 76)],
amine@316 518 ),
amine@241 519 stereo_uc_0_analysis_window_0_2=(
amine@241 520 0.2,
amine@241 521 5,
amine@241 522 0.2,
amine@241 523 2,
amine@241 524 {"uc": 0, "analysis_window": 0.2},
amine@241 525 [(2, 30), (34, 76)],
amine@241 526 ),
amine@220 527 stereo_uc_1_analysis_window_0_2=(
amine@220 528 0.2,
amine@220 529 5,
amine@220 530 0.2,
amine@220 531 2,
amine@220 532 {"uc": 1, "analysis_window": 0.2},
amine@231 533 [(10, 32), (36, 76)],
amine@231 534 ),
amine@233 535 stereo_uc_mix_aw_0_1_max_silence_0=(
amine@233 536 0.2,
amine@233 537 5,
amine@233 538 0,
amine@233 539 2,
amine@233 540 {"uc": "mix", "analysis_window": 0.1},
amine@233 541 [(10, 14), (17, 24), (26, 29), (36, 76)],
amine@233 542 ),
amine@233 543 stereo_uc_mix_aw_0_1_max_silence_0_1=(
amine@233 544 0.2,
amine@233 545 5,
amine@233 546 0.1,
amine@233 547 2,
amine@233 548 {"uc": "mix", "analysis_window": 0.1},
amine@233 549 [(10, 15), (17, 25), (26, 30), (36, 76)],
amine@233 550 ),
amine@233 551 stereo_uc_mix_aw_0_1_max_silence_0_2=(
amine@233 552 0.2,
amine@233 553 5,
amine@233 554 0.2,
amine@233 555 2,
amine@233 556 {"uc": "mix", "analysis_window": 0.1},
amine@233 557 [(10, 16), (17, 31), (36, 76)],
amine@233 558 ),
amine@233 559 stereo_uc_mix_aw_0_1_max_silence_0_3=(
amine@233 560 0.2,
amine@233 561 5,
amine@233 562 0.3,
amine@233 563 2,
amine@233 564 {"uc": "mix", "analysis_window": 0.1},
amine@233 565 [(10, 32), (36, 76)],
amine@233 566 ),
amine@316 567 stereo_uc_avg_aw_0_2_max_silence_0_min_dur_0_3=(
amine@233 568 0.3,
amine@233 569 5,
amine@233 570 0,
amine@233 571 2,
amine@316 572 {"uc": "avg", "analysis_window": 0.2},
amine@233 573 [(10, 14), (16, 24), (36, 76)],
amine@233 574 ),
amine@316 575 stereo_uc_average_aw_0_2_max_silence_0_min_dur_0_41=(
amine@233 576 0.41,
amine@233 577 5,
amine@233 578 0,
amine@233 579 2,
amine@316 580 {"uc": "average", "analysis_window": 0.2},
amine@233 581 [(16, 24), (36, 76)],
amine@233 582 ),
amine@233 583 stereo_uc_mix_aw_0_2_max_silence_0_1=(
amine@233 584 0.2,
amine@233 585 5,
amine@233 586 0.1,
amine@233 587 2,
amine@233 588 {"uc": "mix", "analysis_window": 0.2},
amine@233 589 [(10, 14), (16, 24), (26, 28), (36, 76)],
amine@233 590 ),
amine@233 591 stereo_uc_mix_aw_0_2_max_silence_0_2=(
amine@233 592 0.2,
amine@233 593 5,
amine@233 594 0.2,
amine@233 595 2,
amine@233 596 {"uc": "mix", "analysis_window": 0.2},
amine@233 597 [(10, 30), (36, 76)],
amine@233 598 ),
amine@233 599 stereo_uc_mix_aw_0_2_max_silence_0_4=(
amine@233 600 0.2,
amine@233 601 5,
amine@233 602 0.4,
amine@233 603 2,
amine@233 604 {"uc": "mix", "analysis_window": 0.2},
amine@233 605 [(10, 32), (36, 76)],
amine@233 606 ),
amine@233 607 stereo_uc_mix_aw_0_2_max_silence_0_5=(
amine@233 608 0.2,
amine@233 609 5,
amine@233 610 0.5,
amine@233 611 2,
amine@233 612 {"uc": "mix", "analysis_window": 0.2},
amine@233 613 [(10, 32), (36, 76)],
amine@233 614 ),
amine@233 615 stereo_uc_mix_aw_0_2_max_silence_0_6=(
amine@233 616 0.2,
amine@233 617 5,
amine@233 618 0.6,
amine@233 619 2,
amine@233 620 {"uc": "mix", "analysis_window": 0.2},
amine@233 621 [(10, 34), (36, 76)],
amine@233 622 ),
amine@233 623 stereo_uc_mix_aw_0_3_max_silence_0=(
amine@233 624 0.2,
amine@233 625 5,
amine@233 626 0,
amine@233 627 2,
amine@233 628 {"uc": "mix", "analysis_window": 0.3},
amine@233 629 [(9, 24), (27, 30), (36, 76)],
amine@233 630 ),
amine@233 631 stereo_uc_mix_aw_0_3_max_silence_0_min_dur_0_3=(
amine@233 632 0.4,
amine@233 633 5,
amine@233 634 0,
amine@233 635 2,
amine@233 636 {"uc": "mix", "analysis_window": 0.3},
amine@233 637 [(9, 24), (36, 76)],
amine@233 638 ),
amine@233 639 stereo_uc_mix_aw_0_3_max_silence_0_6=(
amine@233 640 0.2,
amine@233 641 5,
amine@233 642 0.6,
amine@233 643 2,
amine@233 644 {"uc": "mix", "analysis_window": 0.3},
amine@233 645 [(9, 57), (57, 76)],
amine@233 646 ),
amine@233 647 stereo_uc_mix_aw_0_3_max_silence_0_6_max_dur_5_1=(
amine@233 648 0.2,
amine@233 649 5.1,
amine@233 650 0.6,
amine@233 651 2,
amine@233 652 {"uc": "mix", "analysis_window": 0.3},
amine@233 653 [(9, 60), (60, 76)],
amine@233 654 ),
amine@233 655 stereo_uc_mix_aw_0_3_max_silence_0_6_max_dur_5_2=(
amine@233 656 0.2,
amine@233 657 5.2,
amine@233 658 0.6,
amine@233 659 2,
amine@233 660 {"uc": "mix", "analysis_window": 0.3},
amine@233 661 [(9, 60), (60, 76)],
amine@233 662 ),
amine@233 663 stereo_uc_mix_aw_0_3_max_silence_0_6_max_dur_5_3=(
amine@233 664 0.2,
amine@233 665 5.3,
amine@233 666 0.6,
amine@233 667 2,
amine@233 668 {"uc": "mix", "analysis_window": 0.3},
amine@233 669 [(9, 60), (60, 76)],
amine@233 670 ),
amine@233 671 stereo_uc_mix_aw_0_3_max_silence_0_6_max_dur_5_4=(
amine@233 672 0.2,
amine@233 673 5.4,
amine@233 674 0.6,
amine@233 675 2,
amine@233 676 {"uc": "mix", "analysis_window": 0.3},
amine@233 677 [(9, 63), (63, 76)],
amine@233 678 ),
amine@233 679 stereo_uc_mix_aw_0_4_max_silence_0=(
amine@233 680 0.2,
amine@233 681 5,
amine@233 682 0,
amine@233 683 2,
amine@233 684 {"uc": "mix", "analysis_window": 0.4},
amine@233 685 [(16, 24), (36, 76)],
amine@233 686 ),
amine@233 687 stereo_uc_mix_aw_0_4_max_silence_0_3=(
amine@233 688 0.2,
amine@233 689 5,
amine@233 690 0.3,
amine@233 691 2,
amine@233 692 {"uc": "mix", "analysis_window": 0.4},
amine@233 693 [(16, 24), (36, 76)],
amine@233 694 ),
amine@233 695 stereo_uc_mix_aw_0_4_max_silence_0_4=(
amine@233 696 0.2,
amine@233 697 5,
amine@233 698 0.4,
amine@233 699 2,
amine@233 700 {"uc": "mix", "analysis_window": 0.4},
amine@233 701 [(16, 28), (36, 76)],
amine@233 702 ),
amine@220 703 )
amine@220 704 def test_split_analysis_window(
amine@220 705 self, min_dur, max_dur, max_silence, channels, kwargs, expected
amine@220 706 ):
amine@220 707
amine@220 708 mono_or_stereo = "mono" if channels == 1 else "stereo"
amine@220 709 filename = "tests/data/test_split_10HZ_{}.raw".format(mono_or_stereo)
amine@220 710 with open(filename, "rb") as fp:
amine@220 711 data = fp.read()
amine@220 712
amine@220 713 regions = split(
amine@220 714 data,
amine@220 715 min_dur=min_dur,
amine@220 716 max_dur=max_dur,
amine@220 717 max_silence=max_silence,
amine@220 718 drop_trailing_silence=False,
amine@220 719 strict_min_dur=False,
amine@220 720 sr=10,
amine@220 721 sw=2,
amine@220 722 ch=channels,
amine@316 723 eth=49.99,
amine@220 724 **kwargs
amine@220 725 )
amine@255 726
amine@255 727 region = AudioRegion(data, 10, 2, channels)
amine@255 728 regions_ar = region.split(
amine@255 729 min_dur=min_dur,
amine@255 730 max_dur=max_dur,
amine@255 731 max_silence=max_silence,
amine@255 732 drop_trailing_silence=False,
amine@255 733 strict_min_dur=False,
amine@316 734 eth=49.99,
amine@255 735 **kwargs
amine@255 736 )
amine@255 737
amine@220 738 regions = list(regions)
amine@255 739 regions_ar = list(regions_ar)
amine@255 740 err_msg = "Wrong number of regions after split, expected: "
amine@255 741 err_msg += "{}, found: {}".format(len(expected), len(regions))
amine@255 742 self.assertEqual(len(regions), len(expected), err_msg)
amine@255 743 err_msg = "Wrong number of regions after AudioRegion.split, expected: "
amine@255 744 err_msg += "{}, found: {}".format(len(expected), len(regions_ar))
amine@255 745 self.assertEqual(len(regions_ar), len(expected), err_msg)
amine@255 746
amine@220 747 sample_width = 2
amine@255 748 sample_size_bytes = sample_width * channels
amine@255 749 for reg, reg_ar, exp in zip(regions, regions_ar, expected):
amine@220 750 onset, offset = exp
amine@299 751 exp_data = data[
amine@299 752 onset * sample_size_bytes : offset * sample_size_bytes
amine@299 753 ]
amine@299 754 self.assertEqual(bytes(reg), exp_data)
amine@299 755 self.assertEqual(reg, reg_ar)
amine@299 756
amine@299 757 def test_split_custom_validator(self):
amine@299 758 filename = "tests/data/test_split_10HZ_mono.raw"
amine@299 759 with open(filename, "rb") as fp:
amine@299 760 data = fp.read()
amine@299 761
amine@299 762 regions = split(
amine@299 763 data,
amine@299 764 min_dur=0.2,
amine@299 765 max_dur=5,
amine@299 766 max_silence=0.2,
amine@299 767 drop_trailing_silence=False,
amine@299 768 strict_min_dur=False,
amine@299 769 sr=10,
amine@299 770 sw=2,
amine@299 771 ch=1,
amine@299 772 analysis_window=0.1,
amine@299 773 validator=lambda x: array_("h", x)[0] >= 320,
amine@299 774 )
amine@299 775
amine@299 776 region = AudioRegion(data, 10, 2, 1)
amine@299 777 regions_ar = region.split(
amine@299 778 min_dur=0.2,
amine@299 779 max_dur=5,
amine@299 780 max_silence=0.2,
amine@299 781 drop_trailing_silence=False,
amine@299 782 strict_min_dur=False,
amine@299 783 analysis_window=0.1,
amine@299 784 validator=lambda x: array_("h", x)[0] >= 320,
amine@299 785 )
amine@299 786
amine@299 787 expected = [(2, 16), (17, 31), (34, 76)]
amine@299 788 regions = list(regions)
amine@299 789 regions_ar = list(regions_ar)
amine@299 790 err_msg = "Wrong number of regions after split, expected: "
amine@299 791 err_msg += "{}, found: {}".format(len(expected), len(regions))
amine@299 792 self.assertEqual(len(regions), len(expected), err_msg)
amine@299 793 err_msg = "Wrong number of regions after AudioRegion.split, expected: "
amine@299 794 err_msg += "{}, found: {}".format(len(expected), len(regions_ar))
amine@299 795 self.assertEqual(len(regions_ar), len(expected), err_msg)
amine@299 796
amine@299 797 sample_size_bytes = 2
amine@299 798 for reg, reg_ar, exp in zip(regions, regions_ar, expected):
amine@299 799 onset, offset = exp
amine@299 800 exp_data = data[
amine@299 801 onset * sample_size_bytes : offset * sample_size_bytes
amine@299 802 ]
amine@220 803 self.assertEqual(bytes(reg), exp_data)
amine@255 804 self.assertEqual(reg, reg_ar)
amine@220 805
amine@220 806 @genty_dataset(
amine@212 807 filename_audio_format=(
amine@212 808 "tests/data/test_split_10HZ_stereo.raw",
amine@212 809 {"audio_format": "raw", "sr": 10, "sw": 2, "ch": 2},
amine@212 810 ),
amine@212 811 filename_audio_format_short_name=(
amine@212 812 "tests/data/test_split_10HZ_stereo.raw",
amine@212 813 {"fmt": "raw", "sr": 10, "sw": 2, "ch": 2},
amine@212 814 ),
amine@212 815 filename_no_audio_format=(
amine@212 816 "tests/data/test_split_10HZ_stereo.raw",
amine@212 817 {"sr": 10, "sw": 2, "ch": 2},
amine@212 818 ),
amine@212 819 filename_no_long_audio_params=(
amine@212 820 "tests/data/test_split_10HZ_stereo.raw",
amine@212 821 {"sampling_rate": 10, "sample_width": 2, "channels": 2},
amine@212 822 ),
amine@212 823 bytes_=(
amine@212 824 open("tests/data/test_split_10HZ_stereo.raw", "rb").read(),
amine@212 825 {"sr": 10, "sw": 2, "ch": 2},
amine@212 826 ),
amine@212 827 audio_reader=(
amine@212 828 AudioDataSource(
amine@212 829 "tests/data/test_split_10HZ_stereo.raw",
amine@212 830 sr=10,
amine@212 831 sw=2,
amine@212 832 ch=2,
amine@212 833 block_dur=0.1,
amine@212 834 ),
amine@212 835 {},
amine@212 836 ),
amine@212 837 audio_region=(
amine@212 838 AudioRegion(
amine@299 839 open("tests/data/test_split_10HZ_stereo.raw", "rb").read(),
amine@299 840 10,
amine@299 841 2,
amine@299 842 2,
amine@212 843 ),
amine@212 844 {},
amine@212 845 ),
amine@212 846 audio_source=(
amine@212 847 get_audio_source(
amine@212 848 "tests/data/test_split_10HZ_stereo.raw", sr=10, sw=2, ch=2
amine@212 849 ),
amine@212 850 {},
amine@212 851 ),
amine@212 852 )
amine@212 853 def test_split_input_type(self, input, kwargs):
amine@212 854
amine@241 855 with open("tests/data/test_split_10HZ_stereo.raw", "rb") as fp:
amine@212 856 data = fp.read()
amine@212 857
amine@212 858 regions = split(
amine@212 859 input,
amine@212 860 min_dur=0.2,
amine@212 861 max_dur=5,
amine@212 862 max_silence=0.2,
amine@212 863 drop_trailing_silence=False,
amine@212 864 strict_min_dur=False,
amine@212 865 analysis_window=0.1,
amine@212 866 **kwargs
amine@212 867 )
amine@212 868 regions = list(regions)
amine@241 869 expected = [(2, 32), (34, 76)]
amine@212 870 sample_width = 2
amine@212 871 err_msg = "Wrong number of regions after split, expected: "
amine@212 872 err_msg += "{}, found: {}".format(expected, regions)
amine@212 873 self.assertEqual(len(regions), len(expected), err_msg)
amine@211 874 for reg, exp in zip(regions, expected):
amine@211 875 onset, offset = exp
amine@299 876 exp_data = data[
amine@299 877 onset * sample_width * 2 : offset * sample_width * 2
amine@299 878 ]
amine@211 879 self.assertEqual(bytes(reg), exp_data)
amine@211 880
amine@223 881 @genty_dataset(
amine@223 882 min_dur_greater_than_max_dur=(0.5, 0.4, 0.1),
amine@223 883 durations_OK_but_wrong_number_of_analysis_windows=(0.44, 0.49, 0.1),
amine@223 884 )
amine@223 885 def test_split_wrong_min_max_dur(self, min_dur, max_dur, analysis_window):
amine@223 886
amine@223 887 with self.assertRaises(ValueError) as val_err:
amine@223 888 split(
amine@223 889 b"0" * 16,
amine@223 890 min_dur=min_dur,
amine@223 891 max_dur=max_dur,
amine@223 892 max_silence=0.2,
amine@223 893 sr=16000,
amine@223 894 sw=1,
amine@223 895 ch=1,
amine@223 896 analysis_window=analysis_window,
amine@223 897 )
amine@223 898
amine@223 899 err_msg = "'min_dur' ({0} sec.) results in {1} analysis "
amine@223 900 err_msg += "window(s) ({1} == ceil({0} / {2})) which is "
amine@223 901 err_msg += "higher than the number of analysis window(s) for "
amine@223 902 err_msg += "'max_dur' ({3} == floor({4} / {2}))"
amine@223 903
amine@223 904 err_msg = err_msg.format(
amine@223 905 min_dur,
amine@223 906 math.ceil(min_dur / analysis_window),
amine@223 907 analysis_window,
amine@223 908 math.floor(max_dur / analysis_window),
amine@223 909 max_dur,
amine@223 910 )
amine@223 911 self.assertEqual(err_msg, str(val_err.exception))
amine@223 912
amine@224 913 @genty_dataset(
amine@224 914 max_silence_equals_max_dur=(0.5, 0.5, 0.1),
amine@224 915 max_silence_greater_than_max_dur=(0.5, 0.4, 0.1),
amine@224 916 durations_OK_but_wrong_number_of_analysis_windows=(0.44, 0.49, 0.1),
amine@224 917 )
amine@224 918 def test_split_wrong_max_silence_max_dur(
amine@224 919 self, max_silence, max_dur, analysis_window
amine@224 920 ):
amine@224 921
amine@224 922 with self.assertRaises(ValueError) as val_err:
amine@224 923 split(
amine@224 924 b"0" * 16,
amine@224 925 min_dur=0.2,
amine@224 926 max_dur=max_dur,
amine@224 927 max_silence=max_silence,
amine@224 928 sr=16000,
amine@224 929 sw=1,
amine@224 930 ch=1,
amine@224 931 analysis_window=analysis_window,
amine@224 932 )
amine@224 933
amine@224 934 err_msg = "'max_silence' ({0} sec.) results in {1} analysis "
amine@224 935 err_msg += "window(s) ({1} == floor({0} / {2})) which is "
amine@224 936 err_msg += "higher or equal to the number of analysis window(s) for "
amine@224 937 err_msg += "'max_dur' ({3} == floor({4} / {2}))"
amine@224 938
amine@224 939 err_msg = err_msg.format(
amine@224 940 max_silence,
amine@224 941 math.floor(max_silence / analysis_window),
amine@224 942 analysis_window,
amine@224 943 math.floor(max_dur / analysis_window),
amine@224 944 max_dur,
amine@224 945 )
amine@224 946 self.assertEqual(err_msg, str(val_err.exception))
amine@224 947
amine@226 948 @genty_dataset(
amine@226 949 negative_min_dur=({"min_dur": -1},),
amine@226 950 zero_min_dur=({"min_dur": 0},),
amine@226 951 negative_max_dur=({"max_dur": -1},),
amine@226 952 zero_max_dur=({"max_dur": 0},),
amine@226 953 negative_max_silence=({"max_silence": -1},),
amine@237 954 zero_analysis_window=({"analysis_window": 0},),
amine@237 955 negative_analysis_window=({"analysis_window": -1},),
amine@226 956 )
amine@226 957 def test_split_negative_temporal_params(self, wrong_param):
amine@226 958
amine@237 959 params = {
amine@237 960 "min_dur": 0.2,
amine@237 961 "max_dur": 0.5,
amine@237 962 "max_silence": 0.1,
amine@237 963 "analysis_window": 0.1,
amine@237 964 }
amine@226 965 params.update(wrong_param)
amine@226 966 with self.assertRaises(ValueError) as val_err:
amine@226 967 split(None, **params)
amine@226 968
amine@226 969 name = set(wrong_param).pop()
amine@226 970 value = wrong_param[name]
amine@226 971 err_msg = "'{}' ({}) must be >{} 0".format(
amine@226 972 name, value, "=" if name == "max_silence" else ""
amine@226 973 )
amine@226 974 self.assertEqual(err_msg, str(val_err.exception))
amine@226 975
amine@236 976 def test_split_too_small_analysis_window(self):
amine@236 977 with self.assertRaises(ValueError) as val_err:
amine@236 978 split(b"", sr=10, sw=1, ch=1, analysis_window=0.09)
amine@236 979 err_msg = "Too small 'analysis_windows' (0.09) for sampling rate (10)."
amine@236 980 err_msg += " Analysis windows should at least be 1/10 to cover one "
amine@236 981 err_msg += "single data sample"
amine@236 982 self.assertEqual(err_msg, str(val_err.exception))
amine@299 983
amine@264 984 def test_split_and_plot(self):
amine@236 985
amine@264 986 with open("tests/data/test_split_10HZ_mono.raw", "rb") as fp:
amine@264 987 data = fp.read()
amine@264 988
amine@264 989 region = AudioRegion(data, 10, 2, 1)
amine@302 990 with patch("auditok.plotting.plot") as patch_fn:
amine@264 991 regions = region.split_and_plot(
amine@264 992 min_dur=0.2,
amine@264 993 max_dur=5,
amine@264 994 max_silence=0.2,
amine@264 995 drop_trailing_silence=False,
amine@264 996 strict_min_dur=False,
amine@264 997 analysis_window=0.1,
amine@264 998 sr=10,
amine@264 999 sw=2,
amine@264 1000 ch=1,
amine@264 1001 eth=50,
amine@264 1002 )
amine@264 1003 self.assertTrue(patch_fn.called)
amine@264 1004 expected = [(2, 16), (17, 31), (34, 76)]
amine@264 1005 sample_width = 2
amine@264 1006 expected_regions = []
amine@264 1007 for (onset, offset) in expected:
amine@264 1008 onset *= sample_width
amine@264 1009 offset *= sample_width
amine@264 1010 expected_regions.append(AudioRegion(data[onset:offset], 10, 2, 1))
amine@264 1011 self.assertEqual(regions, expected_regions)
amine@207 1012
amine@306 1013 def test_split_exception(self):
amine@306 1014 with open("tests/data/test_split_10HZ_mono.raw", "rb") as fp:
amine@306 1015 data = fp.read()
amine@306 1016 region = AudioRegion(data, 10, 2, 1)
amine@306 1017
amine@306 1018 with self.assertRaises(RuntimeWarning):
amine@306 1019 # max_read is not accepted when calling AudioRegion.split
amine@306 1020 region.split(max_read=2)
amine@306 1021
amine@299 1022
amine@207 1023 @genty
amine@207 1024 class TestAudioRegion(TestCase):
amine@86 1025 @genty_dataset(
amine@86 1026 simple=(b"\0" * 8000, 0, 8000, 1, 1, 1, 1, 1000),
amine@299 1027 one_ms_less_than_1_sec=(
amine@299 1028 b"\0" * 7992,
amine@299 1029 0,
amine@299 1030 8000,
amine@299 1031 1,
amine@299 1032 1,
amine@299 1033 0.999,
amine@299 1034 0.999,
amine@299 1035 999,
amine@299 1036 ),
amine@86 1037 tree_quarter_ms_less_than_1_sec=(
amine@86 1038 b"\0" * 7994,
amine@86 1039 0,
amine@86 1040 8000,
amine@86 1041 1,
amine@86 1042 1,
amine@86 1043 0.99925,
amine@86 1044 0.99925,
amine@86 1045 999,
amine@86 1046 ),
amine@299 1047 half_ms_less_than_1_sec=(
amine@299 1048 b"\0" * 7996,
amine@299 1049 0,
amine@299 1050 8000,
amine@299 1051 1,
amine@299 1052 1,
amine@299 1053 0.9995,
amine@299 1054 0.9995,
amine@299 1055 1000,
amine@299 1056 ),
amine@86 1057 quarter_ms_less_than_1_sec=(
amine@86 1058 b"\0" * 7998,
amine@86 1059 0,
amine@86 1060 8000,
amine@86 1061 1,
amine@86 1062 1,
amine@86 1063 0.99975,
amine@86 1064 0.99975,
amine@86 1065 1000,
amine@86 1066 ),
amine@86 1067 simple_sample_width_2=(b"\0" * 8000 * 2, 0, 8000, 2, 1, 1, 1, 1000),
amine@86 1068 simple_stereo=(b"\0" * 8000 * 2, 0, 8000, 1, 2, 1, 1, 1000),
amine@86 1069 simple_multichannel=(b"\0" * 8000 * 5, 0, 8000, 1, 5, 1, 1, 1000),
amine@86 1070 simple_sample_width_2_multichannel=(
amine@86 1071 b"\0" * 8000 * 2 * 5,
amine@86 1072 0,
amine@86 1073 8000,
amine@86 1074 2,
amine@86 1075 5,
amine@86 1076 1,
amine@86 1077 1,
amine@86 1078 1000,
amine@86 1079 ),
amine@86 1080 one_ms_less_than_1s_sw_2_multichannel=(
amine@86 1081 b"\0" * 7992 * 2 * 5,
amine@86 1082 0,
amine@86 1083 8000,
amine@86 1084 2,
amine@86 1085 5,
amine@86 1086 0.999,
amine@86 1087 0.999,
amine@86 1088 999,
amine@86 1089 ),
amine@86 1090 tree_qrt_ms_lt_1_s_sw_2_multichannel=(
amine@86 1091 b"\0" * 7994 * 2 * 5,
amine@86 1092 0,
amine@86 1093 8000,
amine@86 1094 2,
amine@86 1095 5,
amine@86 1096 0.99925,
amine@86 1097 0.99925,
amine@86 1098 999,
amine@86 1099 ),
amine@86 1100 half_ms_lt_1s_sw_2_multichannel=(
amine@86 1101 b"\0" * 7996 * 2 * 5,
amine@86 1102 0,
amine@86 1103 8000,
amine@86 1104 2,
amine@86 1105 5,
amine@86 1106 0.9995,
amine@86 1107 0.9995,
amine@86 1108 1000,
amine@86 1109 ),
amine@86 1110 quarter_ms_lt_1s_sw_2_multichannel=(
amine@86 1111 b"\0" * 7998 * 2 * 5,
amine@86 1112 0,
amine@86 1113 8000,
amine@86 1114 2,
amine@86 1115 5,
amine@86 1116 0.99975,
amine@86 1117 0.99975,
amine@86 1118 1000,
amine@86 1119 ),
amine@86 1120 arbitrary_length_1=(
amine@86 1121 b"\0" * int(8000 * 1.33),
amine@86 1122 2.7,
amine@86 1123 8000,
amine@86 1124 1,
amine@86 1125 1,
amine@86 1126 4.03,
amine@86 1127 1.33,
amine@86 1128 1330,
amine@86 1129 ),
amine@86 1130 arbitrary_length_2=(
amine@86 1131 b"\0" * int(8000 * 0.476),
amine@86 1132 11.568,
amine@86 1133 8000,
amine@86 1134 1,
amine@86 1135 1,
amine@86 1136 12.044,
amine@86 1137 0.476,
amine@86 1138 476,
amine@86 1139 ),
amine@86 1140 arbitrary_length_sw_2_multichannel=(
amine@86 1141 b"\0" * int(8000 * 1.711) * 2 * 3,
amine@86 1142 9.415,
amine@86 1143 8000,
amine@86 1144 2,
amine@86 1145 3,
amine@86 1146 11.126,
amine@86 1147 1.711,
amine@86 1148 1711,
amine@86 1149 ),
amine@86 1150 arbitrary_samplig_rate=(
amine@86 1151 b"\0" * int(3172 * 1.318),
amine@86 1152 17.236,
amine@86 1153 3172,
amine@86 1154 1,
amine@86 1155 1,
amine@86 1156 17.236 + int(3172 * 1.318) / 3172,
amine@86 1157 int(3172 * 1.318) / 3172,
amine@86 1158 1318,
amine@86 1159 ),
amine@86 1160 arbitrary_sr_sw_2_multichannel=(
amine@86 1161 b"\0" * int(11317 * 0.716) * 2 * 3,
amine@86 1162 18.811,
amine@86 1163 11317,
amine@86 1164 2,
amine@86 1165 3,
amine@86 1166 18.811 + int(11317 * 0.716) / 11317,
amine@86 1167 int(11317 * 0.716) / 11317,
amine@86 1168 716,
amine@86 1169 ),
amine@86 1170 )
amine@86 1171 def test_creation(
amine@86 1172 self,
amine@86 1173 data,
amine@86 1174 start,
amine@86 1175 sampling_rate,
amine@86 1176 sample_width,
amine@86 1177 channels,
amine@86 1178 expected_end,
amine@86 1179 expected_duration_s,
amine@86 1180 expected_duration_ms,
amine@86 1181 ):
amine@244 1182 meta = {"start": start, "end": expected_end}
amine@244 1183 region = AudioRegion(data, sampling_rate, sample_width, channels, meta)
amine@86 1184 self.assertEqual(region.sampling_rate, sampling_rate)
amine@86 1185 self.assertEqual(region.sr, sampling_rate)
amine@86 1186 self.assertEqual(region.sample_width, sample_width)
amine@86 1187 self.assertEqual(region.sw, sample_width)
amine@86 1188 self.assertEqual(region.channels, channels)
amine@86 1189 self.assertEqual(region.ch, channels)
amine@244 1190 self.assertEqual(region.meta.start, start)
amine@244 1191 self.assertEqual(region.meta.end, expected_end)
amine@86 1192 self.assertEqual(region.duration, expected_duration_s)
amine@245 1193 self.assertEqual(len(region.ms), expected_duration_ms)
amine@86 1194 self.assertEqual(bytes(region), data)
amine@88 1195
amine@97 1196 def test_creation_invalid_data_exception(self):
amine@97 1197 with self.assertRaises(AudioParameterError) as audio_param_err:
amine@97 1198 _ = AudioRegion(
amine@244 1199 data=b"ABCDEFGHI", sampling_rate=8, sample_width=2, channels=1
amine@97 1200 )
amine@97 1201 self.assertEqual(
amine@97 1202 "The length of audio data must be an integer "
amine@97 1203 "multiple of `sample_width * channels`",
amine@97 1204 str(audio_param_err.exception),
amine@97 1205 )
amine@97 1206
amine@308 1207 @genty_dataset(
amine@308 1208 no_skip_read_all=(0, -1),
amine@308 1209 no_skip_read_all_stereo=(0, -1, 2),
amine@308 1210 skip_2_read_all=(2, -1),
amine@308 1211 skip_2_read_all_None=(2, None),
amine@308 1212 skip_2_read_3=(2, 3),
amine@308 1213 skip_2_read_3_5_stereo=(2, 3.5, 2),
amine@308 1214 skip_2_4_read_3_5_stereo=(2.4, 3.5, 2),
amine@308 1215 )
amine@308 1216 def test_load(self, skip, max_read, channels=1):
amine@308 1217 sampling_rate = 10
amine@308 1218 sample_width = 2
amine@308 1219 filename = "tests/data/test_split_10HZ_{}.raw"
amine@308 1220 filename = filename.format("mono" if channels == 1 else "stereo")
amine@308 1221 region = AudioRegion.load(
amine@308 1222 filename,
amine@308 1223 skip=skip,
amine@308 1224 max_read=max_read,
amine@308 1225 sr=sampling_rate,
amine@308 1226 sw=sample_width,
amine@308 1227 ch=channels,
amine@308 1228 )
amine@308 1229 with open(filename, "rb") as fp:
amine@308 1230 fp.read(round(skip * sampling_rate * sample_width * channels))
amine@308 1231 if max_read is None or max_read < 0:
amine@308 1232 to_read = -1
amine@308 1233 else:
amine@308 1234 to_read = round(
amine@308 1235 max_read * sampling_rate * sample_width * channels
amine@308 1236 )
amine@308 1237 expected = fp.read(to_read)
amine@308 1238 self.assertEqual(bytes(region), expected)
amine@308 1239
amine@308 1240 def test_load_from_microphone(self):
amine@308 1241 with patch("auditok.io.PyAudioSource") as patch_pyaudio_source:
amine@308 1242 with patch("auditok.core.AudioReader.read") as patch_reader:
amine@308 1243 patch_reader.return_value = None
amine@308 1244 with patch(
amine@308 1245 "auditok.core.AudioRegion.__init__"
amine@308 1246 ) as patch_AudioRegion:
amine@308 1247 patch_AudioRegion.return_value = None
amine@308 1248 AudioRegion.load(
amine@308 1249 None, skip=0, max_read=5, sr=16000, sw=2, ch=1
amine@308 1250 )
amine@308 1251 self.assertTrue(patch_pyaudio_source.called)
amine@308 1252 self.assertTrue(patch_reader.called)
amine@308 1253 self.assertTrue(patch_AudioRegion.called)
amine@308 1254
amine@308 1255 @genty_dataset(none=(None,), negative=(-1,))
amine@308 1256 def test_load_from_microphone_without_max_read_exception(self, max_read):
amine@307 1257 with self.assertRaises(ValueError) as val_err:
amine@308 1258 AudioRegion.load(None, max_read=max_read, sr=16000, sw=2, ch=1)
amine@307 1259 self.assertEqual(
amine@307 1260 "'max_read' should not be None when reading from microphone",
amine@307 1261 str(val_err.exception),
amine@307 1262 )
amine@307 1263
amine@308 1264 def test_load_from_microphone_with_nonzero_skip_exception(self):
amine@308 1265 with self.assertRaises(ValueError) as val_err:
amine@308 1266 AudioRegion.load(None, skip=1, max_read=5, sr=16000, sw=2, ch=1)
amine@308 1267 self.assertEqual(
amine@308 1268 "'skip' should be 0 when reading from microphone",
amine@308 1269 str(val_err.exception),
amine@308 1270 )
amine@308 1271
amine@88 1272 @genty_dataset(
amine@192 1273 simple=("output.wav", 1.230, "output.wav"),
amine@244 1274 start=("output_{meta.start:g}.wav", 1.230, "output_1.23.wav"),
amine@244 1275 start_2=("output_{meta.start}.wav", 1.233712, "output_1.233712.wav"),
amine@244 1276 start_3=("output_{meta.start:.2f}.wav", 1.2300001, "output_1.23.wav"),
amine@244 1277 start_4=("output_{meta.start:.3f}.wav", 1.233712, "output_1.234.wav"),
amine@299 1278 start_5=(
amine@299 1279 "output_{meta.start:.8f}.wav",
amine@299 1280 1.233712,
amine@299 1281 "output_1.23371200.wav",
amine@299 1282 ),
amine@192 1283 start_end_duration=(
amine@244 1284 "output_{meta.start}_{meta.end}_{duration}.wav",
amine@192 1285 1.455,
amine@192 1286 "output_1.455_2.455_1.0.wav",
amine@192 1287 ),
amine@192 1288 start_end_duration_2=(
amine@244 1289 "output_{meta.start}_{meta.end}_{duration}.wav",
amine@192 1290 1.455321,
amine@192 1291 "output_1.455321_2.455321_1.0.wav",
amine@192 1292 ),
amine@192 1293 )
amine@192 1294 def test_save(self, format, start, expected):
amine@192 1295 with TemporaryDirectory() as tmpdir:
amine@244 1296 region = AudioRegion(b"0" * 160, 160, 1, 1)
amine@244 1297 meta = {"start": start, "end": start + region.duration}
amine@244 1298 region.meta = meta
amine@192 1299 format = os.path.join(tmpdir, format)
amine@192 1300 filename = region.save(format)[len(tmpdir) + 1 :]
amine@192 1301 self.assertEqual(filename, expected)
amine@192 1302
amine@193 1303 def test_save_file_exists_exception(self):
amine@193 1304 with TemporaryDirectory() as tmpdir:
amine@193 1305 filename = os.path.join(tmpdir, "output.wav")
amine@193 1306 open(filename, "w").close()
amine@244 1307 region = AudioRegion(b"0" * 160, 160, 1, 1)
amine@193 1308 with self.assertRaises(FileExistsError):
amine@193 1309 region.save(filename, exists_ok=False)
amine@193 1310
amine@192 1311 @genty_dataset(
amine@194 1312 first_half=(
amine@244 1313 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@194 1314 slice(0, 500),
amine@244 1315 b"a" * 80,
amine@244 1316 ),
amine@244 1317 second_half=(
amine@244 1318 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@244 1319 slice(500, None),
amine@244 1320 b"b" * 80,
amine@244 1321 ),
amine@244 1322 second_half_negative=(
amine@244 1323 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@244 1324 slice(-500, None),
amine@244 1325 b"b" * 80,
amine@244 1326 ),
amine@244 1327 middle=(
amine@244 1328 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@244 1329 slice(200, 750),
amine@244 1330 b"a" * 48 + b"b" * 40,
amine@244 1331 ),
amine@244 1332 middle_negative=(
amine@244 1333 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@244 1334 slice(-800, -250),
amine@244 1335 b"a" * 48 + b"b" * 40,
amine@244 1336 ),
amine@244 1337 middle_sw2=(
amine@244 1338 AudioRegion(b"a" * 160 + b"b" * 160, 160, 2, 1),
amine@244 1339 slice(200, 750),
amine@244 1340 b"a" * 96 + b"b" * 80,
amine@244 1341 ),
amine@244 1342 middle_ch2=(
amine@244 1343 AudioRegion(b"a" * 160 + b"b" * 160, 160, 1, 2),
amine@244 1344 slice(200, 750),
amine@244 1345 b"a" * 96 + b"b" * 80,
amine@244 1346 ),
amine@244 1347 middle_sw2_ch2=(
amine@244 1348 AudioRegion(b"a" * 320 + b"b" * 320, 160, 2, 2),
amine@244 1349 slice(200, 750),
amine@244 1350 b"a" * 192 + b"b" * 160,
amine@244 1351 ),
amine@244 1352 but_first_sample=(
amine@244 1353 AudioRegion(b"a" * 4000 + b"b" * 4000, 8000, 1, 1),
amine@244 1354 slice(1, None),
amine@244 1355 b"a" * (4000 - 8) + b"b" * 4000,
amine@244 1356 ),
amine@244 1357 but_first_sample_negative=(
amine@244 1358 AudioRegion(b"a" * 4000 + b"b" * 4000, 8000, 1, 1),
amine@244 1359 slice(-999, None),
amine@244 1360 b"a" * (4000 - 8) + b"b" * 4000,
amine@244 1361 ),
amine@244 1362 but_last_sample=(
amine@244 1363 AudioRegion(b"a" * 4000 + b"b" * 4000, 8000, 1, 1),
amine@244 1364 slice(0, 999),
amine@244 1365 b"a" * 4000 + b"b" * (4000 - 8),
amine@244 1366 ),
amine@244 1367 but_last_sample_negative=(
amine@244 1368 AudioRegion(b"a" * 4000 + b"b" * 4000, 8000, 1, 1),
amine@244 1369 slice(0, -1),
amine@244 1370 b"a" * 4000 + b"b" * (4000 - 8),
amine@244 1371 ),
amine@244 1372 big_negative_start=(
amine@244 1373 AudioRegion(b"a" * 160, 160, 1, 1),
amine@244 1374 slice(-5000, None),
amine@244 1375 b"a" * 160,
amine@244 1376 ),
amine@299 1377 big_negative_stop=(
amine@299 1378 AudioRegion(b"a" * 160, 160, 1, 1),
amine@299 1379 slice(None, -1500),
amine@299 1380 b"",
amine@299 1381 ),
amine@299 1382 empty=(
amine@299 1383 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@299 1384 slice(0, 0),
amine@299 1385 b"",
amine@299 1386 ),
amine@244 1387 empty_start_stop_reversed=(
amine@244 1388 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@244 1389 slice(200, 100),
amine@244 1390 b"",
amine@244 1391 ),
amine@244 1392 empty_big_positive_start=(
amine@244 1393 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@244 1394 slice(2000, 3000),
amine@244 1395 b"",
amine@244 1396 ),
amine@244 1397 empty_negative_reversed=(
amine@244 1398 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@244 1399 slice(-100, -200),
amine@244 1400 b"",
amine@244 1401 ),
amine@244 1402 empty_big_negative_stop=(
amine@244 1403 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@244 1404 slice(0, -2000),
amine@244 1405 b"",
amine@244 1406 ),
amine@244 1407 arbitrary_sampling_rate=(
amine@244 1408 AudioRegion(b"a" * 124 + b"b" * 376, 1234, 1, 1),
amine@244 1409 slice(100, 200),
amine@244 1410 b"a" + b"b" * 123,
amine@244 1411 ),
amine@244 1412 )
amine@244 1413 def test_region_temporal_slicing(self, region, slice_, expected_data):
amine@244 1414 sub_region = region.millis[slice_]
amine@244 1415 self.assertEqual(bytes(sub_region), expected_data)
amine@244 1416 start_sec = slice_.start / 1000 if slice_.start is not None else None
amine@244 1417 stop_sec = slice_.stop / 1000 if slice_.stop is not None else None
amine@244 1418 sub_region = region.sec[start_sec:stop_sec]
amine@244 1419 self.assertEqual(bytes(sub_region), expected_data)
amine@244 1420
amine@244 1421 @genty_dataset(
amine@244 1422 first_half=(
amine@244 1423 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@244 1424 slice(0, 80),
amine@194 1425 0,
amine@194 1426 b"a" * 80,
amine@194 1427 ),
amine@194 1428 second_half=(
amine@244 1429 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@244 1430 slice(80, None),
amine@194 1431 0.5,
amine@194 1432 b"b" * 80,
amine@194 1433 ),
amine@194 1434 second_half_negative=(
amine@244 1435 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@244 1436 slice(-80, None),
amine@194 1437 0.5,
amine@194 1438 b"b" * 80,
amine@194 1439 ),
amine@194 1440 middle=(
amine@244 1441 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@244 1442 slice(160 // 5, 160 // 4 * 3),
amine@194 1443 0.2,
amine@194 1444 b"a" * 48 + b"b" * 40,
amine@194 1445 ),
amine@194 1446 middle_negative=(
amine@244 1447 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@244 1448 slice(-160 // 5 * 4, -160 // 4),
amine@194 1449 0.2,
amine@194 1450 b"a" * 48 + b"b" * 40,
amine@194 1451 ),
amine@194 1452 middle_sw2=(
amine@244 1453 AudioRegion(b"a" * 160 + b"b" * 160, 160, 2, 1),
amine@244 1454 slice(160 // 5, 160 // 4 * 3),
amine@194 1455 0.2,
amine@194 1456 b"a" * 96 + b"b" * 80,
amine@194 1457 ),
amine@194 1458 middle_ch2=(
amine@244 1459 AudioRegion(b"a" * 160 + b"b" * 160, 160, 1, 2),
amine@244 1460 slice(160 // 5, 160 // 4 * 3),
amine@194 1461 0.2,
amine@194 1462 b"a" * 96 + b"b" * 80,
amine@194 1463 ),
amine@194 1464 middle_sw2_ch2=(
amine@244 1465 AudioRegion(b"a" * 320 + b"b" * 320, 160, 2, 2),
amine@244 1466 slice(160 // 5, 160 // 4 * 3),
amine@194 1467 0.2,
amine@194 1468 b"a" * 192 + b"b" * 160,
amine@194 1469 ),
amine@194 1470 but_first_sample=(
amine@244 1471 AudioRegion(b"a" * 4000 + b"b" * 4000, 8000, 1, 1),
amine@194 1472 slice(1, None),
amine@244 1473 1 / 8000,
amine@244 1474 b"a" * (4000 - 1) + b"b" * 4000,
amine@194 1475 ),
amine@194 1476 but_first_sample_negative=(
amine@244 1477 AudioRegion(b"a" * 4000 + b"b" * 4000, 8000, 1, 1),
amine@244 1478 slice(-7999, None),
amine@244 1479 1 / 8000,
amine@244 1480 b"a" * (4000 - 1) + b"b" * 4000,
amine@194 1481 ),
amine@194 1482 but_last_sample=(
amine@244 1483 AudioRegion(b"a" * 4000 + b"b" * 4000, 8000, 1, 1),
amine@244 1484 slice(0, 7999),
amine@194 1485 0,
amine@244 1486 b"a" * 4000 + b"b" * (4000 - 1),
amine@194 1487 ),
amine@194 1488 but_last_sample_negative=(
amine@244 1489 AudioRegion(b"a" * 4000 + b"b" * 4000, 8000, 1, 1),
amine@194 1490 slice(0, -1),
amine@194 1491 0,
amine@244 1492 b"a" * 4000 + b"b" * (4000 - 1),
amine@194 1493 ),
amine@194 1494 big_negative_start=(
amine@244 1495 AudioRegion(b"a" * 160, 160, 1, 1),
amine@244 1496 slice(-1600, None),
amine@194 1497 0,
amine@194 1498 b"a" * 160,
amine@194 1499 ),
amine@194 1500 big_negative_stop=(
amine@244 1501 AudioRegion(b"a" * 160, 160, 1, 1),
amine@244 1502 slice(None, -1600),
amine@194 1503 0,
amine@194 1504 b"",
amine@194 1505 ),
amine@299 1506 empty=(
amine@299 1507 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@299 1508 slice(0, 0),
amine@299 1509 0,
amine@299 1510 b"",
amine@299 1511 ),
amine@194 1512 empty_start_stop_reversed=(
amine@244 1513 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@244 1514 slice(80, 40),
amine@244 1515 0.5,
amine@194 1516 b"",
amine@194 1517 ),
amine@194 1518 empty_big_positive_start=(
amine@244 1519 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@244 1520 slice(1600, 3000),
amine@244 1521 10,
amine@194 1522 b"",
amine@194 1523 ),
amine@194 1524 empty_negative_reversed=(
amine@244 1525 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@244 1526 slice(-16, -32),
amine@194 1527 0.9,
amine@194 1528 b"",
amine@194 1529 ),
amine@194 1530 empty_big_negative_stop=(
amine@244 1531 AudioRegion(b"a" * 80 + b"b" * 80, 160, 1, 1),
amine@194 1532 slice(0, -2000),
amine@194 1533 0,
amine@194 1534 b"",
amine@194 1535 ),
amine@230 1536 arbitrary_sampling_rate=(
amine@244 1537 AudioRegion(b"a" * 124 + b"b" * 376, 1235, 1, 1),
amine@231 1538 slice(100, 200),
amine@231 1539 100 / 1235,
amine@231 1540 b"a" * 24 + b"b" * 76,
amine@231 1541 ),
amine@231 1542 arbitrary_sampling_rate_middle_sw2_ch2=(
amine@244 1543 AudioRegion(b"a" * 124 + b"b" * 376, 1235, 2, 2),
amine@231 1544 slice(25, 50),
amine@231 1545 25 / 1235,
amine@231 1546 b"a" * 24 + b"b" * 76,
amine@231 1547 ),
amine@231 1548 )
amine@299 1549 def test_region_sample_slicing(
amine@299 1550 self, region, slice_, time_shift, expected_data
amine@299 1551 ):
amine@231 1552 sub_region = region[slice_]
amine@231 1553 self.assertEqual(bytes(sub_region), expected_data)
amine@231 1554
amine@231 1555 @genty_dataset(
amine@88 1556 simple=(8000, 1, 1),
amine@88 1557 stereo_sw_2=(8000, 2, 2),
amine@229 1558 arbitrary_sr_multichannel=(5413, 2, 3),
amine@88 1559 )
amine@88 1560 def test_concatenation(self, sampling_rate, sample_width, channels):
amine@88 1561
amine@88 1562 region_1, region_2 = _make_random_length_regions(
amine@88 1563 [b"a", b"b"], sampling_rate, sample_width, channels
amine@88 1564 )
amine@88 1565 expected_duration = region_1.duration + region_2.duration
amine@88 1566 expected_data = bytes(region_1) + bytes(region_2)
amine@88 1567 concat_region = region_1 + region_2
amine@299 1568 self.assertAlmostEqual(
amine@299 1569 concat_region.duration, expected_duration, places=6
amine@299 1570 )
amine@88 1571 self.assertEqual(bytes(concat_region), expected_data)
amine@88 1572
amine@88 1573 @genty_dataset(
amine@88 1574 simple=(8000, 1, 1),
amine@88 1575 stereo_sw_2=(8000, 2, 2),
amine@229 1576 arbitrary_sr_multichannel=(5413, 2, 3),
amine@88 1577 )
amine@88 1578 def test_concatenation_many(self, sampling_rate, sample_width, channels):
amine@88 1579
amine@88 1580 regions = _make_random_length_regions(
amine@88 1581 [b"a", b"b", b"c"], sampling_rate, sample_width, channels
amine@88 1582 )
amine@88 1583 expected_duration = sum(r.duration for r in regions)
amine@88 1584 expected_data = b"".join(bytes(r) for r in regions)
amine@88 1585 concat_region = sum(regions)
amine@88 1586
amine@299 1587 self.assertAlmostEqual(
amine@299 1588 concat_region.duration, expected_duration, places=6
amine@299 1589 )
amine@88 1590 self.assertEqual(bytes(concat_region), expected_data)
amine@88 1591
amine@88 1592 def test_concatenation_different_sampling_rate_error(self):
amine@88 1593
amine@244 1594 region_1 = AudioRegion(b"a" * 100, 8000, 1, 1)
amine@244 1595 region_2 = AudioRegion(b"b" * 100, 3000, 1, 1)
amine@88 1596
amine@88 1597 with self.assertRaises(ValueError) as val_err:
amine@88 1598 region_1 + region_2
amine@88 1599 self.assertEqual(
amine@88 1600 "Can only concatenate AudioRegions of the same "
amine@88 1601 "sampling rate (8000 != 3000)",
amine@88 1602 str(val_err.exception),
amine@88 1603 )
amine@88 1604
amine@88 1605 def test_concatenation_different_sample_width_error(self):
amine@88 1606
amine@244 1607 region_1 = AudioRegion(b"a" * 100, 8000, 2, 1)
amine@244 1608 region_2 = AudioRegion(b"b" * 100, 8000, 4, 1)
amine@88 1609
amine@88 1610 with self.assertRaises(ValueError) as val_err:
amine@88 1611 region_1 + region_2
amine@88 1612 self.assertEqual(
amine@299 1613 "Can only concatenate AudioRegions of the same "
amine@299 1614 "sample width (2 != 4)",
amine@88 1615 str(val_err.exception),
amine@88 1616 )
amine@88 1617
amine@88 1618 def test_concatenation_different_number_of_channels_error(self):
amine@88 1619
amine@244 1620 region_1 = AudioRegion(b"a" * 100, 8000, 1, 1)
amine@244 1621 region_2 = AudioRegion(b"b" * 100, 8000, 1, 2)
amine@88 1622
amine@88 1623 with self.assertRaises(ValueError) as val_err:
amine@88 1624 region_1 + region_2
amine@88 1625 self.assertEqual(
amine@88 1626 "Can only concatenate AudioRegions of the same "
amine@88 1627 "number of channels (1 != 2)",
amine@88 1628 str(val_err.exception),
amine@88 1629 )
amine@196 1630
amine@196 1631 @genty_dataset(
amine@245 1632 simple=(0.01, 0.03, 240, 30),
amine@245 1633 rounded_len_floor=(0.00575, 0.01725, 138, 17),
amine@245 1634 rounded_len_ceil=(0.00625, 0.01875, 150, 19),
amine@196 1635 )
amine@196 1636 def test_multiplication(
amine@245 1637 self, duration, expected_duration, expected_len, expected_len_ms
amine@196 1638 ):
amine@196 1639 sw = 2
amine@196 1640 data = b"0" * int(duration * 8000 * sw)
amine@244 1641 region = AudioRegion(data, 8000, sw, 1)
amine@196 1642 m_region = 1 * region * 3
amine@196 1643 self.assertEqual(bytes(m_region), data * 3)
amine@196 1644 self.assertEqual(m_region.sr, 8000)
amine@196 1645 self.assertEqual(m_region.sw, 2)
amine@196 1646 self.assertEqual(m_region.ch, 1)
amine@196 1647 self.assertEqual(m_region.duration, expected_duration)
amine@245 1648 self.assertEqual(len(m_region), expected_len)
amine@245 1649 self.assertEqual(m_region.len, expected_len)
amine@245 1650 self.assertEqual(m_region.s.len, expected_duration)
amine@245 1651 self.assertEqual(len(m_region.ms), expected_len_ms)
amine@245 1652 self.assertEqual(m_region.ms.len, expected_len_ms)
amine@197 1653
amine@198 1654 @genty_dataset(_str=("x", "str"), _float=(1.4, "float"))
amine@197 1655 def test_multiplication_non_int(self, factor, _type):
amine@197 1656 with self.assertRaises(TypeError) as type_err:
amine@244 1657 AudioRegion(b"0" * 80, 8000, 1, 1) * factor
amine@197 1658 err_msg = "Can't multiply AudioRegion by a non-int of type '{}'"
amine@197 1659 self.assertEqual(err_msg.format(_type), str(type_err.exception))
amine@254 1660
amine@254 1661 @genty_dataset(
amine@254 1662 simple=([b"a" * 80, b"b" * 80],),
amine@254 1663 extra_samples_1=([b"a" * 31, b"b" * 31, b"c" * 30],),
amine@254 1664 extra_samples_2=([b"a" * 31, b"b" * 30, b"c" * 30],),
amine@254 1665 extra_samples_3=([b"a" * 11, b"b" * 11, b"c" * 10, b"c" * 10],),
amine@254 1666 )
amine@252 1667 def test_truediv(self, data):
amine@254 1668
amine@252 1669 region = AudioRegion(b"".join(data), 80, 1, 1)
amine@252 1670
amine@252 1671 sub_regions = region / len(data)
amine@252 1672 for data_i, region in zip(data, sub_regions):
amine@252 1673 self.assertEqual(len(data_i), len(bytes(region)))
amine@254 1674
amine@254 1675 @genty_dataset(
amine@254 1676 mono_sw_1=(b"a" * 10, 1, 1, "b", [97] * 10),
amine@254 1677 mono_sw_2=(b"a" * 10, 2, 1, "h", [24929] * 5),
amine@254 1678 mono_sw_4=(b"a" * 8, 4, 1, "i", [1633771873] * 2),
amine@254 1679 stereo_sw_1=(b"ab" * 5, 1, 2, "b", [[97] * 5, [98] * 5]),
amine@254 1680 )
amine@254 1681 def test_samples(self, data, sample_width, channels, fmt, expected):
amine@254 1682
amine@254 1683 region = AudioRegion(data, 10, sample_width, channels)
amine@254 1684 if isinstance(expected[0], list):
amine@254 1685 expected = [array_(fmt, exp) for exp in expected]
amine@254 1686 else:
amine@254 1687 expected = array_(fmt, expected)
amine@254 1688 samples = region.samples
amine@254 1689 equal = samples == expected
amine@254 1690 try:
amine@254 1691 # for numpy
amine@254 1692 equal = equal.all()
amine@327 1693 except AttributeError:
amine@254 1694 pass
amine@254 1695 self.assertTrue(equal)
amine@337 1696
amine@337 1697
amine@337 1698 if __name__ == "__main__":
amine@337 1699 unittest.main()