annotate tests/test_core.py @ 304:7d0f2e18add4

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