amine@157: import math amine@357: from array import array as array_ amine@403: from unittest.mock import patch amine@403: amine@405: import numpy as np amine@403: import pytest amine@403: amine@405: from auditok import signal amine@345: from auditok.exceptions import TimeFormatError amine@403: from auditok.util import ( amine@403: AudioEnergyValidator, amine@403: make_channel_selector, amine@403: make_duration_formatter, amine@403: ) amine@157: amine@157: amine@405: @pytest.fixture amine@405: def setup_data(): amine@405: return b"012345679ABC" amine@157: amine@157: amine@405: @pytest.mark.parametrize( amine@405: "fmt, duration, expected", amine@405: [ amine@405: ("%S", 5400, "5400.000"), # only_seconds amine@405: ("%I", 5400, "5400000"), # only_millis amine@405: ("%h:%m:%s.%i", 3725.365, "01:02:05.365"), # full amine@405: ("%h:%m:%s.%i", 1925.075, "00:32:05.075"), # full_zero_hours amine@405: ("%h:%m:%s.%i", 3659.075, "01:00:59.075"), # full_zero_minutes amine@405: ("%h:%m:%s.%i", 3720.075, "01:02:00.075"), # full_zero_seconds amine@405: ("%h:%m:%s.%i", 3725, "01:02:05.000"), # full_zero_millis amine@405: ( amine@405: "%h %h:%m:%s.%i %s", amine@405: 3725.365, amine@405: "01 01:02:05.365 05", amine@405: ), # duplicate_directive amine@405: ("%h:%m:%s", 3725, "01:02:05"), # no_millis amine@405: ("%h:%m", 3725, "01:02"), # no_seconds amine@405: ("%h", 3725, "01"), # no_minutes amine@405: ("%m:%s.%i", 3725, "02:05.000"), # no_hours amine@405: ], amine@405: ids=[ amine@405: "only_seconds", amine@405: "only_millis", amine@405: "full", amine@405: "full_zero_hours", amine@405: "full_zero_minutes", amine@405: "full_zero_seconds", amine@405: "full_zero_millis", amine@405: "duplicate_directive", amine@405: "no_millis", amine@405: "no_seconds", amine@405: "no_minutes", amine@405: "no_hours", amine@405: ], amine@405: ) amine@405: def test_make_duration_formatter(fmt, duration, expected): amine@405: formatter = make_duration_formatter(fmt) amine@405: result = formatter(duration) amine@405: assert result == expected amine@405: amine@405: amine@405: @pytest.mark.parametrize( amine@405: "fmt", amine@405: [ amine@405: "%S %S", # duplicate_only_seconds amine@405: "%I %I", # duplicate_only_millis amine@405: "%x", # unknown_directive amine@405: ], amine@405: ids=[ amine@405: "duplicate_only_seconds", amine@405: "duplicate_only_millis", amine@405: "unknown_directive", amine@405: ], amine@405: ) amine@405: def test_make_duration_formatter_error(fmt): amine@405: with pytest.raises(TimeFormatError): amine@405: make_duration_formatter(fmt) amine@405: amine@405: amine@405: @pytest.mark.parametrize( amine@405: "sample_width, channels, selected, expected", amine@405: [ amine@405: ( amine@405: 1, amine@405: 1, amine@405: 0, amine@405: [48, 49, 50, 51, 52, 53, 54, 55, 57, 65, 66, 67], amine@405: ), # int8_1channel_select_0 amine@405: (1, 2, 0, [48, 50, 52, 54, 57, 66]), # int8_2channel_select_0 amine@405: (1, 3, 0, [48, 51, 54, 65]), # int8_3channel_select_0 amine@405: (1, 3, 1, [49, 52, 55, 66]), # int8_3channel_select_1 amine@405: (1, 3, 2, [50, 53, 57, 67]), # int8_3channel_select_2 amine@405: (1, 4, 0, [48, 52, 57]), # int8_4channel_select_0 amine@405: ( amine@405: 2, amine@405: 1, amine@405: 0, amine@405: [12592, 13106, 13620, 14134, 16697, 17218], amine@405: ), # int16_1channel_select_0 amine@405: (2, 2, 0, [12592, 13620, 16697]), # int16_2channel_select_0 amine@405: (2, 2, 1, [13106, 14134, 17218]), # int16_2channel_select_1 amine@405: (2, 3, 0, [12592, 14134]), # int16_3channel_select_0 amine@405: (2, 3, 1, [13106, 16697]), # int16_3channel_select_1 amine@405: (2, 3, 2, [13620, 17218]), # int16_3channel_select_2 amine@405: ( amine@405: 4, amine@405: 1, amine@405: 0, amine@405: [858927408, 926299444, 1128415545], amine@405: ), # int32_1channel_select_0 amine@405: (4, 3, 0, [858927408]), # int32_3channel_select_0 amine@405: (4, 3, 1, [926299444]), # int32_3channel_select_1 amine@405: (4, 3, 2, [1128415545]), # int32_3channel_select_2 amine@405: ], amine@405: ids=[ amine@405: "int8_1channel_select_0", amine@405: "int8_2channel_select_0", amine@405: "int8_3channel_select_0", amine@405: "int8_3channel_select_1", amine@405: "int8_3channel_select_2", amine@405: "int8_4channel_select_0", amine@405: "int16_1channel_select_0", amine@405: "int16_2channel_select_0", amine@405: "int16_2channel_select_1", amine@405: "int16_3channel_select_0", amine@405: "int16_3channel_select_1", amine@405: "int16_3channel_select_2", amine@405: "int32_1channel_select_0", amine@405: "int32_3channel_select_0", amine@405: "int32_3channel_select_1", amine@405: "int32_3channel_select_2", amine@405: ], amine@405: ) amine@405: def test_make_channel_selector_one_channel( amine@405: setup_data, sample_width, channels, selected, expected amine@157: ): amine@157: amine@405: selector = make_channel_selector(sample_width, channels, selected) amine@405: result = selector(setup_data) amine@157: amine@405: dtype = signal.SAMPLE_WIDTH_TO_DTYPE[sample_width] amine@405: expected = np.array(expected).astype(dtype) amine@405: assert (result == expected).all() amine@405: amine@405: amine@405: @pytest.mark.parametrize( amine@405: "sample_width, channels, selected, expected", amine@405: [ amine@405: ( amine@405: 1, amine@405: 1, amine@405: "avg", amine@405: [48, 49, 50, 51, 52, 53, 54, 55, 57, 65, 66, 67], amine@405: ), # int8_1channel amine@405: (1, 2, "mix", [48.5, 50.5, 52.5, 54.5, 61, 66.5]), # int8_2channel amine@405: (1, 4, "average", [49.5, 53.5, 63.75]), # int8_4channel amine@405: ( amine@405: 2, amine@405: 1, amine@405: "mix", amine@405: [12592, 13106, 13620, 14134, 16697, 17218], amine@405: ), # int16_1channel amine@405: (2, 2, "avg", [12849, 13877, 16957.5]), # int16_2channel amine@405: (4, 3, "average", [971214132.33]), # int32_3channel amine@405: ], amine@405: ids=[ amine@405: "int8_1channel", amine@405: "int8_2channel", amine@405: "int8_4channel", amine@405: "int16_1channel", amine@405: "int16_2channel", amine@405: "int32_3channel", amine@405: ], amine@157: ) amine@405: def test_make_channel_selector_average( amine@405: setup_data, sample_width, channels, selected, expected amine@405: ): amine@317: amine@405: selector = make_channel_selector(sample_width, channels, selected) amine@405: result = selector(setup_data).round(2) amine@405: assert (result == expected).all() amine@317: amine@357: amine@405: @pytest.mark.parametrize( amine@405: "sample_width, channels, selected, expected", amine@405: [ amine@405: ( amine@405: 1, amine@405: 1, amine@405: "any", amine@405: [[48, 49, 50, 51, 52, 53, 54, 55, 57, 65, 66, 67]], amine@405: ), # int8_1channel amine@405: ( amine@405: 1, amine@405: 2, amine@405: None, amine@405: [[48, 50, 52, 54, 57, 66], [49, 51, 53, 55, 65, 67]], amine@405: ), # int8_2channel amine@405: ( amine@405: 1, amine@405: 4, amine@405: "any", amine@405: [[48, 52, 57], [49, 53, 65], [50, 54, 66], [51, 55, 67]], amine@405: ), # int8_4channel amine@405: ( amine@405: 2, amine@405: 2, amine@405: None, amine@405: [[12592, 13620, 16697], [13106, 14134, 17218]], amine@405: ), # int16_2channel amine@405: ( amine@405: 4, amine@405: 3, amine@405: "any", amine@405: [[858927408], [926299444], [1128415545]], amine@405: ), # int32_3channel amine@405: ], amine@405: ids=[ amine@405: "int8_1channel", amine@405: "int8_2channel", amine@405: "int8_4channel", amine@405: "int16_2channel", amine@405: "int32_3channel", amine@405: ], amine@405: ) amine@405: def test_make_channel_selector_any( amine@405: setup_data, sample_width, channels, selected, expected amine@405: ): amine@345: amine@405: # Use signal functions with numpy implementation amine@405: selector = make_channel_selector(sample_width, channels, selected) amine@405: result = selector(setup_data) amine@405: assert (result == expected).all() amine@357: amine@345: amine@400: class TestAudioEnergyValidator: amine@400: @pytest.mark.parametrize( amine@400: "data, channels, use_channel, expected", amine@400: [ amine@400: ([350, 400], 1, None, True), # mono_valid_uc_None amine@400: ([350, 400], 1, "any", True), # mono_valid_uc_any amine@400: ([350, 400], 1, 0, True), # mono_valid_uc_0 amine@400: ([350, 400], 1, "mix", True), # mono_valid_uc_mix amine@400: ([300, 300], 1, None, False), # mono_invalid_uc_None amine@400: ([300, 400, 350, 300], 2, None, True), # stereo_valid_uc_None amine@400: ([300, 400, 350, 300], 2, "any", True), # stereo_valid_uc_any amine@400: ([300, 400, 350, 300], 2, "mix", True), # stereo_valid_uc_mix amine@400: ([300, 400, 350, 300], 2, "avg", True), # stereo_valid_uc_avg amine@400: ( amine@400: [300, 400, 300, 300], amine@400: 2, amine@400: "average", amine@400: True, amine@400: ), # stereo_valid_uc_average amine@400: ( amine@400: [634, 0, 634, 0], amine@400: 2, amine@400: "mix", amine@400: True, amine@400: ), # stereo_valid_uc_mix_with_null_channel amine@400: ([320, 100, 320, 100], 2, 0, True), # stereo_valid_uc_0 amine@400: ([100, 320, 100, 320], 2, 1, True), # stereo_valid_uc_1 amine@400: ([280, 100, 280, 100], 2, None, False), # stereo_invalid_uc_None amine@400: ([280, 100, 280, 100], 2, "any", False), # stereo_invalid_uc_any amine@400: ([400, 200, 400, 200], 2, "mix", False), # stereo_invalid_uc_mix amine@400: ([300, 400, 300, 400], 2, 0, False), # stereo_invalid_uc_0 amine@400: ([400, 300, 400, 300], 2, 1, False), # stereo_invalid_uc_1 amine@400: ([0, 0, 0, 0], 2, None, False), # zeros amine@400: ], amine@400: ids=[ amine@400: "mono_valid_uc_None", amine@400: "mono_valid_uc_any", amine@400: "mono_valid_uc_0", amine@400: "mono_valid_uc_mix", amine@400: "mono_invalid_uc_None", amine@400: "stereo_valid_uc_None", amine@400: "stereo_valid_uc_any", amine@400: "stereo_valid_uc_mix", amine@400: "stereo_valid_uc_avg", amine@400: "stereo_valid_uc_average", amine@400: "stereo_valid_uc_mix_with_null_channel", amine@400: "stereo_valid_uc_0", amine@400: "stereo_valid_uc_1", amine@400: "stereo_invalid_uc_None", amine@400: "stereo_invalid_uc_any", amine@400: "stereo_invalid_uc_mix", amine@400: "stereo_invalid_uc_0", amine@400: "stereo_invalid_uc_1", amine@400: "zeros", amine@400: ], amine@317: ) amine@317: def test_audio_energy_validator( amine@317: self, data, channels, use_channel, expected amine@317: ): amine@317: amine@357: data = array_("h", data) amine@317: sample_width = 2 amine@317: energy_threshold = 50 amine@317: validator = AudioEnergyValidator( amine@317: energy_threshold, sample_width, channels, use_channel amine@317: ) amine@317: amine@317: if expected: amine@400: assert validator.is_valid(data) amine@317: else: amine@400: assert not validator.is_valid(data)