annotate tests/test_core.py @ 415:e26dcf224846

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