1 /*
2 * Copyright © 2016 Mozilla Foundation
3 *
4 * This program is made available under an ISC-style license. See the
5 * accompanying file LICENSE for details.
6 */
7 #ifndef NOMINMAX
8 #define NOMINMAX
9 #endif // NOMINMAX
10 #include "gtest/gtest.h"
11 #include "common.h"
12 #include "cubeb_resampler_internal.h"
13 #include <stdio.h>
14 #include <algorithm>
15 #include <iostream>
16
17 /* Windows cmath USE_MATH_DEFINE thing... */
18 const float PI = 3.14159265359f;
19
20 /* Testing all sample rates is very long, so if THOROUGH_TESTING is not defined,
21 * only part of the test suite is ran. */
22 #ifdef THOROUGH_TESTING
23 /* Some standard sample rates we're testing with. */
24 const uint32_t sample_rates[] = {
25 8000,
26 16000,
27 32000,
28 44100,
29 48000,
30 88200,
31 96000,
32 192000
33 };
34 /* The maximum number of channels we're resampling. */
35 const uint32_t max_channels = 2;
36 /* The minimum an maximum number of milliseconds we're resampling for. This is
37 * used to simulate the fact that the audio stream is resampled in chunks,
38 * because audio is delivered using callbacks. */
39 const uint32_t min_chunks = 10; /* ms */
40 const uint32_t max_chunks = 30; /* ms */
41 const uint32_t chunk_increment = 1;
42
43 #else
44
45 const uint32_t sample_rates[] = {
46 8000,
47 44100,
48 48000,
49 };
50 const uint32_t max_channels = 2;
51 const uint32_t min_chunks = 10; /* ms */
52 const uint32_t max_chunks = 30; /* ms */
53 const uint32_t chunk_increment = 10;
54 #endif
55
56 #define DUMP_ARRAYS
57 #ifdef DUMP_ARRAYS
58 /**
59 * Files produced by dump(...) can be converted to .wave files using:
60 *
61 * sox -c <channel_count> -r <rate> -e float -b 32 file.raw file.wav
62 *
63 * for floating-point audio, or:
64 *
65 * sox -c <channel_count> -r <rate> -e unsigned -b 16 file.raw file.wav
66 *
67 * for 16bit integer audio.
68 */
69
70 /* Use the correct implementation of fopen, depending on the platform. */
fopen_portable(FILE ** f,const char * name,const char * mode)71 void fopen_portable(FILE ** f, const char * name, const char * mode)
72 {
73 #ifdef WIN32
74 fopen_s(f, name, mode);
75 #else
76 *f = fopen(name, mode);
77 #endif
78 }
79
80 template<typename T>
dump(const char * name,T * frames,size_t count)81 void dump(const char * name, T * frames, size_t count)
82 {
83 FILE * file;
84 fopen_portable(&file, name, "wb");
85
86 if (!file) {
87 fprintf(stderr, "error opening %s\n", name);
88 return;
89 }
90
91 if (count != fwrite(frames, sizeof(T), count, file)) {
92 fprintf(stderr, "error writing to %s\n", name);
93 }
94 fclose(file);
95 }
96 #else
97 template<typename T>
dump(const char * name,T * frames,size_t count)98 void dump(const char * name, T * frames, size_t count)
99 { }
100 #endif
101
102 // The more the ratio is far from 1, the more we accept a big error.
epsilon_tweak_ratio(float ratio)103 float epsilon_tweak_ratio(float ratio)
104 {
105 return ratio >= 1 ? ratio : 1 / ratio;
106 }
107
108 // Epsilon values for comparing resampled data to expected data.
109 // The bigger the resampling ratio is, the more lax we are about errors.
110 template<typename T>
111 T epsilon(float ratio);
112
113 template<>
epsilon(float ratio)114 float epsilon(float ratio) {
115 return 0.08f * epsilon_tweak_ratio(ratio);
116 }
117
118 template<>
epsilon(float ratio)119 int16_t epsilon(float ratio) {
120 return static_cast<int16_t>(10 * epsilon_tweak_ratio(ratio));
121 }
122
test_delay_lines(uint32_t delay_frames,uint32_t channels,uint32_t chunk_ms)123 void test_delay_lines(uint32_t delay_frames, uint32_t channels, uint32_t chunk_ms)
124 {
125 const size_t length_s = 2;
126 const size_t rate = 44100;
127 const size_t length_frames = rate * length_s;
128 delay_line<float> delay(delay_frames, channels, rate);
129 auto_array<float> input;
130 auto_array<float> output;
131 uint32_t chunk_length = channels * chunk_ms * rate / 1000;
132 uint32_t output_offset = 0;
133 uint32_t channel = 0;
134
135 /** Generate diracs every 100 frames, and check they are delayed. */
136 input.push_silence(length_frames * channels);
137 for (uint32_t i = 0; i < input.length() - 1; i+=100) {
138 input.data()[i + channel] = 0.5;
139 channel = (channel + 1) % channels;
140 }
141 dump("input.raw", input.data(), input.length());
142 while(input.length()) {
143 uint32_t to_pop = std::min<uint32_t>(input.length(), chunk_length * channels);
144 float * in = delay.input_buffer(to_pop / channels);
145 input.pop(in, to_pop);
146 delay.written(to_pop / channels);
147 output.push_silence(to_pop);
148 delay.output(output.data() + output_offset, to_pop / channels);
149 output_offset += to_pop;
150 }
151
152 // Check the diracs have been shifted by `delay_frames` frames.
153 for (uint32_t i = 0; i < output.length() - delay_frames * channels + 1; i+=100) {
154 ASSERT_EQ(output.data()[i + channel + delay_frames * channels], 0.5);
155 channel = (channel + 1) % channels;
156 }
157
158 dump("output.raw", output.data(), output.length());
159 }
160 /**
161 * This takes sine waves with a certain `channels` count, `source_rate`, and
162 * resample them, by chunk of `chunk_duration` milliseconds, to `target_rate`.
163 * Then a sample-wise comparison is performed against a sine wave generated at
164 * the correct rate.
165 */
166 template<typename T>
test_resampler_one_way(uint32_t channels,uint32_t source_rate,uint32_t target_rate,float chunk_duration)167 void test_resampler_one_way(uint32_t channels, uint32_t source_rate, uint32_t target_rate, float chunk_duration)
168 {
169 size_t chunk_duration_in_source_frames = static_cast<uint32_t>(ceil(chunk_duration * source_rate / 1000.));
170 float resampling_ratio = static_cast<float>(source_rate) / target_rate;
171 cubeb_resampler_speex_one_way<T> resampler(channels, source_rate, target_rate, 3);
172 auto_array<T> source(channels * source_rate * 10);
173 auto_array<T> destination(channels * target_rate * 10);
174 auto_array<T> expected(channels * target_rate * 10);
175 uint32_t phase_index = 0;
176 uint32_t offset = 0;
177 const uint32_t buf_len = 2; /* seconds */
178
179 // generate a sine wave in each channel, at the source sample rate
180 source.push_silence(channels * source_rate * buf_len);
181 while(offset != source.length()) {
182 float p = phase_index++ / static_cast<float>(source_rate);
183 for (uint32_t j = 0; j < channels; j++) {
184 source.data()[offset++] = 0.5 * sin(440. * 2 * PI * p);
185 }
186 }
187
188 dump("input.raw", source.data(), source.length());
189
190 expected.push_silence(channels * target_rate * buf_len);
191 // generate a sine wave in each channel, at the target sample rate.
192 // Insert silent samples at the beginning to account for the resampler latency.
193 offset = resampler.latency() * channels;
194 for (uint32_t i = 0; i < offset; i++) {
195 expected.data()[i] = 0.0f;
196 }
197 phase_index = 0;
198 while (offset != expected.length()) {
199 float p = phase_index++ / static_cast<float>(target_rate);
200 for (uint32_t j = 0; j < channels; j++) {
201 expected.data()[offset++] = 0.5 * sin(440. * 2 * PI * p);
202 }
203 }
204
205 dump("expected.raw", expected.data(), expected.length());
206
207 // resample by chunk
208 uint32_t write_offset = 0;
209 destination.push_silence(channels * target_rate * buf_len);
210 while (write_offset < destination.length())
211 {
212 size_t output_frames = static_cast<uint32_t>(floor(chunk_duration_in_source_frames / resampling_ratio));
213 uint32_t input_frames = resampler.input_needed_for_output(output_frames);
214 resampler.input(source.data(), input_frames);
215 source.pop(nullptr, input_frames * channels);
216 resampler.output(destination.data() + write_offset,
217 std::min(output_frames, (destination.length() - write_offset) / channels));
218 write_offset += output_frames * channels;
219 }
220
221 dump("output.raw", destination.data(), expected.length());
222
223 // compare, taking the latency into account
224 bool fuzzy_equal = true;
225 for (uint32_t i = resampler.latency() + 1; i < expected.length(); i++) {
226 float diff = fabs(expected.data()[i] - destination.data()[i]);
227 if (diff > epsilon<T>(resampling_ratio)) {
228 fprintf(stderr, "divergence at %d: %f %f (delta %f)\n", i, expected.data()[i], destination.data()[i], diff);
229 fuzzy_equal = false;
230 }
231 }
232 ASSERT_TRUE(fuzzy_equal);
233 }
234
235 template<typename T>
236 cubeb_sample_format cubeb_format();
237
238 template<>
cubeb_format()239 cubeb_sample_format cubeb_format<float>()
240 {
241 return CUBEB_SAMPLE_FLOAT32NE;
242 }
243
244 template<>
cubeb_format()245 cubeb_sample_format cubeb_format<short>()
246 {
247 return CUBEB_SAMPLE_S16NE;
248 }
249
250 struct osc_state {
osc_stateosc_state251 osc_state()
252 : input_phase_index(0)
253 , output_phase_index(0)
254 , output_offset(0)
255 , input_channels(0)
256 , output_channels(0)
257 {}
258 uint32_t input_phase_index;
259 uint32_t max_output_phase_index;
260 uint32_t output_phase_index;
261 uint32_t output_offset;
262 uint32_t input_channels;
263 uint32_t output_channels;
264 uint32_t output_rate;
265 uint32_t target_rate;
266 auto_array<float> input;
267 auto_array<float> output;
268 };
269
fill_with_sine(float * buf,uint32_t rate,uint32_t channels,uint32_t frames,uint32_t initial_phase)270 uint32_t fill_with_sine(float * buf, uint32_t rate, uint32_t channels,
271 uint32_t frames, uint32_t initial_phase)
272 {
273 uint32_t offset = 0;
274 for (uint32_t i = 0; i < frames; i++) {
275 float p = initial_phase++ / static_cast<float>(rate);
276 for (uint32_t j = 0; j < channels; j++) {
277 buf[offset++] = 0.5 * sin(440. * 2 * PI * p);
278 }
279 }
280 return initial_phase;
281 }
282
data_cb_resampler(cubeb_stream *,void * user_ptr,const void * input_buffer,void * output_buffer,long frame_count)283 long data_cb_resampler(cubeb_stream * /*stm*/, void * user_ptr,
284 const void * input_buffer, void * output_buffer, long frame_count)
285 {
286 osc_state * state = reinterpret_cast<osc_state*>(user_ptr);
287 const float * in = reinterpret_cast<const float*>(input_buffer);
288 float * out = reinterpret_cast<float*>(output_buffer);
289
290 state->input.push(in, frame_count * state->input_channels);
291
292 /* Check how much output frames we need to write */
293 uint32_t remaining = state->max_output_phase_index - state->output_phase_index;
294 uint32_t to_write = std::min<uint32_t>(remaining, frame_count);
295 state->output_phase_index = fill_with_sine(out,
296 state->target_rate,
297 state->output_channels,
298 to_write,
299 state->output_phase_index);
300
301 return to_write;
302 }
303
304 template<typename T>
array_fuzzy_equal(const auto_array<T> & lhs,const auto_array<T> & rhs,T epsi)305 bool array_fuzzy_equal(const auto_array<T>& lhs, const auto_array<T>& rhs, T epsi)
306 {
307 uint32_t len = std::min(lhs.length(), rhs.length());
308
309 for (uint32_t i = 0; i < len; i++) {
310 if (fabs(lhs.at(i) - rhs.at(i)) > epsi) {
311 std::cout << "not fuzzy equal at index: " << i
312 << " lhs: " << lhs.at(i) << " rhs: " << rhs.at(i)
313 << " delta: " << fabs(lhs.at(i) - rhs.at(i))
314 << " epsilon: "<< epsi << std::endl;
315 return false;
316 }
317 }
318 return true;
319 }
320
321 template<typename T>
test_resampler_duplex(uint32_t input_channels,uint32_t output_channels,uint32_t input_rate,uint32_t output_rate,uint32_t target_rate,float chunk_duration)322 void test_resampler_duplex(uint32_t input_channels, uint32_t output_channels,
323 uint32_t input_rate, uint32_t output_rate,
324 uint32_t target_rate, float chunk_duration)
325 {
326 cubeb_stream_params input_params;
327 cubeb_stream_params output_params;
328 osc_state state;
329
330 input_params.format = output_params.format = cubeb_format<T>();
331 state.input_channels = input_params.channels = input_channels;
332 state.output_channels = output_params.channels = output_channels;
333 input_params.rate = input_rate;
334 state.output_rate = output_params.rate = output_rate;
335 state.target_rate = target_rate;
336 input_params.prefs = output_params.prefs = CUBEB_STREAM_PREF_NONE;
337 long got;
338
339 cubeb_resampler * resampler =
340 cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, &output_params, target_rate,
341 data_cb_resampler, (void*)&state, CUBEB_RESAMPLER_QUALITY_VOIP);
342
343 long latency = cubeb_resampler_latency(resampler);
344
345 const uint32_t duration_s = 2;
346 int32_t duration_frames = duration_s * target_rate;
347 uint32_t input_array_frame_count = ceil(chunk_duration * input_rate / 1000) + ceilf(static_cast<float>(input_rate) / target_rate) * 2;
348 uint32_t output_array_frame_count = chunk_duration * output_rate / 1000;
349 auto_array<float> input_buffer(input_channels * input_array_frame_count);
350 auto_array<float> output_buffer(output_channels * output_array_frame_count);
351 auto_array<float> expected_resampled_input(input_channels * duration_frames);
352 auto_array<float> expected_resampled_output(output_channels * output_rate * duration_s);
353
354 state.max_output_phase_index = duration_s * target_rate;
355
356 expected_resampled_input.push_silence(input_channels * duration_frames);
357 expected_resampled_output.push_silence(output_channels * output_rate * duration_s);
358
359 /* expected output is a 440Hz sine wave at 16kHz */
360 fill_with_sine(expected_resampled_input.data() + latency,
361 target_rate, input_channels, duration_frames - latency, 0);
362 /* expected output is a 440Hz sine wave at 32kHz */
363 fill_with_sine(expected_resampled_output.data() + latency,
364 output_rate, output_channels, output_rate * duration_s - latency, 0);
365
366 while (state.output_phase_index != state.max_output_phase_index) {
367 uint32_t leftover_samples = input_buffer.length() * input_channels;
368 input_buffer.reserve(input_array_frame_count);
369 state.input_phase_index = fill_with_sine(input_buffer.data() + leftover_samples,
370 input_rate,
371 input_channels,
372 input_array_frame_count - leftover_samples,
373 state.input_phase_index);
374 long input_consumed = input_array_frame_count;
375 input_buffer.set_length(input_array_frame_count);
376
377 got = cubeb_resampler_fill(resampler,
378 input_buffer.data(), &input_consumed,
379 output_buffer.data(), output_array_frame_count);
380
381 /* handle leftover input */
382 if (input_array_frame_count != static_cast<uint32_t>(input_consumed)) {
383 input_buffer.pop(nullptr, input_consumed * input_channels);
384 } else {
385 input_buffer.clear();
386 }
387
388 state.output.push(output_buffer.data(), got * state.output_channels);
389 }
390
391 dump("input_expected.raw", expected_resampled_input.data(), expected_resampled_input.length());
392 dump("output_expected.raw", expected_resampled_output.data(), expected_resampled_output.length());
393 dump("input.raw", state.input.data(), state.input.length());
394 dump("output.raw", state.output.data(), state.output.length());
395
396 // This is disabled because the latency estimation in the resampler code is
397 // slightly off so we can generate expected vectors.
398 // See https://github.com/kinetiknz/cubeb/issues/93
399 // ASSERT_TRUE(array_fuzzy_equal(state.input, expected_resampled_input, epsilon<T>(input_rate/target_rate)));
400 // ASSERT_TRUE(array_fuzzy_equal(state.output, expected_resampled_output, epsilon<T>(output_rate/target_rate)));
401
402 cubeb_resampler_destroy(resampler);
403 }
404
405 #define array_size(x) (sizeof(x) / sizeof(x[0]))
406
TEST(cubeb,resampler_one_way)407 TEST(cubeb, resampler_one_way)
408 {
409 /* Test one way resamplers */
410 for (uint32_t channels = 1; channels <= max_channels; channels++) {
411 for (uint32_t source_rate = 0; source_rate < array_size(sample_rates); source_rate++) {
412 for (uint32_t dest_rate = 0; dest_rate < array_size(sample_rates); dest_rate++) {
413 for (uint32_t chunk_duration = min_chunks; chunk_duration < max_chunks; chunk_duration+=chunk_increment) {
414 fprintf(stderr, "one_way: channels: %d, source_rate: %d, dest_rate: %d, chunk_duration: %d\n",
415 channels, sample_rates[source_rate], sample_rates[dest_rate], chunk_duration);
416 test_resampler_one_way<float>(channels, sample_rates[source_rate],
417 sample_rates[dest_rate], chunk_duration);
418 }
419 }
420 }
421 }
422 }
423
TEST(cubeb,DISABLED_resampler_duplex)424 TEST(cubeb, DISABLED_resampler_duplex)
425 {
426 for (uint32_t input_channels = 1; input_channels <= max_channels; input_channels++) {
427 for (uint32_t output_channels = 1; output_channels <= max_channels; output_channels++) {
428 for (uint32_t source_rate_input = 0; source_rate_input < array_size(sample_rates); source_rate_input++) {
429 for (uint32_t source_rate_output = 0; source_rate_output < array_size(sample_rates); source_rate_output++) {
430 for (uint32_t dest_rate = 0; dest_rate < array_size(sample_rates); dest_rate++) {
431 for (uint32_t chunk_duration = min_chunks; chunk_duration < max_chunks; chunk_duration+=chunk_increment) {
432 fprintf(stderr, "input channels:%d output_channels:%d input_rate:%d "
433 "output_rate:%d target_rate:%d chunk_ms:%d\n",
434 input_channels, output_channels,
435 sample_rates[source_rate_input],
436 sample_rates[source_rate_output],
437 sample_rates[dest_rate],
438 chunk_duration);
439 test_resampler_duplex<float>(input_channels, output_channels,
440 sample_rates[source_rate_input],
441 sample_rates[source_rate_output],
442 sample_rates[dest_rate],
443 chunk_duration);
444 }
445 }
446 }
447 }
448 }
449 }
450 }
451
TEST(cubeb,resampler_delay_line)452 TEST(cubeb, resampler_delay_line)
453 {
454 for (uint32_t channel = 1; channel <= 2; channel++) {
455 for (uint32_t delay_frames = 4; delay_frames <= 40; delay_frames+=chunk_increment) {
456 for (uint32_t chunk_size = 10; chunk_size <= 30; chunk_size++) {
457 fprintf(stderr, "channel: %d, delay_frames: %d, chunk_size: %d\n",
458 channel, delay_frames, chunk_size);
459 test_delay_lines(delay_frames, channel, chunk_size);
460 }
461 }
462 }
463 }
464
test_output_only_noop_data_cb(cubeb_stream *,void *,const void * input_buffer,void * output_buffer,long frame_count)465 long test_output_only_noop_data_cb(cubeb_stream * /*stm*/, void * /*user_ptr*/,
466 const void * input_buffer,
467 void * output_buffer, long frame_count)
468 {
469 EXPECT_TRUE(output_buffer);
470 EXPECT_TRUE(!input_buffer);
471 return frame_count;
472 }
473
TEST(cubeb,resampler_output_only_noop)474 TEST(cubeb, resampler_output_only_noop)
475 {
476 cubeb_stream_params output_params;
477 int target_rate;
478
479 output_params.rate = 44100;
480 output_params.channels = 1;
481 output_params.format = CUBEB_SAMPLE_FLOAT32NE;
482 target_rate = output_params.rate;
483
484 cubeb_resampler * resampler =
485 cubeb_resampler_create((cubeb_stream*)nullptr, nullptr, &output_params, target_rate,
486 test_output_only_noop_data_cb, nullptr,
487 CUBEB_RESAMPLER_QUALITY_VOIP);
488
489 const long out_frames = 128;
490 float out_buffer[out_frames];
491 long got;
492
493 got = cubeb_resampler_fill(resampler, nullptr, nullptr,
494 out_buffer, out_frames);
495
496 ASSERT_EQ(got, out_frames);
497
498 cubeb_resampler_destroy(resampler);
499 }
500
test_drain_data_cb(cubeb_stream *,void * user_ptr,const void * input_buffer,void * output_buffer,long frame_count)501 long test_drain_data_cb(cubeb_stream * /*stm*/, void * user_ptr,
502 const void * input_buffer,
503 void * output_buffer, long frame_count)
504 {
505 EXPECT_TRUE(output_buffer);
506 EXPECT_TRUE(!input_buffer);
507 auto cb_count = static_cast<int *>(user_ptr);
508 (*cb_count)++;
509 return frame_count - 1;
510 }
511
TEST(cubeb,resampler_drain)512 TEST(cubeb, resampler_drain)
513 {
514 cubeb_stream_params output_params;
515 int target_rate;
516
517 output_params.rate = 44100;
518 output_params.channels = 1;
519 output_params.format = CUBEB_SAMPLE_FLOAT32NE;
520 target_rate = 48000;
521 int cb_count = 0;
522
523 cubeb_resampler * resampler =
524 cubeb_resampler_create((cubeb_stream*)nullptr, nullptr, &output_params, target_rate,
525 test_drain_data_cb, &cb_count,
526 CUBEB_RESAMPLER_QUALITY_VOIP);
527
528 const long out_frames = 128;
529 float out_buffer[out_frames];
530 long got;
531
532 do {
533 got = cubeb_resampler_fill(resampler, nullptr, nullptr,
534 out_buffer, out_frames);
535 } while (got == out_frames);
536
537 /* The callback should be called once but not again after returning <
538 * frame_count. */
539 ASSERT_EQ(cb_count, 1);
540
541 cubeb_resampler_destroy(resampler);
542 }
543
544 // gtest does not support using ASSERT_EQ and friend in a function that returns
545 // a value.
check_output(const void * input_buffer,void * output_buffer,long frame_count)546 void check_output(const void * input_buffer, void * output_buffer, long frame_count)
547 {
548 ASSERT_EQ(input_buffer, nullptr);
549 ASSERT_EQ(frame_count, 256);
550 ASSERT_TRUE(!!output_buffer);
551 }
552
cb_passthrough_resampler_output(cubeb_stream *,void *,const void * input_buffer,void * output_buffer,long frame_count)553 long cb_passthrough_resampler_output(cubeb_stream * /*stm*/, void * /*user_ptr*/,
554 const void * input_buffer,
555 void * output_buffer, long frame_count)
556 {
557 check_output(input_buffer, output_buffer, frame_count);
558 return frame_count;
559 }
560
TEST(cubeb,resampler_passthrough_output_only)561 TEST(cubeb, resampler_passthrough_output_only)
562 {
563 // Test that the passthrough resampler works when there is only an output stream.
564 cubeb_stream_params output_params;
565
566 const size_t output_channels = 2;
567 output_params.channels = output_channels;
568 output_params.rate = 44100;
569 output_params.format = CUBEB_SAMPLE_FLOAT32NE;
570 int target_rate = output_params.rate;
571
572 cubeb_resampler * resampler =
573 cubeb_resampler_create((cubeb_stream*)nullptr, nullptr, &output_params,
574 target_rate, cb_passthrough_resampler_output, nullptr,
575 CUBEB_RESAMPLER_QUALITY_VOIP);
576
577 float output_buffer[output_channels * 256];
578
579 long got;
580 for (uint32_t i = 0; i < 30; i++) {
581 got = cubeb_resampler_fill(resampler, nullptr, nullptr, output_buffer, 256);
582 ASSERT_EQ(got, 256);
583 }
584
585 cubeb_resampler_destroy(resampler);
586 }
587
588 // gtest does not support using ASSERT_EQ and friend in a function that returns
589 // a value.
check_input(const void * input_buffer,void * output_buffer,long frame_count)590 void check_input(const void * input_buffer, void * output_buffer, long frame_count)
591 {
592 ASSERT_EQ(output_buffer, nullptr);
593 ASSERT_EQ(frame_count, 256);
594 ASSERT_TRUE(!!input_buffer);
595 }
596
cb_passthrough_resampler_input(cubeb_stream *,void *,const void * input_buffer,void * output_buffer,long frame_count)597 long cb_passthrough_resampler_input(cubeb_stream * /*stm*/, void * /*user_ptr*/,
598 const void * input_buffer,
599 void * output_buffer, long frame_count)
600 {
601 check_input(input_buffer, output_buffer, frame_count);
602 return frame_count;
603 }
604
TEST(cubeb,resampler_passthrough_input_only)605 TEST(cubeb, resampler_passthrough_input_only)
606 {
607 // Test that the passthrough resampler works when there is only an output stream.
608 cubeb_stream_params input_params;
609
610 const size_t input_channels = 2;
611 input_params.channels = input_channels;
612 input_params.rate = 44100;
613 input_params.format = CUBEB_SAMPLE_FLOAT32NE;
614 int target_rate = input_params.rate;
615
616 cubeb_resampler * resampler =
617 cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, nullptr,
618 target_rate, cb_passthrough_resampler_input, nullptr,
619 CUBEB_RESAMPLER_QUALITY_VOIP);
620
621 float input_buffer[input_channels * 256];
622
623 long got;
624 for (uint32_t i = 0; i < 30; i++) {
625 long int frames = 256;
626 got = cubeb_resampler_fill(resampler, input_buffer, &frames, nullptr, 0);
627 ASSERT_EQ(got, 256);
628 }
629
630 cubeb_resampler_destroy(resampler);
631 }
632
633 template<typename T>
seq(T * array,int stride,long start,long count)634 long seq(T* array, int stride, long start, long count)
635 {
636 uint32_t output_idx = 0;
637 for(int i = 0; i < count; i++) {
638 for (int j = 0; j < stride; j++) {
639 array[output_idx + j] = static_cast<T>(start + i);
640 }
641 output_idx += stride;
642 }
643 return start + count;
644 }
645
646 template<typename T>
is_seq(T * array,int stride,long count,long expected_start)647 void is_seq(T * array, int stride, long count, long expected_start)
648 {
649 uint32_t output_index = 0;
650 for (long i = 0; i < count; i++) {
651 for (int j = 0; j < stride; j++) {
652 ASSERT_EQ(array[output_index + j], expected_start + i);
653 }
654 output_index += stride;
655 }
656 }
657
658 template<typename T>
is_not_seq(T * array,int stride,long count,long expected_start)659 void is_not_seq(T * array, int stride, long count, long expected_start)
660 {
661 uint32_t output_index = 0;
662 for (long i = 0; i < count; i++) {
663 for (int j = 0; j < stride; j++) {
664 ASSERT_NE(array[output_index + j], expected_start + i);
665 }
666 output_index += stride;
667 }
668 }
669
670 struct closure {
671 int input_channel_count;
672 };
673
674 // gtest does not support using ASSERT_EQ and friend in a function that returns
675 // a value.
676 template<typename T>
check_duplex(const T * input_buffer,T * output_buffer,long frame_count,int input_channel_count)677 void check_duplex(const T * input_buffer,
678 T * output_buffer, long frame_count,
679 int input_channel_count)
680 {
681 ASSERT_EQ(frame_count, 256);
682 // Silence scan-build warning.
683 ASSERT_TRUE(!!output_buffer); assert(output_buffer);
684 ASSERT_TRUE(!!input_buffer); assert(input_buffer);
685
686 int output_index = 0;
687 int input_index = 0;
688 for (int i = 0; i < frame_count; i++) {
689 // output is two channels, input one or two channels.
690 if (input_channel_count == 1) {
691 output_buffer[output_index] = output_buffer[output_index + 1] = input_buffer[i];
692 } else if (input_channel_count == 2) {
693 output_buffer[output_index] = input_buffer[input_index];
694 output_buffer[output_index + 1] = input_buffer[input_index + 1];
695 }
696 output_index += 2;
697 input_index += input_channel_count;
698 }
699 }
700
cb_passthrough_resampler_duplex(cubeb_stream *,void * user_ptr,const void * input_buffer,void * output_buffer,long frame_count)701 long cb_passthrough_resampler_duplex(cubeb_stream * /*stm*/, void * user_ptr,
702 const void * input_buffer,
703 void * output_buffer, long frame_count)
704 {
705 closure * c = reinterpret_cast<closure*>(user_ptr);
706 check_duplex<float>(static_cast<const float*>(input_buffer),
707 static_cast<float*>(output_buffer),
708 frame_count, c->input_channel_count);
709 return frame_count;
710 }
711
712
TEST(cubeb,resampler_passthrough_duplex_callback_reordering)713 TEST(cubeb, resampler_passthrough_duplex_callback_reordering)
714 {
715 // Test that when pre-buffering on resampler creation, we can survive an input
716 // callback being delayed.
717
718 cubeb_stream_params input_params;
719 cubeb_stream_params output_params;
720
721 const int input_channels = 1;
722 const int output_channels = 2;
723
724 input_params.channels = input_channels;
725 input_params.rate = 44100;
726 input_params.format = CUBEB_SAMPLE_FLOAT32NE;
727
728 output_params.channels = output_channels;
729 output_params.rate = input_params.rate;
730 output_params.format = CUBEB_SAMPLE_FLOAT32NE;
731
732 int target_rate = input_params.rate;
733
734 closure c;
735 c.input_channel_count = input_channels;
736
737 cubeb_resampler * resampler =
738 cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, &output_params,
739 target_rate, cb_passthrough_resampler_duplex, &c,
740 CUBEB_RESAMPLER_QUALITY_VOIP);
741
742 const long BUF_BASE_SIZE = 256;
743 float input_buffer_prebuffer[input_channels * BUF_BASE_SIZE * 2];
744 float input_buffer_glitch[input_channels * BUF_BASE_SIZE * 2];
745 float input_buffer_normal[input_channels * BUF_BASE_SIZE];
746 float output_buffer[output_channels * BUF_BASE_SIZE];
747
748 long seq_idx = 0;
749 long output_seq_idx = 0;
750
751 long prebuffer_frames = ARRAY_LENGTH(input_buffer_prebuffer) / input_params.channels;
752 seq_idx = seq(input_buffer_prebuffer, input_channels, seq_idx,
753 prebuffer_frames);
754
755 long got = cubeb_resampler_fill(resampler, input_buffer_prebuffer, &prebuffer_frames,
756 output_buffer, BUF_BASE_SIZE);
757
758 output_seq_idx += BUF_BASE_SIZE;
759
760 // prebuffer_frames will hold the frames used by the resampler.
761 ASSERT_EQ(prebuffer_frames, BUF_BASE_SIZE);
762 ASSERT_EQ(got, BUF_BASE_SIZE);
763
764 for (uint32_t i = 0; i < 300; i++) {
765 long int frames = BUF_BASE_SIZE;
766 // Simulate that sometimes, we don't have the input callback on time
767 if (i != 0 && (i % 100) == 0) {
768 long zero = 0;
769 got = cubeb_resampler_fill(resampler, input_buffer_normal /* unused here */,
770 &zero, output_buffer, BUF_BASE_SIZE);
771 is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx);
772 output_seq_idx += BUF_BASE_SIZE;
773 } else if (i != 0 && (i % 100) == 1) {
774 // if this is the case, the on the next iteration, we'll have twice the
775 // amount of input frames
776 seq_idx = seq(input_buffer_glitch, input_channels, seq_idx, BUF_BASE_SIZE * 2);
777 frames = 2 * BUF_BASE_SIZE;
778 got = cubeb_resampler_fill(resampler, input_buffer_glitch, &frames, output_buffer, BUF_BASE_SIZE);
779 is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx);
780 output_seq_idx += BUF_BASE_SIZE;
781 } else {
782 // normal case
783 seq_idx = seq(input_buffer_normal, input_channels, seq_idx, BUF_BASE_SIZE);
784 long normal_input_frame_count = 256;
785 got = cubeb_resampler_fill(resampler, input_buffer_normal, &normal_input_frame_count, output_buffer, BUF_BASE_SIZE);
786 is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx);
787 output_seq_idx += BUF_BASE_SIZE;
788 }
789 ASSERT_EQ(got, BUF_BASE_SIZE);
790 }
791
792 cubeb_resampler_destroy(resampler);
793 }
794
795 // Artificially simulate output thread underruns,
796 // by building up artificial delay in the input.
797 // Check that the frame drop logic kicks in.
TEST(cubeb,resampler_drift_drop_data)798 TEST(cubeb, resampler_drift_drop_data)
799 {
800 for (uint32_t input_channels = 1; input_channels < 3; input_channels++) {
801 cubeb_stream_params input_params;
802 cubeb_stream_params output_params;
803
804 const int output_channels = 2;
805 const int sample_rate = 44100;
806
807 input_params.channels = input_channels;
808 input_params.rate = sample_rate;
809 input_params.format = CUBEB_SAMPLE_FLOAT32NE;
810
811 output_params.channels = output_channels;
812 output_params.rate = sample_rate;
813 output_params.format = CUBEB_SAMPLE_FLOAT32NE;
814
815 int target_rate = input_params.rate;
816
817 closure c;
818 c.input_channel_count = input_channels;
819
820 cubeb_resampler * resampler =
821 cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, &output_params,
822 target_rate, cb_passthrough_resampler_duplex, &c,
823 CUBEB_RESAMPLER_QUALITY_VOIP);
824
825 const long BUF_BASE_SIZE = 256;
826
827 // The factor by which the deadline is missed. This is intentionally
828 // kind of large to trigger the frame drop quickly. In real life, multiple
829 // smaller under-runs would accumulate.
830 const long UNDERRUN_FACTOR = 10;
831 // Number buffer used for pre-buffering, that some backends do.
832 const long PREBUFFER_FACTOR = 2;
833
834 std::vector<float> input_buffer_prebuffer(input_channels * BUF_BASE_SIZE * PREBUFFER_FACTOR);
835 std::vector<float> input_buffer_glitch(input_channels * BUF_BASE_SIZE * UNDERRUN_FACTOR);
836 std::vector<float> input_buffer_normal(input_channels * BUF_BASE_SIZE);
837 std::vector<float> output_buffer(output_channels * BUF_BASE_SIZE);
838
839 long seq_idx = 0;
840 long output_seq_idx = 0;
841
842 long prebuffer_frames = input_buffer_prebuffer.size() / input_params.channels;
843 seq_idx = seq(input_buffer_prebuffer.data(), input_channels, seq_idx,
844 prebuffer_frames);
845
846 long got = cubeb_resampler_fill(resampler, input_buffer_prebuffer.data(), &prebuffer_frames,
847 output_buffer.data(), BUF_BASE_SIZE);
848
849 output_seq_idx += BUF_BASE_SIZE;
850
851 // prebuffer_frames will hold the frames used by the resampler.
852 ASSERT_EQ(prebuffer_frames, BUF_BASE_SIZE);
853 ASSERT_EQ(got, BUF_BASE_SIZE);
854
855 for (uint32_t i = 0; i < 300; i++) {
856 long int frames = BUF_BASE_SIZE;
857 if (i != 0 && (i % 100) == 1) {
858 // Once in a while, the output thread misses its deadline.
859 // The input thread still produces data, so it ends up accumulating. Simulate this by providing a
860 // much bigger input buffer. Check that the sequence is now unaligned, meaning we've dropped data
861 // to keep everything in sync.
862 seq_idx = seq(input_buffer_glitch.data(), input_channels, seq_idx, BUF_BASE_SIZE * UNDERRUN_FACTOR);
863 frames = BUF_BASE_SIZE * UNDERRUN_FACTOR;
864 got = cubeb_resampler_fill(resampler, input_buffer_glitch.data(), &frames, output_buffer.data(), BUF_BASE_SIZE);
865 is_seq(output_buffer.data(), 2, BUF_BASE_SIZE, output_seq_idx);
866 output_seq_idx += BUF_BASE_SIZE;
867 }
868 else if (i != 0 && (i % 100) == 2) {
869 // On the next iteration, the sequence should be broken
870 seq_idx = seq(input_buffer_normal.data(), input_channels, seq_idx, BUF_BASE_SIZE);
871 long normal_input_frame_count = 256;
872 got = cubeb_resampler_fill(resampler, input_buffer_normal.data(), &normal_input_frame_count, output_buffer.data(), BUF_BASE_SIZE);
873 is_not_seq(output_buffer.data(), output_channels, BUF_BASE_SIZE, output_seq_idx);
874 // Reclock so that we can use is_seq again.
875 output_seq_idx = output_buffer[BUF_BASE_SIZE * output_channels - 1] + 1;
876 }
877 else {
878 // normal case
879 seq_idx = seq(input_buffer_normal.data(), input_channels, seq_idx, BUF_BASE_SIZE);
880 long normal_input_frame_count = 256;
881 got = cubeb_resampler_fill(resampler, input_buffer_normal.data(), &normal_input_frame_count, output_buffer.data(), BUF_BASE_SIZE);
882 is_seq(output_buffer.data(), output_channels, BUF_BASE_SIZE, output_seq_idx);
883 output_seq_idx += BUF_BASE_SIZE;
884 }
885 ASSERT_EQ(got, BUF_BASE_SIZE);
886 }
887
888 cubeb_resampler_destroy(resampler);
889 }
890 }
891
892 static long
passthrough_resampler_fill_eq_input(cubeb_stream * stream,void * user_ptr,void const * input_buffer,void * output_buffer,long nframes)893 passthrough_resampler_fill_eq_input(cubeb_stream * stream,
894 void * user_ptr,
895 void const * input_buffer,
896 void * output_buffer,
897 long nframes) {
898 // gtest does not support using ASSERT_EQ and friends in a
899 // function that returns a value.
900 [nframes, input_buffer]() {
901 ASSERT_EQ(nframes, 32);
902 const float* input = static_cast<const float*>(input_buffer);
903 for (int i = 0; i < 64; ++i) {
904 ASSERT_FLOAT_EQ(input[i], 0.01 * i);
905 }
906 }();
907 return nframes;
908 }
909
TEST(cubeb,passthrough_resampler_fill_eq_input)910 TEST(cubeb, passthrough_resampler_fill_eq_input) {
911 uint32_t channels = 2;
912 uint32_t sample_rate = 44100;
913 passthrough_resampler<float> resampler =
914 passthrough_resampler<float>(nullptr, passthrough_resampler_fill_eq_input,
915 nullptr, channels, sample_rate);
916
917 long input_frame_count = 32;
918 long output_frame_count = 32;
919 float input[64] = {};
920 float output[64] = {};
921 for (uint32_t i = 0; i < input_frame_count * channels; ++i) {
922 input[i] = 0.01 * i;
923 }
924 long got = resampler.fill(input, &input_frame_count, output, output_frame_count);
925 ASSERT_EQ(got, output_frame_count);
926 // Input frames used must be equal to output frames.
927 ASSERT_EQ(input_frame_count, output_frame_count);
928 }
929
930 static long
passthrough_resampler_fill_short_input(cubeb_stream * stream,void * user_ptr,void const * input_buffer,void * output_buffer,long nframes)931 passthrough_resampler_fill_short_input(cubeb_stream * stream,
932 void * user_ptr,
933 void const * input_buffer,
934 void * output_buffer,
935 long nframes) {
936 // gtest does not support using ASSERT_EQ and friends in a
937 // function that returns a value.
938 [nframes, input_buffer]() {
939 ASSERT_EQ(nframes, 32);
940 const float* input = static_cast<const float*>(input_buffer);
941 // First part contains the input
942 for (int i = 0; i < 32; ++i) {
943 ASSERT_FLOAT_EQ(input[i], 0.01 * i);
944 }
945 // missing part contains silence
946 for (int i = 32; i < 64; ++i) {
947 ASSERT_FLOAT_EQ(input[i], 0.0);
948 }
949 }();
950 return nframes;
951 }
952
TEST(cubeb,passthrough_resampler_fill_short_input)953 TEST(cubeb, passthrough_resampler_fill_short_input) {
954 uint32_t channels = 2;
955 uint32_t sample_rate = 44100;
956 passthrough_resampler<float> resampler =
957 passthrough_resampler<float>(nullptr, passthrough_resampler_fill_short_input,
958 nullptr, channels, sample_rate);
959
960 long input_frame_count = 16;
961 long output_frame_count = 32;
962 float input[64] = {};
963 float output[64] = {};
964 for (uint32_t i = 0; i < input_frame_count * channels; ++i) {
965 input[i] = 0.01 * i;
966 }
967 long got = resampler.fill(input, &input_frame_count, output, output_frame_count);
968 ASSERT_EQ(got, output_frame_count);
969 // Input frames used are less than the output frames due to glitch.
970 ASSERT_EQ(input_frame_count, output_frame_count - 16);
971 }
972
973 static long
passthrough_resampler_fill_input_left(cubeb_stream * stream,void * user_ptr,void const * input_buffer,void * output_buffer,long nframes)974 passthrough_resampler_fill_input_left(cubeb_stream * stream,
975 void * user_ptr,
976 void const * input_buffer,
977 void * output_buffer,
978 long nframes) {
979 // gtest does not support using ASSERT_EQ and friends in a
980 // function that returns a value.
981 int iteration = *static_cast<int*>(user_ptr);
982 if (iteration == 1) {
983 [nframes, input_buffer]() {
984 ASSERT_EQ(nframes, 32);
985 const float* input = static_cast<const float*>(input_buffer);
986 for (int i = 0; i < 64; ++i) {
987 ASSERT_FLOAT_EQ(input[i], 0.01 * i);
988 }
989 }();
990 } else if (iteration == 2) {
991 [nframes, input_buffer]() {
992 ASSERT_EQ(nframes, 32);
993 const float* input = static_cast<const float*>(input_buffer);
994 for (int i = 0; i < 32; ++i) {
995 // First part contains the reamaining input samples from previous
996 // iteration (since they were more).
997 ASSERT_FLOAT_EQ(input[i], 0.01 * (i + 64));
998 // next part contains the new buffer
999 ASSERT_FLOAT_EQ(input[i + 32], 0.01 * i);
1000 }
1001 }();
1002 } else if (iteration == 3) {
1003 [nframes, input_buffer]() {
1004 ASSERT_EQ(nframes, 32);
1005 const float* input = static_cast<const float*>(input_buffer);
1006 for (int i = 0; i < 32; ++i) {
1007 // First part (16 frames) contains the reamaining input samples
1008 // from previous iteration (since they were more).
1009 ASSERT_FLOAT_EQ(input[i], 0.01 * (i + 32));
1010 }
1011 for (int i = 0; i < 16; ++i) {
1012 // next part (8 frames) contains the new input buffer.
1013 ASSERT_FLOAT_EQ(input[i + 32], 0.01 * i);
1014 // last part (8 frames) contains silence.
1015 ASSERT_FLOAT_EQ(input[i + 32 + 16], 0.0);
1016 }
1017 }();
1018 }
1019 return nframes;
1020 }
1021
TEST(cubeb,passthrough_resampler_fill_input_left)1022 TEST(cubeb, passthrough_resampler_fill_input_left) {
1023 const uint32_t channels = 2;
1024 const uint32_t sample_rate = 44100;
1025 int iteration = 0;
1026 passthrough_resampler<float> resampler =
1027 passthrough_resampler<float>(nullptr, passthrough_resampler_fill_input_left,
1028 &iteration, channels, sample_rate);
1029
1030 long input_frame_count = 48; // 32 + 16
1031 const long output_frame_count = 32;
1032 float input[96] = {};
1033 float output[64] = {};
1034 for (uint32_t i = 0; i < input_frame_count * channels; ++i) {
1035 input[i] = 0.01 * i;
1036 }
1037
1038 // 1st iteration, add the extra input.
1039 iteration = 1;
1040 long got = resampler.fill(input, &input_frame_count, output, output_frame_count);
1041 ASSERT_EQ(got, output_frame_count);
1042 // Input frames used must be equal to output frames.
1043 ASSERT_EQ(input_frame_count, output_frame_count);
1044
1045 // 2st iteration, use the extra input from previous iteration,
1046 // 16 frames are remaining in the input buffer.
1047 input_frame_count = 32; // we need 16 input frames but we get more;
1048 iteration = 2;
1049 got = resampler.fill(input, &input_frame_count, output, output_frame_count);
1050 ASSERT_EQ(got, output_frame_count);
1051 // Input frames used must be equal to output frames.
1052 ASSERT_EQ(input_frame_count, output_frame_count);
1053
1054 // 3rd iteration, use the extra input from previous iteration.
1055 // 16 frames are remaining in the input buffer.
1056 input_frame_count = 16 - 8; // We need 16 more input frames but we only get 8.
1057 iteration = 3;
1058 got = resampler.fill(input, &input_frame_count, output, output_frame_count);
1059 ASSERT_EQ(got, output_frame_count);
1060 // Input frames used are less than the output frames due to glitch.
1061 ASSERT_EQ(input_frame_count, output_frame_count - 8);
1062 }
1063
TEST(cubeb,individual_methods)1064 TEST(cubeb, individual_methods) {
1065 const uint32_t channels = 2;
1066 const uint32_t sample_rate = 44100;
1067 const uint32_t frames = 256;
1068
1069 delay_line<float> dl(10, channels, sample_rate);
1070 uint32_t frames_needed1 = dl.input_needed_for_output(0);
1071 ASSERT_EQ(frames_needed1, 0u);
1072
1073 cubeb_resampler_speex_one_way<float> one_way(channels, sample_rate, sample_rate, CUBEB_RESAMPLER_QUALITY_DEFAULT);
1074 float buffer[channels * frames] = {0.0};
1075 // Add all frames in the resampler's internal buffer.
1076 one_way.input(buffer, frames);
1077 // Ask for less than the existing frames, this would create a uint overlflow without the fix.
1078 uint32_t frames_needed2 = one_way.input_needed_for_output(0);
1079 ASSERT_EQ(frames_needed2, 0u);
1080 }
1081
1082