amine@164
|
1 """
|
amine@164
|
2 @author: Amine Sehili <amine.sehili@gmail.com>
|
amine@164
|
3 """
|
amine@400
|
4
|
amine@164
|
5 from array import array
|
amine@403
|
6
|
amine@405
|
7 import numpy as np
|
amine@400
|
8 import pytest
|
amine@403
|
9
|
amine@164
|
10 from auditok.io import (
|
amine@405
|
11 AudioIOError,
|
amine@164
|
12 AudioParameterError,
|
amine@164
|
13 BufferAudioSource,
|
amine@164
|
14 RawAudioSource,
|
amine@164
|
15 WaveAudioSource,
|
amine@164
|
16 )
|
amine@405
|
17 from auditok.signal import SAMPLE_WIDTH_TO_DTYPE
|
amine@405
|
18
|
amine@405
|
19
|
amine@405
|
20 def _sample_generator(*data_buffers):
|
amine@405
|
21 """
|
amine@405
|
22 Takes a list of many mono audio data buffers and makes a sample generator
|
amine@405
|
23 of interleaved audio samples, one sample from each channel. The resulting
|
amine@405
|
24 generator can be used to build a multichannel audio buffer.
|
amine@405
|
25 >>> gen = _sample_generator("abcd", "ABCD")
|
amine@405
|
26 >>> list(gen)
|
amine@405
|
27 ["a", "A", "b", "B", "c", "C", "d", "D"]
|
amine@405
|
28 """
|
amine@426
|
29 frame_gen = zip(*data_buffers)
|
amine@405
|
30 return (sample for frame in frame_gen for sample in frame)
|
amine@405
|
31
|
amine@405
|
32
|
amine@405
|
33 def _generate_pure_tone(
|
amine@405
|
34 frequency, duration_sec=1, sampling_rate=16000, sample_width=2, volume=1e4
|
amine@405
|
35 ):
|
amine@405
|
36 """
|
amine@405
|
37 Generates a pure tone with the given frequency.
|
amine@405
|
38 """
|
amine@405
|
39 assert frequency <= sampling_rate / 2
|
amine@405
|
40 max_value = (2 ** (sample_width * 8) // 2) - 1
|
amine@405
|
41 if volume > max_value:
|
amine@405
|
42 volume = max_value
|
amine@405
|
43 dtype = SAMPLE_WIDTH_TO_DTYPE[sample_width]
|
amine@405
|
44 total_samples = int(sampling_rate * duration_sec)
|
amine@405
|
45 step = frequency / sampling_rate
|
amine@405
|
46 two_pi_step = 2 * np.pi * step
|
amine@405
|
47 data = np.array(
|
amine@405
|
48 [int(np.sin(two_pi_step * i) * volume) for i in range(total_samples)]
|
amine@405
|
49 ).astype(dtype)
|
amine@405
|
50 return data
|
amine@405
|
51
|
amine@405
|
52
|
amine@405
|
53 @pytest.fixture
|
amine@405
|
54 def pure_tone_data(freq):
|
amine@405
|
55
|
amine@405
|
56 PURE_TONE_DICT = {
|
amine@405
|
57 freq: _generate_pure_tone(freq, 1, 16000, 2)
|
amine@405
|
58 for freq in (400, 800, 1600)
|
amine@405
|
59 }
|
amine@405
|
60 PURE_TONE_DICT.update(
|
amine@405
|
61 {
|
amine@405
|
62 freq: _generate_pure_tone(freq, 0.1, 16000, 2)
|
amine@405
|
63 for freq in (600, 1150, 2400, 7220)
|
amine@405
|
64 }
|
amine@405
|
65 )
|
amine@405
|
66 return PURE_TONE_DICT[freq]
|
amine@405
|
67
|
amine@405
|
68
|
amine@405
|
69 PURE_TONE_DICT = {
|
amine@405
|
70 freq: _generate_pure_tone(freq, 1, 16000, 2) for freq in (400, 800, 1600)
|
amine@405
|
71 }
|
amine@405
|
72 PURE_TONE_DICT.update(
|
amine@405
|
73 {
|
amine@405
|
74 freq: _generate_pure_tone(freq, 0.1, 16000, 2)
|
amine@405
|
75 for freq in (600, 1150, 2400, 7220)
|
amine@405
|
76 }
|
amine@405
|
77 )
|
amine@164
|
78
|
amine@164
|
79
|
amine@164
|
80 def audio_source_read_all_gen(audio_source, size=None):
|
amine@164
|
81 if size is None:
|
amine@164
|
82 size = int(audio_source.sr * 0.1) # 100ms
|
amine@164
|
83 while True:
|
amine@164
|
84 data = audio_source.read(size)
|
amine@164
|
85 if data is None:
|
amine@164
|
86 break
|
amine@164
|
87 yield data
|
amine@164
|
88
|
amine@164
|
89
|
amine@400
|
90 @pytest.mark.parametrize(
|
amine@400
|
91 "file_suffix, frequencies",
|
amine@400
|
92 [
|
amine@400
|
93 ("mono_400Hz", (400,)), # mono
|
amine@400
|
94 ("3channel_400-800-1600Hz", (400, 800, 1600)), # multichannel
|
amine@400
|
95 ],
|
amine@400
|
96 ids=["mono", "multichannel"],
|
amine@400
|
97 )
|
amine@400
|
98 def test_BufferAudioSource_read_all(file_suffix, frequencies):
|
amine@400
|
99 file = "tests/data/test_16KHZ_{}.raw".format(file_suffix)
|
amine@400
|
100 with open(file, "rb") as fp:
|
amine@400
|
101 expected = fp.read()
|
amine@400
|
102 channels = len(frequencies)
|
amine@400
|
103 audio_source = BufferAudioSource(expected, 16000, 2, channels)
|
amine@400
|
104 audio_source.open()
|
amine@400
|
105 data = audio_source.read(None)
|
amine@400
|
106 assert data == expected
|
amine@400
|
107 audio_source.rewind()
|
amine@400
|
108 data = audio_source.read(-10)
|
amine@400
|
109 assert data == expected
|
amine@400
|
110 audio_source.close()
|
amine@164
|
111
|
amine@164
|
112
|
amine@400
|
113 @pytest.mark.parametrize(
|
amine@400
|
114 "file_suffix, frequencies",
|
amine@400
|
115 [
|
amine@400
|
116 ("mono_400Hz", (400,)), # mono
|
amine@400
|
117 ("3channel_400-800-1600Hz", (400, 800, 1600)), # multichannel
|
amine@400
|
118 ],
|
amine@400
|
119 ids=["mono", "multichannel"],
|
amine@400
|
120 )
|
amine@400
|
121 def test_RawAudioSource(file_suffix, frequencies):
|
amine@400
|
122 file = "tests/data/test_16KHZ_{}.raw".format(file_suffix)
|
amine@400
|
123 channels = len(frequencies)
|
amine@400
|
124 audio_source = RawAudioSource(file, 16000, 2, channels)
|
amine@400
|
125 audio_source.open()
|
amine@400
|
126 data_read_all = b"".join(audio_source_read_all_gen(audio_source))
|
amine@400
|
127 audio_source.close()
|
amine@400
|
128 mono_channels = [PURE_TONE_DICT[freq] for freq in frequencies]
|
amine@405
|
129 dtype = SAMPLE_WIDTH_TO_DTYPE[audio_source.sample_width]
|
amine@405
|
130 expected = np.fromiter(_sample_generator(*mono_channels), dtype).tobytes()
|
amine@238
|
131
|
amine@400
|
132 assert data_read_all == expected
|
amine@240
|
133
|
amine@400
|
134 # assert read all data with None
|
amine@400
|
135 audio_source = RawAudioSource(file, 16000, 2, channels)
|
amine@400
|
136 audio_source.open()
|
amine@400
|
137 data_read_all = audio_source.read(None)
|
amine@400
|
138 audio_source.close()
|
amine@400
|
139 assert data_read_all == expected
|
amine@164
|
140
|
amine@400
|
141 # assert read all data with a negative size
|
amine@400
|
142 audio_source = RawAudioSource(file, 16000, 2, channels)
|
amine@400
|
143 audio_source.open()
|
amine@400
|
144 data_read_all = audio_source.read(-10)
|
amine@400
|
145 audio_source.close()
|
amine@400
|
146 assert data_read_all == expected
|
amine@238
|
147
|
amine@238
|
148
|
amine@400
|
149 @pytest.mark.parametrize(
|
amine@400
|
150 "file_suffix, frequencies",
|
amine@400
|
151 [
|
amine@400
|
152 ("mono_400Hz", (400,)), # mono
|
amine@400
|
153 ("3channel_400-800-1600Hz", (400, 800, 1600)), # multichannel
|
amine@400
|
154 ],
|
amine@400
|
155 ids=["mono", "multichannel"],
|
amine@400
|
156 )
|
amine@400
|
157 def test_WaveAudioSource(file_suffix, frequencies):
|
amine@400
|
158 file = "tests/data/test_16KHZ_{}.wav".format(file_suffix)
|
amine@400
|
159 audio_source = WaveAudioSource(file)
|
amine@400
|
160 audio_source.open()
|
amine@400
|
161 data = b"".join(audio_source_read_all_gen(audio_source))
|
amine@400
|
162 audio_source.close()
|
amine@400
|
163 mono_channels = [PURE_TONE_DICT[freq] for freq in frequencies]
|
amine@405
|
164 dtype = SAMPLE_WIDTH_TO_DTYPE[audio_source.sample_width]
|
amine@405
|
165 expected = np.fromiter(_sample_generator(*mono_channels), dtype).tobytes()
|
amine@164
|
166
|
amine@400
|
167 assert data == expected
|
amine@164
|
168
|
amine@400
|
169 # assert read all data with None
|
amine@400
|
170 audio_source = WaveAudioSource(file)
|
amine@400
|
171 audio_source.open()
|
amine@400
|
172 data_read_all = audio_source.read(None)
|
amine@400
|
173 audio_source.close()
|
amine@400
|
174 assert data_read_all == expected
|
amine@238
|
175
|
amine@400
|
176 # assert read all data with a negative size
|
amine@400
|
177 audio_source = WaveAudioSource(file)
|
amine@400
|
178 audio_source.open()
|
amine@400
|
179 data_read_all = audio_source.read(-10)
|
amine@400
|
180 audio_source.close()
|
amine@400
|
181 assert data_read_all == expected
|
amine@238
|
182
|
amine@164
|
183
|
amine@400
|
184 class TestBufferAudioSource_SR10_SW1_CH1:
|
amine@400
|
185 @pytest.fixture(autouse=True)
|
amine@400
|
186 def setup_and_teardown(self):
|
amine@164
|
187 self.data = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"
|
amine@164
|
188 self.audio_source = BufferAudioSource(
|
amine@321
|
189 data=self.data, sampling_rate=10, sample_width=1, channels=1
|
amine@164
|
190 )
|
amine@164
|
191 self.audio_source.open()
|
amine@400
|
192 yield
|
amine@164
|
193 self.audio_source.close()
|
amine@164
|
194
|
amine@164
|
195 def test_sr10_sw1_ch1_read_1(self):
|
amine@164
|
196 block = self.audio_source.read(1)
|
amine@164
|
197 exp = b"A"
|
amine@400
|
198 assert block == exp
|
amine@164
|
199
|
amine@164
|
200 def test_sr10_sw1_ch1_read_6(self):
|
amine@164
|
201 block = self.audio_source.read(6)
|
amine@164
|
202 exp = b"ABCDEF"
|
amine@400
|
203 assert block == exp
|
amine@164
|
204
|
amine@164
|
205 def test_sr10_sw1_ch1_read_multiple(self):
|
amine@164
|
206 block = self.audio_source.read(1)
|
amine@164
|
207 exp = b"A"
|
amine@400
|
208 assert block == exp
|
amine@164
|
209
|
amine@164
|
210 block = self.audio_source.read(6)
|
amine@164
|
211 exp = b"BCDEFG"
|
amine@400
|
212 assert block == exp
|
amine@164
|
213
|
amine@164
|
214 block = self.audio_source.read(13)
|
amine@164
|
215 exp = b"HIJKLMNOPQRST"
|
amine@400
|
216 assert block == exp
|
amine@164
|
217
|
amine@164
|
218 block = self.audio_source.read(9999)
|
amine@164
|
219 exp = b"UVWXYZ012345"
|
amine@400
|
220 assert block == exp
|
amine@164
|
221
|
amine@164
|
222 def test_sr10_sw1_ch1_read_all(self):
|
amine@164
|
223 block = self.audio_source.read(9999)
|
amine@400
|
224 assert block == self.data
|
amine@164
|
225
|
amine@164
|
226 block = self.audio_source.read(1)
|
amine@400
|
227 assert block is None
|
amine@164
|
228
|
amine@335
|
229 def test_sr10_sw1_ch1_sampling_rate(self):
|
amine@335
|
230 srate = self.audio_source.sampling_rate
|
amine@400
|
231 assert srate == 10
|
amine@164
|
232
|
amine@335
|
233 def test_sr10_sw1_ch1_sample_width(self):
|
amine@335
|
234 swidth = self.audio_source.sample_width
|
amine@400
|
235 assert swidth == 1
|
amine@164
|
236
|
amine@335
|
237 def test_sr10_sw1_ch1_channels(self):
|
amine@335
|
238 channels = self.audio_source.channels
|
amine@400
|
239 assert channels == 1
|
amine@164
|
240
|
amine@400
|
241 @pytest.mark.parametrize(
|
amine@400
|
242 "block_sizes, expected_sample, expected_second, expected_ms",
|
amine@400
|
243 [
|
amine@400
|
244 ([], 0, 0, 0), # empty
|
amine@400
|
245 ([0], 0, 0, 0), # zero
|
amine@400
|
246 ([5], 5, 0.5, 500), # five
|
amine@400
|
247 ([5, 20], 25, 2.5, 2500), # multiple
|
amine@400
|
248 ],
|
amine@400
|
249 ids=["empty", "zero", "five", "multiple"],
|
amine@166
|
250 )
|
amine@166
|
251 def test_position(
|
amine@166
|
252 self, block_sizes, expected_sample, expected_second, expected_ms
|
amine@166
|
253 ):
|
amine@166
|
254 for block_size in block_sizes:
|
amine@166
|
255 self.audio_source.read(block_size)
|
amine@166
|
256 position = self.audio_source.position
|
amine@400
|
257 assert position == expected_sample
|
amine@166
|
258
|
amine@166
|
259 position_s = self.audio_source.position_s
|
amine@400
|
260 assert position_s == expected_second
|
amine@166
|
261
|
amine@166
|
262 position_ms = self.audio_source.position_ms
|
amine@400
|
263 assert position_ms == expected_ms
|
amine@166
|
264
|
amine@400
|
265 @pytest.mark.parametrize(
|
amine@400
|
266 "position, expected_sample, expected_second, expected_ms",
|
amine@400
|
267 [
|
amine@400
|
268 (0, 0, 0, 0), # zero
|
amine@400
|
269 (1, 1, 0.1, 100), # one
|
amine@400
|
270 (10, 10, 1, 1000), # ten
|
amine@400
|
271 (-1, 31, 3.1, 3100), # negative_1
|
amine@400
|
272 (-7, 25, 2.5, 2500), # negative_2
|
amine@400
|
273 ],
|
amine@400
|
274 ids=["zero", "one", "ten", "negative_1", "negative_2"],
|
amine@166
|
275 )
|
amine@166
|
276 def test_position_setter(
|
amine@166
|
277 self, position, expected_sample, expected_second, expected_ms
|
amine@166
|
278 ):
|
amine@166
|
279 self.audio_source.position = position
|
amine@166
|
280
|
amine@166
|
281 position = self.audio_source.position
|
amine@400
|
282 assert position == expected_sample
|
amine@166
|
283
|
amine@166
|
284 position_s = self.audio_source.position_s
|
amine@400
|
285 assert position_s == expected_second
|
amine@166
|
286
|
amine@166
|
287 position_ms = self.audio_source.position_ms
|
amine@400
|
288 assert position_ms == expected_ms
|
amine@166
|
289
|
amine@400
|
290 @pytest.mark.parametrize(
|
amine@400
|
291 "position_s, expected_sample, expected_second, expected_ms",
|
amine@400
|
292 [
|
amine@400
|
293 (0, 0, 0, 0), # zero
|
amine@400
|
294 (0.1, 1, 0.1, 100), # one
|
amine@400
|
295 (1, 10, 1, 1000), # ten
|
amine@400
|
296 (-0.1, 31, 3.1, 3100), # negative_1
|
amine@400
|
297 (-0.7, 25, 2.5, 2500), # negative_2
|
amine@400
|
298 ],
|
amine@400
|
299 ids=["zero", "one", "ten", "negative_1", "negative_2"],
|
amine@166
|
300 )
|
amine@166
|
301 def test_position_s_setter(
|
amine@166
|
302 self, position_s, expected_sample, expected_second, expected_ms
|
amine@166
|
303 ):
|
amine@166
|
304 self.audio_source.position_s = position_s
|
amine@166
|
305
|
amine@166
|
306 position = self.audio_source.position
|
amine@400
|
307 assert position == expected_sample
|
amine@166
|
308
|
amine@166
|
309 position_s = self.audio_source.position_s
|
amine@400
|
310 assert position_s == expected_second
|
amine@166
|
311
|
amine@166
|
312 position_ms = self.audio_source.position_ms
|
amine@400
|
313 assert position_ms == expected_ms
|
amine@166
|
314
|
amine@400
|
315 @pytest.mark.parametrize(
|
amine@400
|
316 "position_ms, expected_sample, expected_second, expected_ms",
|
amine@400
|
317 [
|
amine@400
|
318 (0, 0, 0, 0), # zero
|
amine@400
|
319 (100, 1, 0.1, 100), # one
|
amine@400
|
320 (1000, 10, 1, 1000), # ten
|
amine@400
|
321 (-100, 31, 3.1, 3100), # negative_1
|
amine@400
|
322 (-700, 25, 2.5, 2500), # negative_2
|
amine@400
|
323 ],
|
amine@400
|
324 ids=["zero", "one", "ten", "negative_1", "negative_2"],
|
amine@166
|
325 )
|
amine@166
|
326 def test_position_ms_setter(
|
amine@166
|
327 self, position_ms, expected_sample, expected_second, expected_ms
|
amine@166
|
328 ):
|
amine@166
|
329 self.audio_source.position_ms = position_ms
|
amine@166
|
330
|
amine@166
|
331 position = self.audio_source.position
|
amine@400
|
332 assert position == expected_sample
|
amine@166
|
333
|
amine@166
|
334 position_s = self.audio_source.position_s
|
amine@400
|
335 assert position_s == expected_second
|
amine@166
|
336
|
amine@166
|
337 position_ms = self.audio_source.position_ms
|
amine@400
|
338 assert position_ms == expected_ms
|
amine@166
|
339
|
amine@400
|
340 @pytest.mark.parametrize(
|
amine@400
|
341 "position",
|
amine@400
|
342 [
|
amine@400
|
343 100, # positive
|
amine@400
|
344 -100, # negative
|
amine@400
|
345 ],
|
amine@400
|
346 ids=["positive", "negative"],
|
amine@400
|
347 )
|
amine@166
|
348 def test_position_setter_out_of_range(self, position):
|
amine@400
|
349 with pytest.raises(IndexError):
|
amine@166
|
350 self.audio_source.position = position
|
amine@166
|
351
|
amine@400
|
352 @pytest.mark.parametrize(
|
amine@400
|
353 "position_s",
|
amine@400
|
354 [
|
amine@400
|
355 100, # positive
|
amine@400
|
356 -100, # negative
|
amine@400
|
357 ],
|
amine@400
|
358 ids=["positive", "negative"],
|
amine@400
|
359 )
|
amine@166
|
360 def test_position_s_setter_out_of_range(self, position_s):
|
amine@400
|
361 with pytest.raises(IndexError):
|
amine@166
|
362 self.audio_source.position_s = position_s
|
amine@166
|
363
|
amine@400
|
364 @pytest.mark.parametrize(
|
amine@400
|
365 "position_ms",
|
amine@400
|
366 [
|
amine@400
|
367 10000, # positive
|
amine@400
|
368 -10000, # negative
|
amine@400
|
369 ],
|
amine@400
|
370 ids=["positive", "negative"],
|
amine@400
|
371 )
|
amine@166
|
372 def test_position_ms_setter_out_of_range(self, position_ms):
|
amine@400
|
373 with pytest.raises(IndexError):
|
amine@166
|
374 self.audio_source.position_ms = position_ms
|
amine@166
|
375
|
amine@330
|
376 def test_sr10_sw1_ch1_initial_position_s_0(self):
|
amine@216
|
377 tp = self.audio_source.position_s
|
amine@400
|
378 assert tp == 0.0
|
amine@164
|
379
|
amine@330
|
380 def test_sr10_sw1_ch1_position_s_1_after_read(self):
|
amine@330
|
381 srate = self.audio_source.sampling_rate
|
amine@164
|
382 # read one second
|
amine@164
|
383 self.audio_source.read(srate)
|
amine@216
|
384 tp = self.audio_source.position_s
|
amine@400
|
385 assert tp == 1.0
|
amine@164
|
386
|
amine@216
|
387 def test_sr10_sw1_ch1_position_s_2_5(self):
|
amine@164
|
388 # read 2.5 seconds
|
amine@164
|
389 self.audio_source.read(25)
|
amine@216
|
390 tp = self.audio_source.position_s
|
amine@400
|
391 assert tp == 2.5
|
amine@164
|
392
|
amine@216
|
393 def test_sr10_sw1_ch1_position_s_0(self):
|
amine@164
|
394 self.audio_source.read(10)
|
amine@216
|
395 self.audio_source.position_s = 0
|
amine@216
|
396 tp = self.audio_source.position_s
|
amine@400
|
397 assert tp == 0.0
|
amine@164
|
398
|
amine@216
|
399 def test_sr10_sw1_ch1_position_s_1(self):
|
amine@216
|
400 self.audio_source.position_s = 1
|
amine@216
|
401 tp = self.audio_source.position_s
|
amine@400
|
402 assert tp == 1.0
|
amine@164
|
403
|
amine@164
|
404 def test_sr10_sw1_ch1_rewind(self):
|
amine@164
|
405 self.audio_source.read(10)
|
amine@164
|
406 self.audio_source.rewind()
|
amine@169
|
407 tp = self.audio_source.position
|
amine@400
|
408 assert tp == 0
|
amine@164
|
409
|
amine@164
|
410 def test_sr10_sw1_ch1_read_closed(self):
|
amine@164
|
411 self.audio_source.close()
|
amine@405
|
412 with pytest.raises(AudioIOError):
|
amine@164
|
413 self.audio_source.read(1)
|
amine@164
|
414
|
amine@209
|
415
|
amine@400
|
416 class TestBufferAudioSource_SR16_SW2_CH1:
|
amine@400
|
417 @pytest.fixture(autouse=True)
|
amine@400
|
418 def setup_and_teardown(self):
|
amine@164
|
419 self.data = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"
|
amine@164
|
420 self.audio_source = BufferAudioSource(
|
amine@321
|
421 data=self.data, sampling_rate=16, sample_width=2, channels=1
|
amine@164
|
422 )
|
amine@164
|
423 self.audio_source.open()
|
amine@400
|
424 yield
|
amine@164
|
425 self.audio_source.close()
|
amine@164
|
426
|
amine@164
|
427 def test_sr16_sw2_ch1_read_1(self):
|
amine@164
|
428 block = self.audio_source.read(1)
|
amine@164
|
429 exp = b"AB"
|
amine@400
|
430 assert block == exp
|
amine@164
|
431
|
amine@164
|
432 def test_sr16_sw2_ch1_read_6(self):
|
amine@164
|
433 block = self.audio_source.read(6)
|
amine@164
|
434 exp = b"ABCDEFGHIJKL"
|
amine@400
|
435 assert block == exp
|
amine@164
|
436
|
amine@164
|
437 def test_sr16_sw2_ch1_read_multiple(self):
|
amine@164
|
438 block = self.audio_source.read(1)
|
amine@164
|
439 exp = b"AB"
|
amine@400
|
440 assert block == exp
|
amine@164
|
441
|
amine@164
|
442 block = self.audio_source.read(6)
|
amine@164
|
443 exp = b"CDEFGHIJKLMN"
|
amine@400
|
444 assert block == exp
|
amine@164
|
445
|
amine@164
|
446 block = self.audio_source.read(5)
|
amine@164
|
447 exp = b"OPQRSTUVWX"
|
amine@400
|
448 assert block == exp
|
amine@164
|
449
|
amine@164
|
450 block = self.audio_source.read(9999)
|
amine@164
|
451 exp = b"YZ012345"
|
amine@400
|
452 assert block == exp
|
amine@164
|
453
|
amine@164
|
454 def test_sr16_sw2_ch1_read_all(self):
|
amine@164
|
455 block = self.audio_source.read(9999)
|
amine@400
|
456 assert block == self.data
|
amine@164
|
457
|
amine@164
|
458 block = self.audio_source.read(1)
|
amine@400
|
459 assert block is None
|
amine@164
|
460
|
amine@335
|
461 def test_sr16_sw2_ch1_sampling_rate(self):
|
amine@335
|
462 srate = self.audio_source.sampling_rate
|
amine@400
|
463 assert srate == 16
|
amine@164
|
464
|
amine@335
|
465 def test_sr16_sw2_ch1_sample_width(self):
|
amine@335
|
466 swidth = self.audio_source.sample_width
|
amine@400
|
467 assert swidth == 2
|
amine@164
|
468
|
amine@335
|
469 def test_sr16_sw2_ch1_channels(self):
|
amine@400
|
470 channels = self.audio_source.channels
|
amine@400
|
471 assert channels == 1
|
amine@164
|
472
|
amine@400
|
473 @pytest.mark.parametrize(
|
amine@400
|
474 "block_sizes, expected_sample, expected_second, expected_ms",
|
amine@400
|
475 [
|
amine@400
|
476 ([], 0, 0, 0), # empty
|
amine@400
|
477 ([0], 0, 0, 0), # zero
|
amine@400
|
478 ([2], 2, 2 / 16, int(2000 / 16)), # two
|
amine@400
|
479 ([11], 11, 11 / 16, int(11 * 1000 / 16)), # eleven
|
amine@400
|
480 ([4, 8], 12, 0.75, 750), # multiple
|
amine@400
|
481 ],
|
amine@400
|
482 ids=["empty", "zero", "two", "eleven", "multiple"],
|
amine@169
|
483 )
|
amine@169
|
484 def test_position(
|
amine@169
|
485 self, block_sizes, expected_sample, expected_second, expected_ms
|
amine@169
|
486 ):
|
amine@169
|
487 for block_size in block_sizes:
|
amine@169
|
488 self.audio_source.read(block_size)
|
amine@169
|
489 position = self.audio_source.position
|
amine@400
|
490 assert position == expected_sample
|
amine@169
|
491
|
amine@169
|
492 position_s = self.audio_source.position_s
|
amine@400
|
493 assert position_s == expected_second
|
amine@169
|
494
|
amine@169
|
495 position_ms = self.audio_source.position_ms
|
amine@400
|
496 assert position_ms == expected_ms
|
amine@169
|
497
|
amine@216
|
498 def test_sr16_sw2_ch1_read_position_0(self):
|
amine@169
|
499 self.audio_source.read(10)
|
amine@169
|
500 self.audio_source.position = 0
|
amine@169
|
501 pos = self.audio_source.position
|
amine@400
|
502 assert pos == 0
|
amine@164
|
503
|
amine@400
|
504 @pytest.mark.parametrize(
|
amine@400
|
505 "position, expected_sample, expected_second, expected_ms",
|
amine@400
|
506 [
|
amine@400
|
507 (0, 0, 0, 0), # zero
|
amine@400
|
508 (1, 1, 1 / 16, int(1000 / 16)), # one
|
amine@400
|
509 (10, 10, 10 / 16, int(10000 / 16)), # ten
|
amine@400
|
510 (-1, 15, 15 / 16, int(15000 / 16)), # negative_1
|
amine@400
|
511 (-7, 9, 9 / 16, int(9000 / 16)), # negative_2
|
amine@400
|
512 ],
|
amine@400
|
513 ids=["zero", "one", "ten", "negative_1", "negative_2"],
|
amine@169
|
514 )
|
amine@169
|
515 def test_position_setter(
|
amine@169
|
516 self, position, expected_sample, expected_second, expected_ms
|
amine@169
|
517 ):
|
amine@169
|
518 self.audio_source.position = position
|
amine@169
|
519
|
amine@169
|
520 position = self.audio_source.position
|
amine@400
|
521 assert position == expected_sample
|
amine@164
|
522
|
amine@169
|
523 position_s = self.audio_source.position_s
|
amine@400
|
524 assert position_s == expected_second
|
amine@164
|
525
|
amine@169
|
526 position_ms = self.audio_source.position_ms
|
amine@400
|
527 assert position_ms == expected_ms
|
amine@164
|
528
|
amine@400
|
529 @pytest.mark.parametrize(
|
amine@400
|
530 "position_s, expected_sample, expected_second, expected_ms",
|
amine@400
|
531 [
|
amine@400
|
532 (0, 0, 0, 0), # zero
|
amine@400
|
533 (0.1, 1, 1 / 16, int(1000 / 16)), # one
|
amine@400
|
534 (1 / 8, 2, 1 / 8, int(1 / 8 * 1000)), # two
|
amine@400
|
535 (0.75, 12, 0.75, 750), # twelve
|
amine@400
|
536 (-0.1, 15, 15 / 16, int(15000 / 16)), # negative_1
|
amine@400
|
537 (-0.7, 5, 5 / 16, int(5000 / 16)), # negative_2
|
amine@400
|
538 ],
|
amine@400
|
539 ids=["zero", "one", "two", "twelve", "negative_1", "negative_2"],
|
amine@169
|
540 )
|
amine@169
|
541 def test_position_s_setter(
|
amine@169
|
542 self, position_s, expected_sample, expected_second, expected_ms
|
amine@169
|
543 ):
|
amine@169
|
544 self.audio_source.position_s = position_s
|
amine@169
|
545
|
amine@169
|
546 position = self.audio_source.position
|
amine@400
|
547 assert position == expected_sample
|
amine@164
|
548
|
amine@169
|
549 position_s = self.audio_source.position_s
|
amine@400
|
550 assert position_s == expected_second
|
amine@164
|
551
|
amine@169
|
552 position_ms = self.audio_source.position_ms
|
amine@400
|
553 assert position_ms == expected_ms
|
amine@164
|
554
|
amine@400
|
555 @pytest.mark.parametrize(
|
amine@400
|
556 "position_ms, expected_sample, expected_second, expected_ms",
|
amine@400
|
557 [
|
amine@400
|
558 (0, 0, 0, 0), # zero
|
amine@400
|
559 (100, 1, 1 / 16, int(1000 / 16)), # one
|
amine@400
|
560 (1000, 16, 1, 1000), # ten
|
amine@400
|
561 (-100, 15, 15 / 16, int(15 * 1000 / 16)), # negative_1
|
amine@400
|
562 (-500, 8, 0.5, 500), # negative_2
|
amine@400
|
563 (-700, 5, 5 / 16, int(5 * 1000 / 16)), # negative_3
|
amine@400
|
564 ],
|
amine@400
|
565 ids=["zero", "one", "ten", "negative_1", "negative_2", "negative_3"],
|
amine@169
|
566 )
|
amine@169
|
567 def test_position_ms_setter(
|
amine@169
|
568 self, position_ms, expected_sample, expected_second, expected_ms
|
amine@169
|
569 ):
|
amine@169
|
570 self.audio_source.position_ms = position_ms
|
amine@169
|
571
|
amine@169
|
572 position = self.audio_source.position
|
amine@400
|
573 assert position == expected_sample
|
amine@164
|
574
|
amine@169
|
575 position_s = self.audio_source.position_s
|
amine@400
|
576 assert position_s == expected_second
|
amine@164
|
577
|
amine@169
|
578 position_ms = self.audio_source.position_ms
|
amine@400
|
579 assert position_ms == expected_ms
|
amine@164
|
580
|
amine@164
|
581 def test_sr16_sw2_ch1_rewind(self):
|
amine@164
|
582 self.audio_source.read(10)
|
amine@164
|
583 self.audio_source.rewind()
|
amine@216
|
584 tp = self.audio_source.position
|
amine@400
|
585 assert tp == 0
|
amine@164
|
586
|
amine@164
|
587
|
amine@400
|
588 class TestBufferAudioSource_SR11_SW4_CH1:
|
amine@400
|
589 @pytest.fixture(autouse=True)
|
amine@400
|
590 def setup_and_teardown(self):
|
amine@164
|
591 self.data = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh"
|
amine@164
|
592 self.audio_source = BufferAudioSource(
|
amine@321
|
593 data=self.data, sampling_rate=11, sample_width=4, channels=1
|
amine@164
|
594 )
|
amine@164
|
595 self.audio_source.open()
|
amine@400
|
596 yield
|
amine@164
|
597 self.audio_source.close()
|
amine@164
|
598
|
amine@164
|
599 def test_sr11_sw4_ch1_read_1(self):
|
amine@164
|
600 block = self.audio_source.read(1)
|
amine@164
|
601 exp = b"ABCD"
|
amine@400
|
602 assert block == exp
|
amine@164
|
603
|
amine@164
|
604 def test_sr11_sw4_ch1_read_6(self):
|
amine@164
|
605 block = self.audio_source.read(6)
|
amine@164
|
606 exp = b"ABCDEFGHIJKLMNOPQRSTUVWX"
|
amine@400
|
607 assert block == exp
|
amine@164
|
608
|
amine@164
|
609 def test_sr11_sw4_ch1_read_multiple(self):
|
amine@164
|
610 block = self.audio_source.read(1)
|
amine@164
|
611 exp = b"ABCD"
|
amine@400
|
612 assert block == exp
|
amine@164
|
613
|
amine@164
|
614 block = self.audio_source.read(6)
|
amine@164
|
615 exp = b"EFGHIJKLMNOPQRSTUVWXYZ01"
|
amine@400
|
616 assert block == exp
|
amine@164
|
617
|
amine@164
|
618 block = self.audio_source.read(3)
|
amine@164
|
619 exp = b"23456789abcd"
|
amine@400
|
620 assert block == exp
|
amine@164
|
621
|
amine@164
|
622 block = self.audio_source.read(9999)
|
amine@164
|
623 exp = b"efgh"
|
amine@400
|
624 assert block == exp
|
amine@164
|
625
|
amine@164
|
626 def test_sr11_sw4_ch1_read_all(self):
|
amine@164
|
627 block = self.audio_source.read(9999)
|
amine@400
|
628 assert block == self.data
|
amine@164
|
629
|
amine@164
|
630 block = self.audio_source.read(1)
|
amine@400
|
631 assert block is None
|
amine@164
|
632
|
amine@335
|
633 def test_sr11_sw4_ch1_sampling_rate(self):
|
amine@335
|
634 srate = self.audio_source.sampling_rate
|
amine@400
|
635 assert srate == 11
|
amine@164
|
636
|
amine@335
|
637 def test_sr11_sw4_ch1_sample_width(self):
|
amine@335
|
638 swidth = self.audio_source.sample_width
|
amine@400
|
639 assert swidth == 4
|
amine@164
|
640
|
amine@335
|
641 def test_sr11_sw4_ch1_channels(self):
|
amine@335
|
642 channels = self.audio_source.channels
|
amine@400
|
643 assert channels == 1
|
amine@164
|
644
|
amine@330
|
645 def test_sr11_sw4_ch1_intial_position_0(self):
|
amine@216
|
646 pos = self.audio_source.position
|
amine@400
|
647 assert pos == 0
|
amine@164
|
648
|
amine@216
|
649 def test_sr11_sw4_ch1_position_5(self):
|
amine@164
|
650 self.audio_source.read(5)
|
amine@216
|
651 pos = self.audio_source.position
|
amine@400
|
652 assert pos == 5
|
amine@164
|
653
|
amine@216
|
654 def test_sr11_sw4_ch1_position_9(self):
|
amine@164
|
655 self.audio_source.read(5)
|
amine@164
|
656 self.audio_source.read(4)
|
amine@216
|
657 pos = self.audio_source.position
|
amine@400
|
658 assert pos == 9
|
amine@164
|
659
|
amine@216
|
660 def test_sr11_sw4_ch1_position_0(self):
|
amine@164
|
661 self.audio_source.read(10)
|
amine@216
|
662 self.audio_source.position = 0
|
amine@216
|
663 pos = self.audio_source.position
|
amine@400
|
664 assert pos == 0
|
amine@164
|
665
|
amine@216
|
666 def test_sr11_sw4_ch1_position_10(self):
|
amine@216
|
667 self.audio_source.position = 10
|
amine@216
|
668 pos = self.audio_source.position
|
amine@400
|
669 assert pos == 10
|
amine@164
|
670
|
amine@330
|
671 def test_sr11_sw4_ch1_initial_position_s_0(self):
|
amine@216
|
672 tp = self.audio_source.position_s
|
amine@400
|
673 assert tp == 0.0
|
amine@164
|
674
|
amine@330
|
675 def test_sr11_sw4_ch1_position_s_1_after_read(self):
|
amine@330
|
676 srate = self.audio_source.sampling_rate
|
amine@164
|
677 # read one second
|
amine@164
|
678 self.audio_source.read(srate)
|
amine@216
|
679 tp = self.audio_source.position_s
|
amine@400
|
680 assert tp == 1.0
|
amine@164
|
681
|
amine@216
|
682 def test_sr11_sw4_ch1_position_s_0_63(self):
|
amine@164
|
683 # read 2.5 seconds
|
amine@164
|
684 self.audio_source.read(7)
|
amine@216
|
685 tp = self.audio_source.position_s
|
amine@400
|
686 assert tp, pytest.approx(0.636363636364)
|
amine@164
|
687
|
amine@216
|
688 def test_sr11_sw4_ch1_position_s_0(self):
|
amine@164
|
689 self.audio_source.read(10)
|
amine@216
|
690 self.audio_source.position_s = 0
|
amine@216
|
691 tp = self.audio_source.position_s
|
amine@400
|
692 assert tp == 0.0
|
amine@164
|
693
|
amine@216
|
694 def test_sr11_sw4_ch1_position_s_1(self):
|
amine@216
|
695 self.audio_source.position_s = 1
|
amine@216
|
696 tp = self.audio_source.position_s
|
amine@400
|
697 assert tp == 1.0
|
amine@164
|
698
|
amine@164
|
699 def test_sr11_sw4_ch1_rewind(self):
|
amine@164
|
700 self.audio_source.read(10)
|
amine@164
|
701 self.audio_source.rewind()
|
amine@169
|
702 tp = self.audio_source.position
|
amine@400
|
703 assert tp == 0
|
amine@164
|
704
|
amine@164
|
705
|
amine@400
|
706 class TestBufferAudioSourceCreationException:
|
amine@164
|
707 def test_wrong_sample_width_value(self):
|
amine@400
|
708 with pytest.raises(AudioParameterError) as audio_param_err:
|
amine@164
|
709 _ = BufferAudioSource(
|
amine@321
|
710 data=b"ABCDEFGHI", sampling_rate=9, sample_width=3, channels=1
|
amine@164
|
711 )
|
amine@400
|
712 assert (
|
amine@400
|
713 str(audio_param_err.value)
|
amine@400
|
714 == "Sample width must be one of: 1, 2 or 4 (bytes)"
|
amine@164
|
715 )
|
amine@164
|
716
|
amine@164
|
717 def test_wrong_data_buffer_size(self):
|
amine@400
|
718 with pytest.raises(AudioParameterError) as audio_param_err:
|
amine@164
|
719 _ = BufferAudioSource(
|
amine@321
|
720 data=b"ABCDEFGHI", sampling_rate=8, sample_width=2, channels=1
|
amine@164
|
721 )
|
amine@405
|
722 msg = "The length of audio data must be an integer multiple of "
|
amine@405
|
723 msg += "`sample_width * channels`"
|
amine@405
|
724 assert str(audio_param_err.value) == msg
|
amine@164
|
725
|
amine@164
|
726
|
amine@400
|
727 class TestAudioSourceProperties:
|
amine@164
|
728 def test_read_properties(self):
|
amine@164
|
729 data = b""
|
amine@164
|
730 sampling_rate = 8000
|
amine@164
|
731 sample_width = 2
|
amine@164
|
732 channels = 1
|
amine@164
|
733 a_source = BufferAudioSource(
|
amine@164
|
734 data, sampling_rate, sample_width, channels
|
amine@164
|
735 )
|
amine@164
|
736
|
amine@400
|
737 assert a_source.sampling_rate == sampling_rate
|
amine@400
|
738 assert a_source.sample_width == sample_width
|
amine@400
|
739 assert a_source.channels == channels
|
amine@164
|
740
|
amine@164
|
741 def test_set_readonly_properties_exception(self):
|
amine@164
|
742 data = b""
|
amine@164
|
743 sampling_rate = 8000
|
amine@164
|
744 sample_width = 2
|
amine@164
|
745 channels = 1
|
amine@164
|
746 a_source = BufferAudioSource(
|
amine@164
|
747 data, sampling_rate, sample_width, channels
|
amine@164
|
748 )
|
amine@164
|
749
|
amine@400
|
750 with pytest.raises(AttributeError):
|
amine@164
|
751 a_source.sampling_rate = 16000
|
amine@405
|
752
|
amine@405
|
753 with pytest.raises(AttributeError):
|
amine@164
|
754 a_source.sample_width = 1
|
amine@405
|
755
|
amine@405
|
756 with pytest.raises(AttributeError):
|
amine@164
|
757 a_source.channels = 2
|
amine@164
|
758
|
amine@164
|
759
|
amine@400
|
760 class TestAudioSourceShortProperties:
|
amine@164
|
761 def test_read_short_properties(self):
|
amine@164
|
762 data = b""
|
amine@164
|
763 sampling_rate = 8000
|
amine@164
|
764 sample_width = 2
|
amine@164
|
765 channels = 1
|
amine@164
|
766 a_source = BufferAudioSource(
|
amine@164
|
767 data, sampling_rate, sample_width, channels
|
amine@164
|
768 )
|
amine@164
|
769
|
amine@400
|
770 assert a_source.sr == sampling_rate
|
amine@400
|
771 assert a_source.sw == sample_width
|
amine@400
|
772 assert a_source.ch == channels
|
amine@164
|
773
|
amine@164
|
774 def test_set_readonly_short_properties_exception(self):
|
amine@164
|
775 data = b""
|
amine@164
|
776 sampling_rate = 8000
|
amine@164
|
777 sample_width = 2
|
amine@164
|
778 channels = 1
|
amine@164
|
779 a_source = BufferAudioSource(
|
amine@164
|
780 data, sampling_rate, sample_width, channels
|
amine@164
|
781 )
|
amine@164
|
782
|
amine@400
|
783 with pytest.raises(AttributeError):
|
amine@164
|
784 a_source.sr = 16000
|
amine@405
|
785
|
amine@405
|
786 with pytest.raises(AttributeError):
|
amine@164
|
787 a_source.sw = 1
|
amine@405
|
788
|
amine@405
|
789 with pytest.raises(AttributeError):
|
amine@164
|
790 a_source.ch = 2
|