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