amine@86
|
1 import unittest
|
amine@88
|
2 from random import random
|
amine@86
|
3 from genty import genty, genty_dataset
|
amine@97
|
4 from auditok import AudioRegion, AudioParameterError
|
amine@86
|
5
|
amine@86
|
6
|
amine@88
|
7 def _make_random_length_regions(
|
amine@88
|
8 byte_seq, sampling_rate, sample_width, channels
|
amine@88
|
9 ):
|
amine@88
|
10 regions = []
|
amine@88
|
11 for b in byte_seq:
|
amine@88
|
12 duration = round(random() * 10, 6)
|
amine@95
|
13 data = b * int(duration * sampling_rate) * sample_width * channels
|
amine@88
|
14 start = round(random() * 13, 3)
|
amine@88
|
15 region = AudioRegion(
|
amine@88
|
16 data, start, sampling_rate, sample_width, channels
|
amine@88
|
17 )
|
amine@88
|
18 regions.append(region)
|
amine@88
|
19 return regions
|
amine@88
|
20
|
amine@88
|
21
|
amine@86
|
22 @genty
|
amine@86
|
23 class TestAudioRegion(unittest.TestCase):
|
amine@86
|
24 @genty_dataset(
|
amine@86
|
25 simple=(b"\0" * 8000, 0, 8000, 1, 1, 1, 1, 1000),
|
amine@86
|
26 one_ms_less_than_1_sec=(
|
amine@86
|
27 b"\0" * 7992,
|
amine@86
|
28 0,
|
amine@86
|
29 8000,
|
amine@86
|
30 1,
|
amine@86
|
31 1,
|
amine@86
|
32 0.999,
|
amine@86
|
33 0.999,
|
amine@86
|
34 999,
|
amine@86
|
35 ),
|
amine@86
|
36 tree_quarter_ms_less_than_1_sec=(
|
amine@86
|
37 b"\0" * 7994,
|
amine@86
|
38 0,
|
amine@86
|
39 8000,
|
amine@86
|
40 1,
|
amine@86
|
41 1,
|
amine@86
|
42 0.99925,
|
amine@86
|
43 0.99925,
|
amine@86
|
44 999,
|
amine@86
|
45 ),
|
amine@86
|
46 half_ms_less_than_1_sec=(
|
amine@86
|
47 b"\0" * 7996,
|
amine@86
|
48 0,
|
amine@86
|
49 8000,
|
amine@86
|
50 1,
|
amine@86
|
51 1,
|
amine@86
|
52 0.9995,
|
amine@86
|
53 0.9995,
|
amine@86
|
54 1000,
|
amine@86
|
55 ),
|
amine@86
|
56 quarter_ms_less_than_1_sec=(
|
amine@86
|
57 b"\0" * 7998,
|
amine@86
|
58 0,
|
amine@86
|
59 8000,
|
amine@86
|
60 1,
|
amine@86
|
61 1,
|
amine@86
|
62 0.99975,
|
amine@86
|
63 0.99975,
|
amine@86
|
64 1000,
|
amine@86
|
65 ),
|
amine@86
|
66 simple_sample_width_2=(b"\0" * 8000 * 2, 0, 8000, 2, 1, 1, 1, 1000),
|
amine@86
|
67 simple_stereo=(b"\0" * 8000 * 2, 0, 8000, 1, 2, 1, 1, 1000),
|
amine@86
|
68 simple_multichannel=(b"\0" * 8000 * 5, 0, 8000, 1, 5, 1, 1, 1000),
|
amine@86
|
69 simple_sample_width_2_multichannel=(
|
amine@86
|
70 b"\0" * 8000 * 2 * 5,
|
amine@86
|
71 0,
|
amine@86
|
72 8000,
|
amine@86
|
73 2,
|
amine@86
|
74 5,
|
amine@86
|
75 1,
|
amine@86
|
76 1,
|
amine@86
|
77 1000,
|
amine@86
|
78 ),
|
amine@86
|
79 one_ms_less_than_1s_sw_2_multichannel=(
|
amine@86
|
80 b"\0" * 7992 * 2 * 5,
|
amine@86
|
81 0,
|
amine@86
|
82 8000,
|
amine@86
|
83 2,
|
amine@86
|
84 5,
|
amine@86
|
85 0.999,
|
amine@86
|
86 0.999,
|
amine@86
|
87 999,
|
amine@86
|
88 ),
|
amine@86
|
89 tree_qrt_ms_lt_1_s_sw_2_multichannel=(
|
amine@86
|
90 b"\0" * 7994 * 2 * 5,
|
amine@86
|
91 0,
|
amine@86
|
92 8000,
|
amine@86
|
93 2,
|
amine@86
|
94 5,
|
amine@86
|
95 0.99925,
|
amine@86
|
96 0.99925,
|
amine@86
|
97 999,
|
amine@86
|
98 ),
|
amine@86
|
99 half_ms_lt_1s_sw_2_multichannel=(
|
amine@86
|
100 b"\0" * 7996 * 2 * 5,
|
amine@86
|
101 0,
|
amine@86
|
102 8000,
|
amine@86
|
103 2,
|
amine@86
|
104 5,
|
amine@86
|
105 0.9995,
|
amine@86
|
106 0.9995,
|
amine@86
|
107 1000,
|
amine@86
|
108 ),
|
amine@86
|
109 quarter_ms_lt_1s_sw_2_multichannel=(
|
amine@86
|
110 b"\0" * 7998 * 2 * 5,
|
amine@86
|
111 0,
|
amine@86
|
112 8000,
|
amine@86
|
113 2,
|
amine@86
|
114 5,
|
amine@86
|
115 0.99975,
|
amine@86
|
116 0.99975,
|
amine@86
|
117 1000,
|
amine@86
|
118 ),
|
amine@86
|
119 arbitrary_length_1=(
|
amine@86
|
120 b"\0" * int(8000 * 1.33),
|
amine@86
|
121 2.7,
|
amine@86
|
122 8000,
|
amine@86
|
123 1,
|
amine@86
|
124 1,
|
amine@86
|
125 4.03,
|
amine@86
|
126 1.33,
|
amine@86
|
127 1330,
|
amine@86
|
128 ),
|
amine@86
|
129 arbitrary_length_2=(
|
amine@86
|
130 b"\0" * int(8000 * 0.476),
|
amine@86
|
131 11.568,
|
amine@86
|
132 8000,
|
amine@86
|
133 1,
|
amine@86
|
134 1,
|
amine@86
|
135 12.044,
|
amine@86
|
136 0.476,
|
amine@86
|
137 476,
|
amine@86
|
138 ),
|
amine@86
|
139 arbitrary_length_sw_2_multichannel=(
|
amine@86
|
140 b"\0" * int(8000 * 1.711) * 2 * 3,
|
amine@86
|
141 9.415,
|
amine@86
|
142 8000,
|
amine@86
|
143 2,
|
amine@86
|
144 3,
|
amine@86
|
145 11.126,
|
amine@86
|
146 1.711,
|
amine@86
|
147 1711,
|
amine@86
|
148 ),
|
amine@86
|
149 arbitrary_samplig_rate=(
|
amine@86
|
150 b"\0" * int(3172 * 1.318),
|
amine@86
|
151 17.236,
|
amine@86
|
152 3172,
|
amine@86
|
153 1,
|
amine@86
|
154 1,
|
amine@86
|
155 17.236 + int(3172 * 1.318) / 3172,
|
amine@86
|
156 int(3172 * 1.318) / 3172,
|
amine@86
|
157 1318,
|
amine@86
|
158 ),
|
amine@86
|
159 arbitrary_sr_sw_2_multichannel=(
|
amine@86
|
160 b"\0" * int(11317 * 0.716) * 2 * 3,
|
amine@86
|
161 18.811,
|
amine@86
|
162 11317,
|
amine@86
|
163 2,
|
amine@86
|
164 3,
|
amine@86
|
165 18.811 + int(11317 * 0.716) / 11317,
|
amine@86
|
166 int(11317 * 0.716) / 11317,
|
amine@86
|
167 716,
|
amine@86
|
168 ),
|
amine@86
|
169 )
|
amine@86
|
170 def test_creation(
|
amine@86
|
171 self,
|
amine@86
|
172 data,
|
amine@86
|
173 start,
|
amine@86
|
174 sampling_rate,
|
amine@86
|
175 sample_width,
|
amine@86
|
176 channels,
|
amine@86
|
177 expected_end,
|
amine@86
|
178 expected_duration_s,
|
amine@86
|
179 expected_duration_ms,
|
amine@86
|
180 ):
|
amine@86
|
181 region = AudioRegion(
|
amine@86
|
182 data, start, sampling_rate, sample_width, channels
|
amine@86
|
183 )
|
amine@86
|
184 self.assertEqual(region.sampling_rate, sampling_rate)
|
amine@86
|
185 self.assertEqual(region.sr, sampling_rate)
|
amine@86
|
186 self.assertEqual(region.sample_width, sample_width)
|
amine@86
|
187 self.assertEqual(region.sw, sample_width)
|
amine@86
|
188 self.assertEqual(region.channels, channels)
|
amine@86
|
189 self.assertEqual(region.ch, channels)
|
amine@86
|
190 self.assertEqual(region.start, start)
|
amine@86
|
191 self.assertEqual(region.end, expected_end)
|
amine@86
|
192 self.assertEqual(region.duration, expected_duration_s)
|
amine@86
|
193 self.assertEqual(len(region), expected_duration_ms)
|
amine@86
|
194 self.assertEqual(bytes(region), data)
|
amine@88
|
195
|
amine@97
|
196 def test_creation_invalid_data_exception(self):
|
amine@97
|
197 with self.assertRaises(AudioParameterError) as audio_param_err:
|
amine@97
|
198 _ = AudioRegion(
|
amine@97
|
199 data=b"ABCDEFGHI",
|
amine@97
|
200 start=0,
|
amine@97
|
201 sampling_rate=8,
|
amine@97
|
202 sample_width=2,
|
amine@97
|
203 channels=1,
|
amine@97
|
204 )
|
amine@97
|
205 self.assertEqual(
|
amine@97
|
206 "The length of audio data must be an integer "
|
amine@97
|
207 "multiple of `sample_width * channels`",
|
amine@97
|
208 str(audio_param_err.exception),
|
amine@97
|
209 )
|
amine@97
|
210
|
amine@88
|
211 @genty_dataset(
|
amine@88
|
212 simple=(8000, 1, 1),
|
amine@88
|
213 stereo_sw_2=(8000, 2, 2),
|
amine@88
|
214 arbitray_sr_multichannel=(5413, 2, 3),
|
amine@88
|
215 )
|
amine@88
|
216 def test_concatenation(self, sampling_rate, sample_width, channels):
|
amine@88
|
217
|
amine@88
|
218 region_1, region_2 = _make_random_length_regions(
|
amine@88
|
219 [b"a", b"b"], sampling_rate, sample_width, channels
|
amine@88
|
220 )
|
amine@88
|
221
|
amine@88
|
222 expected_start = region_1.start
|
amine@88
|
223 expected_duration = region_1.duration + region_2.duration
|
amine@88
|
224 expected_end = expected_start + expected_duration
|
amine@88
|
225 expected_data = bytes(region_1) + bytes(region_2)
|
amine@88
|
226 concat_region = region_1 + region_2
|
amine@88
|
227
|
amine@88
|
228 self.assertEqual(concat_region.start, expected_start)
|
amine@88
|
229 self.assertAlmostEqual(concat_region.end, expected_end, places=6)
|
amine@88
|
230 self.assertAlmostEqual(
|
amine@88
|
231 concat_region.duration, expected_duration, places=6
|
amine@88
|
232 )
|
amine@88
|
233 self.assertEqual(bytes(concat_region), expected_data)
|
amine@88
|
234 # due to the behavior of `round` len(concat_region) does not always
|
amine@88
|
235 # equal len(region_1) + len(region_2)
|
amine@88
|
236 # Exmaple if both regions are 1.0005 seconds long, then:
|
amine@88
|
237 # len(region_1) == len(region_2) == round(1.0005) == 1000
|
amine@88
|
238 # and:
|
amine@88
|
239 # region_1.duration + region_2.duration == 1.0005 * 2 = 2.001
|
amine@88
|
240 # and:
|
amine@88
|
241 # len(region_3) == round(2.001 * 1000) = 2001
|
amine@88
|
242 # != len(region_1) + len(region_2)
|
amine@88
|
243 self.assertEqual(len(concat_region), round(expected_duration * 1000))
|
amine@88
|
244
|
amine@88
|
245 @genty_dataset(
|
amine@88
|
246 simple=(8000, 1, 1),
|
amine@88
|
247 stereo_sw_2=(8000, 2, 2),
|
amine@88
|
248 arbitray_sr_multichannel=(5413, 2, 3),
|
amine@88
|
249 )
|
amine@88
|
250 def test_concatenation_many(self, sampling_rate, sample_width, channels):
|
amine@88
|
251
|
amine@88
|
252 regions = _make_random_length_regions(
|
amine@88
|
253 [b"a", b"b", b"c"], sampling_rate, sample_width, channels
|
amine@88
|
254 )
|
amine@88
|
255 expected_start = regions[0].start
|
amine@88
|
256 expected_duration = sum(r.duration for r in regions)
|
amine@88
|
257 expected_end = expected_start + expected_duration
|
amine@88
|
258 expected_data = b"".join(bytes(r) for r in regions)
|
amine@88
|
259 concat_region = sum(regions)
|
amine@88
|
260
|
amine@88
|
261 self.assertEqual(concat_region.start, expected_start)
|
amine@88
|
262 self.assertAlmostEqual(concat_region.end, expected_end, places=6)
|
amine@88
|
263 self.assertAlmostEqual(
|
amine@88
|
264 concat_region.duration, expected_duration, places=6
|
amine@88
|
265 )
|
amine@88
|
266 self.assertEqual(bytes(concat_region), expected_data)
|
amine@88
|
267 # see test_concatenation
|
amine@88
|
268 self.assertEqual(len(concat_region), round(expected_duration * 1000))
|
amine@88
|
269
|
amine@88
|
270 def test_concatenation_different_sampling_rate_error(self):
|
amine@88
|
271
|
amine@88
|
272 region_1 = AudioRegion(b"a" * 100, 0, 8000, 1, 1)
|
amine@88
|
273 region_2 = AudioRegion(b"b" * 100, 0, 3000, 1, 1)
|
amine@88
|
274
|
amine@88
|
275 with self.assertRaises(ValueError) as val_err:
|
amine@88
|
276 region_1 + region_2
|
amine@88
|
277 self.assertEqual(
|
amine@88
|
278 "Can only concatenate AudioRegions of the same "
|
amine@88
|
279 "sampling rate (8000 != 3000)",
|
amine@88
|
280 str(val_err.exception),
|
amine@88
|
281 )
|
amine@88
|
282
|
amine@88
|
283 def test_concatenation_different_sample_width_error(self):
|
amine@88
|
284
|
amine@88
|
285 region_1 = AudioRegion(b"a" * 100, 0, 8000, 2, 1)
|
amine@88
|
286 region_2 = AudioRegion(b"b" * 100, 0, 8000, 4, 1)
|
amine@88
|
287
|
amine@88
|
288 with self.assertRaises(ValueError) as val_err:
|
amine@88
|
289 region_1 + region_2
|
amine@88
|
290 self.assertEqual(
|
amine@88
|
291 "Can only concatenate AudioRegions of the same "
|
amine@88
|
292 "sample width (2 != 4)",
|
amine@88
|
293 str(val_err.exception),
|
amine@88
|
294 )
|
amine@88
|
295
|
amine@88
|
296 def test_concatenation_different_number_of_channels_error(self):
|
amine@88
|
297
|
amine@88
|
298 region_1 = AudioRegion(b"a" * 100, 0, 8000, 1, 1)
|
amine@88
|
299 region_2 = AudioRegion(b"b" * 100, 0, 8000, 1, 2)
|
amine@88
|
300
|
amine@88
|
301 with self.assertRaises(ValueError) as val_err:
|
amine@88
|
302 region_1 + region_2
|
amine@88
|
303 self.assertEqual(
|
amine@88
|
304 "Can only concatenate AudioRegions of the same "
|
amine@88
|
305 "number of channels (1 != 2)",
|
amine@88
|
306 str(val_err.exception),
|
amine@88
|
307 )
|