annotate tests/test_core.py @ 307:334c8760e80f

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