annotate tests/test_core.py @ 244:ee6d2294cdd5

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