amine@337
|
1 import unittest
|
amine@317
|
2 from unittest import TestCase
|
amine@157
|
3 import math
|
amine@157
|
4 from array import array
|
amine@317
|
5 from genty import genty, genty_dataset
|
amine@345
|
6 from auditok.util import AudioEnergyValidator, make_duration_formatter
|
amine@321
|
7 from auditok.signal import FORMAT
|
amine@345
|
8 from auditok.exceptions import TimeFormatError
|
amine@157
|
9
|
amine@157
|
10
|
amine@157
|
11 def _sample_generator(*data_buffers):
|
amine@157
|
12 """
|
amine@157
|
13 Takes a list of many mono audio data buffers and makes a sample generator
|
amine@157
|
14 of interleaved audio samples, one sample from each channel. The resulting
|
amine@157
|
15 generator can be used to build a multichannel audio buffer.
|
amine@157
|
16 >>> gen = _sample_generator("abcd", "ABCD")
|
amine@157
|
17 >>> list(gen)
|
amine@157
|
18 ["a", "A", "b", "B", "c", "C", "d", "D"]
|
amine@157
|
19 """
|
amine@157
|
20 frame_gen = zip(*data_buffers)
|
amine@157
|
21 return (sample for frame in frame_gen for sample in frame)
|
amine@157
|
22
|
amine@157
|
23
|
amine@157
|
24 def _generate_pure_tone(
|
amine@157
|
25 frequency, duration_sec=1, sampling_rate=16000, sample_width=2, volume=1e4
|
amine@157
|
26 ):
|
amine@157
|
27 """
|
amine@157
|
28 Generates a pure tone with the given frequency.
|
amine@157
|
29 """
|
amine@157
|
30 assert frequency <= sampling_rate / 2
|
amine@157
|
31 max_value = (2 ** (sample_width * 8) // 2) - 1
|
amine@157
|
32 if volume > max_value:
|
amine@157
|
33 volume = max_value
|
amine@321
|
34 fmt = FORMAT[sample_width]
|
amine@157
|
35 total_samples = int(sampling_rate * duration_sec)
|
amine@157
|
36 step = frequency / sampling_rate
|
amine@157
|
37 two_pi_step = 2 * math.pi * step
|
amine@157
|
38 data = array(
|
amine@157
|
39 fmt,
|
amine@157
|
40 (
|
amine@157
|
41 int(math.sin(two_pi_step * i) * volume)
|
amine@157
|
42 for i in range(total_samples)
|
amine@157
|
43 ),
|
amine@157
|
44 )
|
amine@157
|
45 return data
|
amine@157
|
46
|
amine@157
|
47
|
amine@157
|
48 PURE_TONE_DICT = {
|
amine@157
|
49 freq: _generate_pure_tone(freq, 1, 16000, 2) for freq in (400, 800, 1600)
|
amine@157
|
50 }
|
amine@157
|
51 PURE_TONE_DICT.update(
|
amine@157
|
52 {
|
amine@157
|
53 freq: _generate_pure_tone(freq, 0.1, 16000, 2)
|
amine@157
|
54 for freq in (600, 1150, 2400, 7220)
|
amine@157
|
55 }
|
amine@157
|
56 )
|
amine@317
|
57
|
amine@317
|
58
|
amine@317
|
59 @genty
|
amine@345
|
60 class TestFunctions(TestCase):
|
amine@345
|
61 @genty_dataset(
|
amine@345
|
62 only_seconds=("%S", 5400, "5400.000"),
|
amine@345
|
63 only_millis=("%I", 5400, "5400000"),
|
amine@345
|
64 full=("%h:%m:%s.%i", 3725.365, "01:02:05.365"),
|
amine@345
|
65 full_zero_hours=("%h:%m:%s.%i", 1925.075, "00:32:05.075"),
|
amine@345
|
66 full_zero_minutes=("%h:%m:%s.%i", 3659.075, "01:00:59.075"),
|
amine@345
|
67 full_zero_seconds=("%h:%m:%s.%i", 3720.075, "01:02:00.075"),
|
amine@345
|
68 full_zero_millis=("%h:%m:%s.%i", 3725, "01:02:05.000"),
|
amine@345
|
69 duplicate_directive=(
|
amine@345
|
70 "%h %h:%m:%s.%i %s",
|
amine@345
|
71 3725.365,
|
amine@345
|
72 "01 01:02:05.365 05",
|
amine@345
|
73 ),
|
amine@345
|
74 no_millis=("%h:%m:%s", 3725, "01:02:05"),
|
amine@345
|
75 no_seconds=("%h:%m", 3725, "01:02"),
|
amine@345
|
76 no_minutes=("%h", 3725, "01"),
|
amine@345
|
77 no_hours=("%m:%s.%i", 3725, "02:05.000"),
|
amine@345
|
78 )
|
amine@345
|
79 def test_make_duration_formatter(self, fmt, duration, expected):
|
amine@345
|
80 formatter = make_duration_formatter(fmt)
|
amine@345
|
81 result = formatter(duration)
|
amine@345
|
82 self.assertEqual(result, expected)
|
amine@345
|
83
|
amine@345
|
84 @genty_dataset(
|
amine@345
|
85 duplicate_only_seconds=("%S %S",),
|
amine@345
|
86 duplicate_only_millis=("%I %I",),
|
amine@345
|
87 unknown_directive=("%x",),
|
amine@345
|
88 )
|
amine@345
|
89 def test_make_duration_formatter_error(self, fmt):
|
amine@345
|
90 with self.assertRaises(TimeFormatError):
|
amine@345
|
91 make_duration_formatter(fmt)
|
amine@345
|
92
|
amine@345
|
93
|
amine@345
|
94 @genty
|
amine@317
|
95 class TestAudioEnergyValidator(TestCase):
|
amine@317
|
96 @genty_dataset(
|
amine@317
|
97 mono_valid_uc_None=([350, 400], 1, None, True),
|
amine@317
|
98 mono_valid_uc_any=([350, 400], 1, "any", True),
|
amine@317
|
99 mono_valid_uc_0=([350, 400], 1, 0, True),
|
amine@317
|
100 mono_valid_uc_mix=([350, 400], 1, "mix", True),
|
amine@317
|
101 # previous cases are all the same since we have mono audio
|
amine@317
|
102 mono_invalid_uc_None=([300, 300], 1, None, False),
|
amine@317
|
103 stereo_valid_uc_None=([300, 400, 350, 300], 2, None, True),
|
amine@317
|
104 stereo_valid_uc_any=([300, 400, 350, 300], 2, "any", True),
|
amine@317
|
105 stereo_valid_uc_mix=([300, 400, 350, 300], 2, "mix", True),
|
amine@317
|
106 stereo_valid_uc_avg=([300, 400, 350, 300], 2, "avg", True),
|
amine@317
|
107 stereo_valid_uc_average=([300, 400, 300, 300], 2, "average", True),
|
amine@317
|
108 stereo_valid_uc_mix_with_null_channel=(
|
amine@317
|
109 [634, 0, 634, 0],
|
amine@317
|
110 2,
|
amine@317
|
111 "mix",
|
amine@317
|
112 True,
|
amine@317
|
113 ),
|
amine@317
|
114 stereo_valid_uc_0=([320, 100, 320, 100], 2, 0, True),
|
amine@317
|
115 stereo_valid_uc_1=([100, 320, 100, 320], 2, 1, True),
|
amine@317
|
116 stereo_invalid_uc_None=([280, 100, 280, 100], 2, None, False),
|
amine@317
|
117 stereo_invalid_uc_any=([280, 100, 280, 100], 2, "any", False),
|
amine@317
|
118 stereo_invalid_uc_mix=([400, 200, 400, 200], 2, "mix", False),
|
amine@317
|
119 stereo_invalid_uc_0=([300, 400, 300, 400], 2, 0, False),
|
amine@317
|
120 stereo_invalid_uc_1=([400, 300, 400, 300], 2, 1, False),
|
amine@317
|
121 zeros=([0, 0, 0, 0], 2, None, False),
|
amine@317
|
122 )
|
amine@317
|
123 def test_audio_energy_validator(
|
amine@317
|
124 self, data, channels, use_channel, expected
|
amine@317
|
125 ):
|
amine@317
|
126
|
amine@317
|
127 data = array("h", data)
|
amine@317
|
128 sample_width = 2
|
amine@317
|
129 energy_threshold = 50
|
amine@317
|
130 validator = AudioEnergyValidator(
|
amine@317
|
131 energy_threshold, sample_width, channels, use_channel
|
amine@317
|
132 )
|
amine@317
|
133
|
amine@317
|
134 if expected:
|
amine@317
|
135 self.assertTrue(validator.is_valid(data))
|
amine@317
|
136 else:
|
amine@317
|
137 self.assertFalse(validator.is_valid(data))
|
amine@337
|
138
|
amine@337
|
139
|
amine@337
|
140 if __name__ == "__main__":
|
amine@337
|
141 unittest.main()
|