tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_resampler.cpp (53031B)


      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 "cubeb/cubeb.h"
     11 #include "cubeb_audio_dump.h"
     12 #include "cubeb_log.h"
     13 #include "cubeb_resampler.h"
     14 // #define ENABLE_NORMAL_LOG
     15 // #define ENABLE_VERBOSE_LOG
     16 #include "common.h"
     17 #include "cubeb_resampler_internal.h"
     18 #include "gtest/gtest.h"
     19 #include <algorithm>
     20 #include <cmath>
     21 #include <iostream>
     22 #include <queue>
     23 #include <stdio.h>
     24 #include <thread>
     25 
     26 /* Windows cmath USE_MATH_DEFINE thing... */
     27 const float PI = 3.14159265359f;
     28 
     29 /* Testing all sample rates is very long, so if THOROUGH_TESTING is not defined,
     30 * only part of the test suite is ran. */
     31 #ifdef THOROUGH_TESTING
     32 /* Some standard sample rates we're testing with. */
     33 const uint32_t sample_rates[] = {8000,  16000, 32000, 44100,
     34                                 48000, 88200, 96000, 192000};
     35 /* The maximum number of channels we're resampling. */
     36 const uint32_t max_channels = 2;
     37 /* The minimum an maximum number of milliseconds we're resampling for. This is
     38 * used to simulate the fact that the audio stream is resampled in chunks,
     39 * because audio is delivered using callbacks. */
     40 const uint32_t min_chunks = 10; /* ms */
     41 const uint32_t max_chunks = 30; /* ms */
     42 const uint32_t chunk_increment = 1;
     43 
     44 #else
     45 
     46 const uint32_t sample_rates[] = {
     47    8000,
     48    44100,
     49    48000,
     50 };
     51 const uint32_t max_channels = 2;
     52 const uint32_t min_chunks = 10; /* ms */
     53 const uint32_t max_chunks = 30; /* ms */
     54 const uint32_t chunk_increment = 10;
     55 #endif
     56 
     57 // #define DUMP_ARRAYS
     58 #ifdef DUMP_ARRAYS
     59 /**
     60 * Files produced by dump(...) can be converted to .wave files using:
     61 *
     62 * sox -c <channel_count> -r <rate> -e float -b 32  file.raw file.wav
     63 *
     64 * for floating-point audio, or:
     65 *
     66 * sox -c <channel_count> -r <rate> -e unsigned -b 16  file.raw file.wav
     67 *
     68 * for 16bit integer audio.
     69 */
     70 
     71 /* Use the correct implementation of fopen, depending on the platform. */
     72 void
     73 fopen_portable(FILE ** f, const char * name, const char * mode)
     74 {
     75 #ifdef WIN32
     76  fopen_s(f, name, mode);
     77 #else
     78  *f = fopen(name, mode);
     79 #endif
     80 }
     81 
     82 template <typename T>
     83 void
     84 dump(const char * name, T * frames, size_t count)
     85 {
     86  FILE * file;
     87  fopen_portable(&file, name, "wb");
     88 
     89  if (!file) {
     90    fprintf(stderr, "error opening %s\n", name);
     91    return;
     92  }
     93 
     94  if (count != fwrite(frames, sizeof(T), count, file)) {
     95    fprintf(stderr, "error writing to %s\n", name);
     96  }
     97  fclose(file);
     98 }
     99 #else
    100 template <typename T>
    101 void
    102 dump(const char * name, T * frames, size_t count)
    103 {
    104 }
    105 #endif
    106 
    107 // The more the ratio is far from 1, the more we accept a big error.
    108 float
    109 epsilon_tweak_ratio(float ratio)
    110 {
    111  return ratio >= 1 ? ratio : 1 / ratio;
    112 }
    113 
    114 // Epsilon values for comparing resampled data to expected data.
    115 // The bigger the resampling ratio is, the more lax we are about errors.
    116 template <typename T>
    117 T
    118 epsilon(float ratio);
    119 
    120 template <>
    121 float
    122 epsilon(float ratio)
    123 {
    124  return 0.08f * epsilon_tweak_ratio(ratio);
    125 }
    126 
    127 template <>
    128 int16_t
    129 epsilon(float ratio)
    130 {
    131  return static_cast<int16_t>(10 * epsilon_tweak_ratio(ratio));
    132 }
    133 
    134 void
    135 test_delay_lines(uint32_t delay_frames, uint32_t channels, uint32_t chunk_ms)
    136 {
    137  const size_t length_s = 2;
    138  const size_t rate = 44100;
    139  const size_t length_frames = rate * length_s;
    140  delay_line<float> delay(delay_frames, channels, rate);
    141  auto_array<float> input;
    142  auto_array<float> output;
    143  uint32_t chunk_length = channels * chunk_ms * rate / 1000;
    144  uint32_t output_offset = 0;
    145  uint32_t channel = 0;
    146 
    147  /** Generate diracs every 100 frames, and check they are delayed. */
    148  input.push_silence(length_frames * channels);
    149  for (uint32_t i = 0; i < input.length() - 1; i += 100) {
    150    input.data()[i + channel] = 0.5;
    151    channel = (channel + 1) % channels;
    152  }
    153  dump("input.raw", input.data(), input.length());
    154  while (input.length()) {
    155    uint32_t to_pop =
    156        std::min<uint32_t>(input.length(), chunk_length * channels);
    157    float * in = delay.input_buffer(to_pop / channels);
    158    input.pop(in, to_pop);
    159    delay.written(to_pop / channels);
    160    output.push_silence(to_pop);
    161    delay.output(output.data() + output_offset, to_pop / channels);
    162    output_offset += to_pop;
    163  }
    164 
    165  // Check the diracs have been shifted by `delay_frames` frames.
    166  for (uint32_t i = 0; i < output.length() - delay_frames * channels + 1;
    167       i += 100) {
    168    ASSERT_EQ(output.data()[i + channel + delay_frames * channels], 0.5);
    169    channel = (channel + 1) % channels;
    170  }
    171 
    172  dump("output.raw", output.data(), output.length());
    173 }
    174 /**
    175 * This takes sine waves with a certain `channels` count, `source_rate`, and
    176 * resample them, by chunk of `chunk_duration` milliseconds, to `target_rate`.
    177 * Then a sample-wise comparison is performed against a sine wave generated at
    178 * the correct rate.
    179 */
    180 template <typename T>
    181 void
    182 test_resampler_one_way(uint32_t channels, uint32_t source_rate,
    183                       uint32_t target_rate, float chunk_duration)
    184 {
    185  size_t chunk_duration_in_source_frames =
    186      static_cast<uint32_t>(ceil(chunk_duration * source_rate / 1000.));
    187  float resampling_ratio = static_cast<float>(source_rate) / target_rate;
    188  cubeb_resampler_speex_one_way<T> resampler(channels, source_rate, target_rate,
    189                                             3);
    190  auto_array<T> source(channels * source_rate * 10);
    191  auto_array<T> destination(channels * target_rate * 10);
    192  auto_array<T> expected(channels * target_rate * 10);
    193  uint32_t phase_index = 0;
    194  uint32_t offset = 0;
    195  const uint32_t buf_len = 2; /* seconds */
    196 
    197  // generate a sine wave in each channel, at the source sample rate
    198  source.push_silence(channels * source_rate * buf_len);
    199  while (offset != source.length()) {
    200    float p = phase_index++ / static_cast<float>(source_rate);
    201    for (uint32_t j = 0; j < channels; j++) {
    202      source.data()[offset++] = 0.5 * sin(440. * 2 * PI * p);
    203    }
    204  }
    205 
    206  dump("input.raw", source.data(), source.length());
    207 
    208  expected.push_silence(channels * target_rate * buf_len);
    209  // generate a sine wave in each channel, at the target sample rate.
    210  // Insert silent samples at the beginning to account for the resampler
    211  // latency.
    212  offset = resampler.latency() * channels;
    213  for (uint32_t i = 0; i < offset; i++) {
    214    expected.data()[i] = 0.0f;
    215  }
    216  phase_index = 0;
    217  while (offset != expected.length()) {
    218    float p = phase_index++ / static_cast<float>(target_rate);
    219    for (uint32_t j = 0; j < channels; j++) {
    220      expected.data()[offset++] = 0.5 * sin(440. * 2 * PI * p);
    221    }
    222  }
    223 
    224  dump("expected.raw", expected.data(), expected.length());
    225 
    226  // resample by chunk
    227  uint32_t write_offset = 0;
    228  destination.push_silence(channels * target_rate * buf_len);
    229  while (write_offset < destination.length()) {
    230    size_t output_frames = static_cast<uint32_t>(
    231        floor(chunk_duration_in_source_frames / resampling_ratio));
    232    uint32_t input_frames = resampler.input_needed_for_output(output_frames);
    233    resampler.input(source.data(), input_frames);
    234    source.pop(nullptr, input_frames * channels);
    235    resampler.output(
    236        destination.data() + write_offset,
    237        std::min(output_frames,
    238                 (destination.length() - write_offset) / channels));
    239    write_offset += output_frames * channels;
    240  }
    241 
    242  dump("output.raw", destination.data(), expected.length());
    243 
    244  // compare, taking the latency into account
    245  bool fuzzy_equal = true;
    246  for (uint32_t i = resampler.latency() + 1; i < expected.length(); i++) {
    247    float diff = fabs(expected.data()[i] - destination.data()[i]);
    248    if (diff > epsilon<T>(resampling_ratio)) {
    249      fprintf(stderr, "divergence at %d: %f %f (delta %f)\n", i,
    250              expected.data()[i], destination.data()[i], diff);
    251      fuzzy_equal = false;
    252    }
    253  }
    254  ASSERT_TRUE(fuzzy_equal);
    255 }
    256 
    257 template <typename T>
    258 cubeb_sample_format
    259 cubeb_format();
    260 
    261 template <>
    262 cubeb_sample_format
    263 cubeb_format<float>()
    264 {
    265  return CUBEB_SAMPLE_FLOAT32NE;
    266 }
    267 
    268 template <>
    269 cubeb_sample_format
    270 cubeb_format<short>()
    271 {
    272  return CUBEB_SAMPLE_S16NE;
    273 }
    274 
    275 struct osc_state {
    276  osc_state()
    277      : input_phase_index(0), output_phase_index(0), output_offset(0),
    278        input_channels(0), output_channels(0)
    279  {
    280  }
    281  uint32_t input_phase_index;
    282  uint32_t max_output_phase_index;
    283  uint32_t output_phase_index;
    284  uint32_t output_offset;
    285  uint32_t input_channels;
    286  uint32_t output_channels;
    287  uint32_t output_rate;
    288  uint32_t target_rate;
    289  auto_array<float> input;
    290  auto_array<float> output;
    291 };
    292 
    293 uint32_t
    294 fill_with_sine(float * buf, uint32_t rate, uint32_t channels, uint32_t frames,
    295               uint32_t initial_phase)
    296 {
    297  uint32_t offset = 0;
    298  for (uint32_t i = 0; i < frames; i++) {
    299    float p = initial_phase++ / static_cast<float>(rate);
    300    for (uint32_t j = 0; j < channels; j++) {
    301      buf[offset++] = 0.5 * sin(440. * 2 * PI * p);
    302    }
    303  }
    304  return initial_phase;
    305 }
    306 
    307 long
    308 data_cb_resampler(cubeb_stream * /*stm*/, void * user_ptr,
    309                  const void * input_buffer, void * output_buffer,
    310                  long frame_count)
    311 {
    312  osc_state * state = reinterpret_cast<osc_state *>(user_ptr);
    313  const float * in = reinterpret_cast<const float *>(input_buffer);
    314  float * out = reinterpret_cast<float *>(output_buffer);
    315 
    316  state->input.push(in, frame_count * state->input_channels);
    317 
    318  /* Check how much output frames we need to write */
    319  uint32_t remaining =
    320      state->max_output_phase_index - state->output_phase_index;
    321  uint32_t to_write = std::min<uint32_t>(remaining, frame_count);
    322  state->output_phase_index =
    323      fill_with_sine(out, state->target_rate, state->output_channels, to_write,
    324                     state->output_phase_index);
    325 
    326  return to_write;
    327 }
    328 
    329 template <typename T>
    330 bool
    331 array_fuzzy_equal(const auto_array<T> & lhs, const auto_array<T> & rhs, T epsi)
    332 {
    333  uint32_t len = std::min(lhs.length(), rhs.length());
    334 
    335  for (uint32_t i = 0; i < len; i++) {
    336    if (fabs(lhs.at(i) - rhs.at(i)) > epsi) {
    337      std::cout << "not fuzzy equal at index: " << i << " lhs: " << lhs.at(i)
    338                << " rhs: " << rhs.at(i)
    339                << " delta: " << fabs(lhs.at(i) - rhs.at(i))
    340                << " epsilon: " << epsi << std::endl;
    341      return false;
    342    }
    343  }
    344  return true;
    345 }
    346 
    347 template <typename T>
    348 void
    349 test_resampler_duplex(uint32_t input_channels, uint32_t output_channels,
    350                      uint32_t input_rate, uint32_t output_rate,
    351                      uint32_t target_rate, float chunk_duration)
    352 {
    353  cubeb_stream_params input_params;
    354  cubeb_stream_params output_params;
    355  osc_state state;
    356 
    357  input_params.format = output_params.format = cubeb_format<T>();
    358  state.input_channels = input_params.channels = input_channels;
    359  state.output_channels = output_params.channels = output_channels;
    360  input_params.rate = input_rate;
    361  state.output_rate = output_params.rate = output_rate;
    362  state.target_rate = target_rate;
    363  input_params.prefs = output_params.prefs = CUBEB_STREAM_PREF_NONE;
    364  long got;
    365 
    366  cubeb_resampler * resampler = cubeb_resampler_create(
    367      (cubeb_stream *)nullptr, &input_params, &output_params, target_rate,
    368      data_cb_resampler, (void *)&state, CUBEB_RESAMPLER_QUALITY_VOIP,
    369      CUBEB_RESAMPLER_RECLOCK_NONE);
    370 
    371  long latency = cubeb_resampler_latency(resampler);
    372 
    373  const uint32_t duration_s = 2;
    374  int32_t duration_frames = duration_s * target_rate;
    375  uint32_t input_array_frame_count =
    376      ceil(chunk_duration * input_rate / 1000) +
    377      ceilf(static_cast<float>(input_rate) / target_rate) * 2;
    378  uint32_t output_array_frame_count = chunk_duration * output_rate / 1000;
    379  auto_array<float> input_buffer(input_channels * input_array_frame_count);
    380  auto_array<float> output_buffer(output_channels * output_array_frame_count);
    381  auto_array<float> expected_resampled_input(input_channels * duration_frames);
    382  auto_array<float> expected_resampled_output(output_channels * output_rate *
    383                                              duration_s);
    384 
    385  state.max_output_phase_index = duration_s * target_rate;
    386 
    387  expected_resampled_input.push_silence(input_channels * duration_frames);
    388  expected_resampled_output.push_silence(output_channels * output_rate *
    389                                         duration_s);
    390 
    391  /* expected output is a 440Hz sine wave at 16kHz */
    392  fill_with_sine(expected_resampled_input.data() + latency, target_rate,
    393                 input_channels, duration_frames - latency, 0);
    394  /* expected output is a 440Hz sine wave at 32kHz */
    395  fill_with_sine(expected_resampled_output.data() + latency, output_rate,
    396                 output_channels, output_rate * duration_s - latency, 0);
    397 
    398  while (state.output_phase_index != state.max_output_phase_index) {
    399    uint32_t leftover_samples = input_buffer.length() * input_channels;
    400    input_buffer.reserve(input_array_frame_count);
    401    state.input_phase_index = fill_with_sine(
    402        input_buffer.data() + leftover_samples, input_rate, input_channels,
    403        input_array_frame_count - leftover_samples, state.input_phase_index);
    404    long input_consumed = input_array_frame_count;
    405    input_buffer.set_length(input_array_frame_count);
    406 
    407    got = cubeb_resampler_fill(resampler, input_buffer.data(), &input_consumed,
    408                               output_buffer.data(), output_array_frame_count);
    409 
    410    /* handle leftover input */
    411    if (input_array_frame_count != static_cast<uint32_t>(input_consumed)) {
    412      input_buffer.pop(nullptr, input_consumed * input_channels);
    413    } else {
    414      input_buffer.clear();
    415    }
    416 
    417    state.output.push(output_buffer.data(), got * state.output_channels);
    418  }
    419 
    420  dump("input_expected.raw", expected_resampled_input.data(),
    421       expected_resampled_input.length());
    422  dump("output_expected.raw", expected_resampled_output.data(),
    423       expected_resampled_output.length());
    424  dump("input.raw", state.input.data(), state.input.length());
    425  dump("output.raw", state.output.data(), state.output.length());
    426 
    427  // This is disabled because the latency estimation in the resampler code is
    428  // slightly off so we can generate expected vectors.
    429  // See https://github.com/kinetiknz/cubeb/issues/93
    430  // ASSERT_TRUE(array_fuzzy_equal(state.input, expected_resampled_input,
    431  // epsilon<T>(input_rate/target_rate)));
    432  // ASSERT_TRUE(array_fuzzy_equal(state.output, expected_resampled_output,
    433  // epsilon<T>(output_rate/target_rate)));
    434 
    435  cubeb_resampler_destroy(resampler);
    436 }
    437 
    438 #define array_size(x) (sizeof(x) / sizeof(x[0]))
    439 
    440 TEST(cubeb, resampler_one_way)
    441 {
    442  /* Test one way resamplers */
    443  for (uint32_t channels = 1; channels <= max_channels; channels++) {
    444    for (uint32_t source_rate = 0; source_rate < array_size(sample_rates);
    445         source_rate++) {
    446      for (uint32_t dest_rate = 0; dest_rate < array_size(sample_rates);
    447           dest_rate++) {
    448        for (uint32_t chunk_duration = min_chunks; chunk_duration < max_chunks;
    449             chunk_duration += chunk_increment) {
    450          fprintf(stderr,
    451                  "one_way: channels: %d, source_rate: %d, dest_rate: %d, "
    452                  "chunk_duration: %d\n",
    453                  channels, sample_rates[source_rate], sample_rates[dest_rate],
    454                  chunk_duration);
    455          test_resampler_one_way<float>(channels, sample_rates[source_rate],
    456                                        sample_rates[dest_rate],
    457                                        chunk_duration);
    458        }
    459      }
    460    }
    461  }
    462 }
    463 
    464 TEST(cubeb, DISABLED_resampler_duplex)
    465 {
    466  for (uint32_t input_channels = 1; input_channels <= max_channels;
    467       input_channels++) {
    468    for (uint32_t output_channels = 1; output_channels <= max_channels;
    469         output_channels++) {
    470      for (uint32_t source_rate_input = 0;
    471           source_rate_input < array_size(sample_rates); source_rate_input++) {
    472        for (uint32_t source_rate_output = 0;
    473             source_rate_output < array_size(sample_rates);
    474             source_rate_output++) {
    475          for (uint32_t dest_rate = 0; dest_rate < array_size(sample_rates);
    476               dest_rate++) {
    477            for (uint32_t chunk_duration = min_chunks;
    478                 chunk_duration < max_chunks;
    479                 chunk_duration += chunk_increment) {
    480              fprintf(stderr,
    481                      "input channels:%d output_channels:%d input_rate:%d "
    482                      "output_rate:%d target_rate:%d chunk_ms:%d\n",
    483                      input_channels, output_channels,
    484                      sample_rates[source_rate_input],
    485                      sample_rates[source_rate_output], sample_rates[dest_rate],
    486                      chunk_duration);
    487              test_resampler_duplex<float>(input_channels, output_channels,
    488                                           sample_rates[source_rate_input],
    489                                           sample_rates[source_rate_output],
    490                                           sample_rates[dest_rate],
    491                                           chunk_duration);
    492            }
    493          }
    494        }
    495      }
    496    }
    497  }
    498 }
    499 
    500 TEST(cubeb, resampler_delay_line)
    501 {
    502  for (uint32_t channel = 1; channel <= 2; channel++) {
    503    for (uint32_t delay_frames = 4; delay_frames <= 40;
    504         delay_frames += chunk_increment) {
    505      for (uint32_t chunk_size = 10; chunk_size <= 30; chunk_size++) {
    506        fprintf(stderr, "channel: %d, delay_frames: %d, chunk_size: %d\n",
    507                channel, delay_frames, chunk_size);
    508        test_delay_lines(delay_frames, channel, chunk_size);
    509      }
    510    }
    511  }
    512 }
    513 
    514 long
    515 test_output_only_noop_data_cb(cubeb_stream * /*stm*/, void * /*user_ptr*/,
    516                              const void * input_buffer, void * output_buffer,
    517                              long frame_count)
    518 {
    519  EXPECT_TRUE(output_buffer);
    520  EXPECT_TRUE(!input_buffer);
    521  return frame_count;
    522 }
    523 
    524 TEST(cubeb, resampler_output_only_noop)
    525 {
    526  cubeb_stream_params output_params;
    527  int target_rate;
    528 
    529  output_params.rate = 44100;
    530  output_params.channels = 1;
    531  output_params.format = CUBEB_SAMPLE_FLOAT32NE;
    532  target_rate = output_params.rate;
    533 
    534  cubeb_resampler * resampler = cubeb_resampler_create(
    535      (cubeb_stream *)nullptr, nullptr, &output_params, target_rate,
    536      test_output_only_noop_data_cb, nullptr, CUBEB_RESAMPLER_QUALITY_VOIP,
    537      CUBEB_RESAMPLER_RECLOCK_NONE);
    538  const long out_frames = 128;
    539  float out_buffer[out_frames];
    540  long got;
    541 
    542  got =
    543      cubeb_resampler_fill(resampler, nullptr, nullptr, out_buffer, out_frames);
    544 
    545  ASSERT_EQ(got, out_frames);
    546 
    547  cubeb_resampler_destroy(resampler);
    548 }
    549 
    550 long
    551 test_drain_data_cb(cubeb_stream * /*stm*/, void * user_ptr,
    552                   const void * input_buffer, void * output_buffer,
    553                   long frame_count)
    554 {
    555  EXPECT_TRUE(output_buffer);
    556  EXPECT_TRUE(!input_buffer);
    557  auto cb_count = static_cast<int *>(user_ptr);
    558  (*cb_count)++;
    559  return frame_count - 1;
    560 }
    561 
    562 TEST(cubeb, resampler_drain)
    563 {
    564  cubeb_stream_params output_params;
    565  int target_rate;
    566 
    567  output_params.rate = 44100;
    568  output_params.channels = 1;
    569  output_params.format = CUBEB_SAMPLE_FLOAT32NE;
    570  target_rate = 48000;
    571  int cb_count = 0;
    572 
    573  cubeb_resampler * resampler = cubeb_resampler_create(
    574      (cubeb_stream *)nullptr, nullptr, &output_params, target_rate,
    575      test_drain_data_cb, &cb_count, CUBEB_RESAMPLER_QUALITY_VOIP,
    576      CUBEB_RESAMPLER_RECLOCK_NONE);
    577 
    578  const long out_frames = 128;
    579  float out_buffer[out_frames];
    580  long got;
    581 
    582  do {
    583    got = cubeb_resampler_fill(resampler, nullptr, nullptr, out_buffer,
    584                               out_frames);
    585  } while (got == out_frames);
    586 
    587  /* The callback should be called once but not again after returning <
    588   * frame_count. */
    589  ASSERT_EQ(cb_count, 1);
    590 
    591  cubeb_resampler_destroy(resampler);
    592 }
    593 
    594 // gtest does not support using ASSERT_EQ and friend in a function that returns
    595 // a value.
    596 void
    597 check_output(const void * input_buffer, void * output_buffer, long frame_count)
    598 {
    599  ASSERT_EQ(input_buffer, nullptr);
    600  ASSERT_EQ(frame_count, 256);
    601  ASSERT_TRUE(!!output_buffer);
    602 }
    603 
    604 long
    605 cb_passthrough_resampler_output(cubeb_stream * /*stm*/, void * /*user_ptr*/,
    606                                const void * input_buffer, void * output_buffer,
    607                                long frame_count)
    608 {
    609  check_output(input_buffer, output_buffer, frame_count);
    610  return frame_count;
    611 }
    612 
    613 TEST(cubeb, resampler_passthrough_output_only)
    614 {
    615  // Test that the passthrough resampler works when there is only an output
    616  // stream.
    617  cubeb_stream_params output_params;
    618 
    619  const size_t output_channels = 2;
    620  output_params.channels = output_channels;
    621  output_params.rate = 44100;
    622  output_params.format = CUBEB_SAMPLE_FLOAT32NE;
    623  int target_rate = output_params.rate;
    624 
    625  cubeb_resampler * resampler = cubeb_resampler_create(
    626      (cubeb_stream *)nullptr, nullptr, &output_params, target_rate,
    627      cb_passthrough_resampler_output, nullptr, CUBEB_RESAMPLER_QUALITY_VOIP,
    628      CUBEB_RESAMPLER_RECLOCK_NONE);
    629 
    630  float output_buffer[output_channels * 256];
    631 
    632  long got;
    633  for (uint32_t i = 0; i < 30; i++) {
    634    got = cubeb_resampler_fill(resampler, nullptr, nullptr, output_buffer, 256);
    635    ASSERT_EQ(got, 256);
    636  }
    637 
    638  cubeb_resampler_destroy(resampler);
    639 }
    640 
    641 // gtest does not support using ASSERT_EQ and friend in a function that returns
    642 // a value.
    643 void
    644 check_input(const void * input_buffer, void * output_buffer, long frame_count)
    645 {
    646  ASSERT_EQ(output_buffer, nullptr);
    647  ASSERT_EQ(frame_count, 256);
    648  ASSERT_TRUE(!!input_buffer);
    649 }
    650 
    651 long
    652 cb_passthrough_resampler_input(cubeb_stream * /*stm*/, void * /*user_ptr*/,
    653                               const void * input_buffer, void * output_buffer,
    654                               long frame_count)
    655 {
    656  check_input(input_buffer, output_buffer, frame_count);
    657  return frame_count;
    658 }
    659 
    660 TEST(cubeb, resampler_passthrough_input_only)
    661 {
    662  // Test that the passthrough resampler works when there is only an output
    663  // stream.
    664  cubeb_stream_params input_params;
    665 
    666  const size_t input_channels = 2;
    667  input_params.channels = input_channels;
    668  input_params.rate = 44100;
    669  input_params.format = CUBEB_SAMPLE_FLOAT32NE;
    670  int target_rate = input_params.rate;
    671 
    672  cubeb_resampler * resampler = cubeb_resampler_create(
    673      (cubeb_stream *)nullptr, &input_params, nullptr, target_rate,
    674      cb_passthrough_resampler_input, nullptr, CUBEB_RESAMPLER_QUALITY_VOIP,
    675      CUBEB_RESAMPLER_RECLOCK_NONE);
    676 
    677  float input_buffer[input_channels * 256];
    678 
    679  long got;
    680  for (uint32_t i = 0; i < 30; i++) {
    681    long int frames = 256;
    682    got = cubeb_resampler_fill(resampler, input_buffer, &frames, nullptr, 0);
    683    ASSERT_EQ(got, 256);
    684  }
    685 
    686  cubeb_resampler_destroy(resampler);
    687 }
    688 
    689 template <typename T>
    690 long
    691 seq(T * array, int stride, long start, long count)
    692 {
    693  uint32_t output_idx = 0;
    694  for (int i = 0; i < count; i++) {
    695    for (int j = 0; j < stride; j++) {
    696      array[output_idx + j] = static_cast<T>(start + i);
    697    }
    698    output_idx += stride;
    699  }
    700  return start + count;
    701 }
    702 
    703 template <typename T>
    704 void
    705 is_seq(T * array, int stride, long count, long expected_start)
    706 {
    707  uint32_t output_index = 0;
    708  for (long i = 0; i < count; i++) {
    709    for (int j = 0; j < stride; j++) {
    710      ASSERT_EQ(array[output_index + j], expected_start + i);
    711    }
    712    output_index += stride;
    713  }
    714 }
    715 
    716 template <typename T>
    717 void
    718 is_not_seq(T * array, int stride, long count, long expected_start)
    719 {
    720  uint32_t output_index = 0;
    721  for (long i = 0; i < count; i++) {
    722    for (int j = 0; j < stride; j++) {
    723      ASSERT_NE(array[output_index + j], expected_start + i);
    724    }
    725    output_index += stride;
    726  }
    727 }
    728 
    729 struct closure {
    730  int input_channel_count;
    731 };
    732 
    733 // gtest does not support using ASSERT_EQ and friend in a function that returns
    734 // a value.
    735 template <typename T>
    736 void
    737 check_duplex(const T * input_buffer, T * output_buffer, long frame_count,
    738             int input_channel_count)
    739 {
    740  ASSERT_EQ(frame_count, 256);
    741  // Silence scan-build warning.
    742  ASSERT_TRUE(!!output_buffer);
    743  assert(output_buffer);
    744  ASSERT_TRUE(!!input_buffer);
    745  assert(input_buffer);
    746 
    747  int output_index = 0;
    748  int input_index = 0;
    749  for (int i = 0; i < frame_count; i++) {
    750    // output is two channels, input one or two channels.
    751    if (input_channel_count == 1) {
    752      output_buffer[output_index] = output_buffer[output_index + 1] =
    753          input_buffer[i];
    754    } else if (input_channel_count == 2) {
    755      output_buffer[output_index] = input_buffer[input_index];
    756      output_buffer[output_index + 1] = input_buffer[input_index + 1];
    757    }
    758    output_index += 2;
    759    input_index += input_channel_count;
    760  }
    761 }
    762 
    763 long
    764 cb_passthrough_resampler_duplex(cubeb_stream * /*stm*/, void * user_ptr,
    765                                const void * input_buffer, void * output_buffer,
    766                                long frame_count)
    767 {
    768  closure * c = reinterpret_cast<closure *>(user_ptr);
    769  check_duplex<float>(static_cast<const float *>(input_buffer),
    770                      static_cast<float *>(output_buffer), frame_count,
    771                      c->input_channel_count);
    772  return frame_count;
    773 }
    774 
    775 TEST(cubeb, resampler_passthrough_duplex_callback_reordering)
    776 {
    777  // Test that when pre-buffering on resampler creation, we can survive an input
    778  // callback being delayed.
    779 
    780  cubeb_stream_params input_params;
    781  cubeb_stream_params output_params;
    782 
    783  const int input_channels = 1;
    784  const int output_channels = 2;
    785 
    786  input_params.channels = input_channels;
    787  input_params.rate = 44100;
    788  input_params.format = CUBEB_SAMPLE_FLOAT32NE;
    789 
    790  output_params.channels = output_channels;
    791  output_params.rate = input_params.rate;
    792  output_params.format = CUBEB_SAMPLE_FLOAT32NE;
    793 
    794  int target_rate = input_params.rate;
    795 
    796  closure c;
    797  c.input_channel_count = input_channels;
    798 
    799  cubeb_resampler * resampler = cubeb_resampler_create(
    800      (cubeb_stream *)nullptr, &input_params, &output_params, target_rate,
    801      cb_passthrough_resampler_duplex, &c, CUBEB_RESAMPLER_QUALITY_VOIP,
    802      CUBEB_RESAMPLER_RECLOCK_NONE);
    803 
    804  const long BUF_BASE_SIZE = 256;
    805  float input_buffer_prebuffer[input_channels * BUF_BASE_SIZE * 2];
    806  float input_buffer_glitch[input_channels * BUF_BASE_SIZE * 2];
    807  float input_buffer_normal[input_channels * BUF_BASE_SIZE];
    808  float output_buffer[output_channels * BUF_BASE_SIZE];
    809 
    810  long seq_idx = 0;
    811  long output_seq_idx = 0;
    812 
    813  long prebuffer_frames =
    814      ARRAY_LENGTH(input_buffer_prebuffer) / input_params.channels;
    815  seq_idx =
    816      seq(input_buffer_prebuffer, input_channels, seq_idx, prebuffer_frames);
    817 
    818  long got =
    819      cubeb_resampler_fill(resampler, input_buffer_prebuffer, &prebuffer_frames,
    820                           output_buffer, BUF_BASE_SIZE);
    821 
    822  output_seq_idx += BUF_BASE_SIZE;
    823 
    824  // prebuffer_frames will hold the frames used by the resampler.
    825  ASSERT_EQ(prebuffer_frames, BUF_BASE_SIZE);
    826  ASSERT_EQ(got, BUF_BASE_SIZE);
    827 
    828  for (uint32_t i = 0; i < 300; i++) {
    829    long int frames = BUF_BASE_SIZE;
    830    // Simulate that sometimes, we don't have the input callback on time
    831    if (i != 0 && (i % 100) == 0) {
    832      long zero = 0;
    833      got =
    834          cubeb_resampler_fill(resampler, input_buffer_normal /* unused here */,
    835                               &zero, output_buffer, BUF_BASE_SIZE);
    836      is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx);
    837      output_seq_idx += BUF_BASE_SIZE;
    838    } else if (i != 0 && (i % 100) == 1) {
    839      // if this is the case, the on the next iteration, we'll have twice the
    840      // amount of input frames
    841      seq_idx =
    842          seq(input_buffer_glitch, input_channels, seq_idx, BUF_BASE_SIZE * 2);
    843      frames = 2 * BUF_BASE_SIZE;
    844      got = cubeb_resampler_fill(resampler, input_buffer_glitch, &frames,
    845                                 output_buffer, BUF_BASE_SIZE);
    846      is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx);
    847      output_seq_idx += BUF_BASE_SIZE;
    848    } else {
    849      // normal case
    850      seq_idx =
    851          seq(input_buffer_normal, input_channels, seq_idx, BUF_BASE_SIZE);
    852      long normal_input_frame_count = 256;
    853      got = cubeb_resampler_fill(resampler, input_buffer_normal,
    854                                 &normal_input_frame_count, output_buffer,
    855                                 BUF_BASE_SIZE);
    856      is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx);
    857      output_seq_idx += BUF_BASE_SIZE;
    858    }
    859    ASSERT_EQ(got, BUF_BASE_SIZE);
    860  }
    861 
    862  cubeb_resampler_destroy(resampler);
    863 }
    864 
    865 // Artificially simulate output thread underruns,
    866 // by building up artificial delay in the input.
    867 // Check that the frame drop logic kicks in.
    868 TEST(cubeb, resampler_drift_drop_data)
    869 {
    870  for (uint32_t input_channels = 1; input_channels < 3; input_channels++) {
    871    cubeb_stream_params input_params;
    872    cubeb_stream_params output_params;
    873 
    874    const int output_channels = 2;
    875    const int sample_rate = 44100;
    876 
    877    input_params.channels = input_channels;
    878    input_params.rate = sample_rate;
    879    input_params.format = CUBEB_SAMPLE_FLOAT32NE;
    880 
    881    output_params.channels = output_channels;
    882    output_params.rate = sample_rate;
    883    output_params.format = CUBEB_SAMPLE_FLOAT32NE;
    884 
    885    int target_rate = input_params.rate;
    886 
    887    closure c;
    888    c.input_channel_count = input_channels;
    889 
    890    cubeb_resampler * resampler = cubeb_resampler_create(
    891        (cubeb_stream *)nullptr, &input_params, &output_params, target_rate,
    892        cb_passthrough_resampler_duplex, &c, CUBEB_RESAMPLER_QUALITY_VOIP,
    893        CUBEB_RESAMPLER_RECLOCK_NONE);
    894 
    895    const long BUF_BASE_SIZE = 256;
    896 
    897    // The factor by which the deadline is missed. This is intentionally
    898    // kind of large to trigger the frame drop quickly. In real life, multiple
    899    // smaller under-runs would accumulate.
    900    const long UNDERRUN_FACTOR = 10;
    901    // Number buffer used for pre-buffering, that some backends do.
    902    const long PREBUFFER_FACTOR = 2;
    903 
    904    std::vector<float> input_buffer_prebuffer(input_channels * BUF_BASE_SIZE *
    905                                              PREBUFFER_FACTOR);
    906    std::vector<float> input_buffer_glitch(input_channels * BUF_BASE_SIZE *
    907                                           UNDERRUN_FACTOR);
    908    std::vector<float> input_buffer_normal(input_channels * BUF_BASE_SIZE);
    909    std::vector<float> output_buffer(output_channels * BUF_BASE_SIZE);
    910 
    911    long seq_idx = 0;
    912    long output_seq_idx = 0;
    913 
    914    long prebuffer_frames =
    915        input_buffer_prebuffer.size() / input_params.channels;
    916    seq_idx = seq(input_buffer_prebuffer.data(), input_channels, seq_idx,
    917                  prebuffer_frames);
    918 
    919    long got = cubeb_resampler_fill(resampler, input_buffer_prebuffer.data(),
    920                                    &prebuffer_frames, output_buffer.data(),
    921                                    BUF_BASE_SIZE);
    922 
    923    output_seq_idx += BUF_BASE_SIZE;
    924 
    925    // prebuffer_frames will hold the frames used by the resampler.
    926    ASSERT_EQ(prebuffer_frames, BUF_BASE_SIZE);
    927    ASSERT_EQ(got, BUF_BASE_SIZE);
    928 
    929    for (uint32_t i = 0; i < 300; i++) {
    930      long int frames = BUF_BASE_SIZE;
    931      if (i != 0 && (i % 100) == 1) {
    932        // Once in a while, the output thread misses its deadline.
    933        // The input thread still produces data, so it ends up accumulating.
    934        // Simulate this by providing a much bigger input buffer. Check that the
    935        // sequence is now unaligned, meaning we've dropped data to keep
    936        // everything in sync.
    937        seq_idx = seq(input_buffer_glitch.data(), input_channels, seq_idx,
    938                      BUF_BASE_SIZE * UNDERRUN_FACTOR);
    939        frames = BUF_BASE_SIZE * UNDERRUN_FACTOR;
    940        got =
    941            cubeb_resampler_fill(resampler, input_buffer_glitch.data(), &frames,
    942                                 output_buffer.data(), BUF_BASE_SIZE);
    943        is_seq(output_buffer.data(), 2, BUF_BASE_SIZE, output_seq_idx);
    944        output_seq_idx += BUF_BASE_SIZE;
    945      } else if (i != 0 && (i % 100) == 2) {
    946        // On the next iteration, the sequence should be broken
    947        seq_idx = seq(input_buffer_normal.data(), input_channels, seq_idx,
    948                      BUF_BASE_SIZE);
    949        long normal_input_frame_count = 256;
    950        got = cubeb_resampler_fill(resampler, input_buffer_normal.data(),
    951                                   &normal_input_frame_count,
    952                                   output_buffer.data(), BUF_BASE_SIZE);
    953        is_not_seq(output_buffer.data(), output_channels, BUF_BASE_SIZE,
    954                   output_seq_idx);
    955        // Reclock so that we can use is_seq again.
    956        output_seq_idx = output_buffer[BUF_BASE_SIZE * output_channels - 1] + 1;
    957      } else {
    958        // normal case
    959        seq_idx = seq(input_buffer_normal.data(), input_channels, seq_idx,
    960                      BUF_BASE_SIZE);
    961        long normal_input_frame_count = 256;
    962        got = cubeb_resampler_fill(resampler, input_buffer_normal.data(),
    963                                   &normal_input_frame_count,
    964                                   output_buffer.data(), BUF_BASE_SIZE);
    965        is_seq(output_buffer.data(), output_channels, BUF_BASE_SIZE,
    966               output_seq_idx);
    967        output_seq_idx += BUF_BASE_SIZE;
    968      }
    969      ASSERT_EQ(got, BUF_BASE_SIZE);
    970    }
    971 
    972    cubeb_resampler_destroy(resampler);
    973  }
    974 }
    975 
    976 static long
    977 passthrough_resampler_fill_eq_input(cubeb_stream * stream, void * user_ptr,
    978                                    void const * input_buffer,
    979                                    void * output_buffer, long nframes)
    980 {
    981  // gtest does not support using ASSERT_EQ and friends in a
    982  // function that returns a value.
    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  return nframes;
    991 }
    992 
    993 TEST(cubeb, passthrough_resampler_fill_eq_input)
    994 {
    995  uint32_t channels = 2;
    996  uint32_t sample_rate = 44100;
    997  passthrough_resampler<float> resampler =
    998      passthrough_resampler<float>(nullptr, passthrough_resampler_fill_eq_input,
    999                                   nullptr, channels, sample_rate);
   1000 
   1001  long input_frame_count = 32;
   1002  long output_frame_count = 32;
   1003  float input[64] = {};
   1004  float output[64] = {};
   1005  for (uint32_t i = 0; i < input_frame_count * channels; ++i) {
   1006    input[i] = 0.01 * i;
   1007  }
   1008  long got =
   1009      resampler.fill(input, &input_frame_count, output, output_frame_count);
   1010  ASSERT_EQ(got, output_frame_count);
   1011  // Input frames used must be equal to output frames.
   1012  ASSERT_EQ(input_frame_count, output_frame_count);
   1013 }
   1014 
   1015 static long
   1016 passthrough_resampler_fill_short_input(cubeb_stream * stream, void * user_ptr,
   1017                                       void const * input_buffer,
   1018                                       void * output_buffer, long nframes)
   1019 {
   1020  // gtest does not support using ASSERT_EQ and friends in a
   1021  // function that returns a value.
   1022  [nframes, input_buffer]() {
   1023    ASSERT_EQ(nframes, 32);
   1024    const float * input = static_cast<const float *>(input_buffer);
   1025    // First part contains the input
   1026    for (int i = 0; i < 32; ++i) {
   1027      ASSERT_FLOAT_EQ(input[i], 0.01 * i);
   1028    }
   1029    // missing part contains silence
   1030    for (int i = 32; i < 64; ++i) {
   1031      ASSERT_FLOAT_EQ(input[i], 0.0);
   1032    }
   1033  }();
   1034  return nframes;
   1035 }
   1036 
   1037 TEST(cubeb, passthrough_resampler_fill_short_input)
   1038 {
   1039  uint32_t channels = 2;
   1040  uint32_t sample_rate = 44100;
   1041  passthrough_resampler<float> resampler = passthrough_resampler<float>(
   1042      nullptr, passthrough_resampler_fill_short_input, nullptr, channels,
   1043      sample_rate);
   1044 
   1045  long input_frame_count = 16;
   1046  long output_frame_count = 32;
   1047  float input[64] = {};
   1048  float output[64] = {};
   1049  for (uint32_t i = 0; i < input_frame_count * channels; ++i) {
   1050    input[i] = 0.01 * i;
   1051  }
   1052  long got =
   1053      resampler.fill(input, &input_frame_count, output, output_frame_count);
   1054  ASSERT_EQ(got, output_frame_count);
   1055  // Input frames used are less than the output frames due to glitch.
   1056  ASSERT_EQ(input_frame_count, output_frame_count - 16);
   1057 }
   1058 
   1059 static long
   1060 passthrough_resampler_fill_input_left(cubeb_stream * stream, void * user_ptr,
   1061                                      void const * input_buffer,
   1062                                      void * output_buffer, long nframes)
   1063 {
   1064  // gtest does not support using ASSERT_EQ and friends in a
   1065  // function that returns a value.
   1066  int iteration = *static_cast<int *>(user_ptr);
   1067  if (iteration == 1) {
   1068    [nframes, input_buffer]() {
   1069      ASSERT_EQ(nframes, 32);
   1070      const float * input = static_cast<const float *>(input_buffer);
   1071      for (int i = 0; i < 64; ++i) {
   1072        ASSERT_FLOAT_EQ(input[i], 0.01 * i);
   1073      }
   1074    }();
   1075  } else if (iteration == 2) {
   1076    [nframes, input_buffer]() {
   1077      ASSERT_EQ(nframes, 32);
   1078      const float * input = static_cast<const float *>(input_buffer);
   1079      for (int i = 0; i < 32; ++i) {
   1080        // First part contains the reamaining input samples from previous
   1081        // iteration (since they were more).
   1082        ASSERT_FLOAT_EQ(input[i], 0.01 * (i + 64));
   1083        // next part contains the new buffer
   1084        ASSERT_FLOAT_EQ(input[i + 32], 0.01 * i);
   1085      }
   1086    }();
   1087  } else if (iteration == 3) {
   1088    [nframes, input_buffer]() {
   1089      ASSERT_EQ(nframes, 32);
   1090      const float * input = static_cast<const float *>(input_buffer);
   1091      for (int i = 0; i < 32; ++i) {
   1092        // First part (16 frames) contains the reamaining input samples
   1093        // from previous iteration (since they were more).
   1094        ASSERT_FLOAT_EQ(input[i], 0.01 * (i + 32));
   1095      }
   1096      for (int i = 0; i < 16; ++i) {
   1097        // next part (8 frames) contains the new input buffer.
   1098        ASSERT_FLOAT_EQ(input[i + 32], 0.01 * i);
   1099        // last part (8 frames) contains silence.
   1100        ASSERT_FLOAT_EQ(input[i + 32 + 16], 0.0);
   1101      }
   1102    }();
   1103  }
   1104  return nframes;
   1105 }
   1106 
   1107 TEST(cubeb, passthrough_resampler_fill_input_left)
   1108 {
   1109  const uint32_t channels = 2;
   1110  const uint32_t sample_rate = 44100;
   1111  int iteration = 0;
   1112  passthrough_resampler<float> resampler = passthrough_resampler<float>(
   1113      nullptr, passthrough_resampler_fill_input_left, &iteration, channels,
   1114      sample_rate);
   1115 
   1116  long input_frame_count = 48; // 32 + 16
   1117  const long output_frame_count = 32;
   1118  float input[96] = {};
   1119  float output[64] = {};
   1120  for (uint32_t i = 0; i < input_frame_count * channels; ++i) {
   1121    input[i] = 0.01 * i;
   1122  }
   1123 
   1124  // 1st iteration, add the extra input.
   1125  iteration = 1;
   1126  long got =
   1127      resampler.fill(input, &input_frame_count, output, output_frame_count);
   1128  ASSERT_EQ(got, output_frame_count);
   1129  // Input frames used must be equal to output frames.
   1130  ASSERT_EQ(input_frame_count, output_frame_count);
   1131 
   1132  // 2st iteration, use the extra input from previous iteration,
   1133  // 16 frames are remaining in the input buffer.
   1134  input_frame_count = 32; // we need 16 input frames but we get more;
   1135  iteration = 2;
   1136  got = resampler.fill(input, &input_frame_count, output, output_frame_count);
   1137  ASSERT_EQ(got, output_frame_count);
   1138  // Input frames used must be equal to output frames.
   1139  ASSERT_EQ(input_frame_count, output_frame_count);
   1140 
   1141  // 3rd iteration, use the extra input from previous iteration.
   1142  // 16 frames are remaining in the input buffer.
   1143  input_frame_count = 16 - 8; // We need 16 more input frames but we only get 8.
   1144  iteration = 3;
   1145  got = resampler.fill(input, &input_frame_count, output, output_frame_count);
   1146  ASSERT_EQ(got, output_frame_count);
   1147  // Input frames used are less than the output frames due to glitch.
   1148  ASSERT_EQ(input_frame_count, output_frame_count - 8);
   1149 }
   1150 
   1151 TEST(cubeb, individual_methods)
   1152 {
   1153  const uint32_t channels = 2;
   1154  const uint32_t sample_rate = 44100;
   1155  const uint32_t frames = 256;
   1156 
   1157  delay_line<float> dl(10, channels, sample_rate);
   1158  uint32_t frames_needed1 = dl.input_needed_for_output(0);
   1159  ASSERT_EQ(frames_needed1, 0u);
   1160 
   1161  cubeb_resampler_speex_one_way<float> one_way(
   1162      channels, sample_rate, sample_rate, CUBEB_RESAMPLER_QUALITY_DEFAULT);
   1163  float buffer[channels * frames] = {0.0};
   1164  // Add all frames in the resampler's internal buffer.
   1165  one_way.input(buffer, frames);
   1166  // Ask for less than the existing frames, this would create a uint overlflow
   1167  // without the fix.
   1168  uint32_t frames_needed2 = one_way.input_needed_for_output(0);
   1169  ASSERT_EQ(frames_needed2, 0u);
   1170 }
   1171 
   1172 struct sine_wave_state {
   1173  float frequency;
   1174  int sample_rate;
   1175  size_t count = 0;
   1176  sine_wave_state(float freq, int rate) : frequency(freq), sample_rate(rate) {}
   1177 };
   1178 
   1179 long
   1180 data_cb(cubeb_stream * stream, void * user_ptr, void const * input_buffer,
   1181        void * output_buffer, long nframes)
   1182 {
   1183  sine_wave_state * state = static_cast<sine_wave_state *>(user_ptr);
   1184  float * out = static_cast<float *>(output_buffer);
   1185  double phase_increment = 2.0f * M_PI * state->frequency / state->sample_rate;
   1186 
   1187  for (int i = 0; i < nframes; i++) {
   1188    float sample = sin(phase_increment * state->count);
   1189    state->count++;
   1190    out[i] = sample * 0.8;
   1191  }
   1192  return nframes;
   1193 }
   1194 
   1195 // This implements 4.6.2 from "Standard for Digitizing Waveform Recorders"
   1196 // (in particular Annex A), then returns the estimated amplitude, phase, and the
   1197 // sum of squared error relative to a sine wave sampled at `sample_rate` and of
   1198 // frequency `frequency`. This is also described in "Numerical methods for
   1199 // engineers" chapter 19.1, and explained at
   1200 // https://www.youtube.com/watch?v=afQszl_OwKo and videos of the same series.
   1201 // In practice here we're sending a perfect 1khz sine wave into a good
   1202 // resampler, and despite the resampling ratio being quite extreme sometimes,
   1203 // we're expecting a very good fit.
   1204 float
   1205 fit_sine(const std::vector<float> & signal, float sample_rate, float frequency,
   1206         float & out_amplitude, float & out_phase)
   1207 {
   1208  // The formulation below is exact for samples spanning an integer number of
   1209  // periods. It can be important for `signal` to be trimmed to an integer
   1210  // number of periods if it doesn't contain a lot of periods.
   1211  double phase_incr = 2.0 * M_PI * frequency / sample_rate;
   1212 
   1213  double sum_cos = 0.0;
   1214  double sum_sin = 0.0;
   1215  for (size_t i = 0; i < signal.size(); ++i) {
   1216    double c = std::cos(phase_incr * static_cast<double>(i));
   1217    double s = std::sin(phase_incr * static_cast<double>(i));
   1218    sum_cos += signal[i] * c;
   1219    sum_sin += signal[i] * s;
   1220  }
   1221 
   1222  double amplitude = 2.0f * std::sqrt(sum_cos * sum_cos + sum_sin * sum_sin) /
   1223                     static_cast<double>(signal.size());
   1224  double phi = std::atan2(sum_cos, sum_sin);
   1225 
   1226  out_amplitude = amplitude;
   1227  out_phase = phi;
   1228 
   1229  // Compute sum of squared errors relative to the fitted sine wave
   1230  double sse = 0.0;
   1231  for (size_t i = 0; i < signal.size(); ++i) {
   1232    // Use known amplitude here instead instead of the from the fitted function.
   1233    double fit = 0.8 * std::sin(phase_incr * i + phi);
   1234    double diff = signal[i] - fit;
   1235    sse += diff * diff;
   1236  }
   1237 
   1238  return sse;
   1239 }
   1240 
   1241 // Finds the offset of the start of an input_freq sine wave sampled at
   1242 // target_rate in data. Remove the leading silence from data.
   1243 size_t
   1244 find_sine_start(const std::vector<float> & data, float input_freq,
   1245                float target_rate)
   1246 {
   1247  const size_t POINTS = 10;
   1248  size_t skipped = 0;
   1249 
   1250  while (skipped + POINTS < data.size()) {
   1251    double phase = 0;
   1252    double phase_increment = 2.0f * M_PI * input_freq / target_rate;
   1253    bool fits_sine = true;
   1254 
   1255    for (size_t i = 0; i < POINTS; i++) {
   1256      float expected = sin(phase) * 0.8;
   1257      float actual = data[skipped + i];
   1258      if (fabs(expected - actual) > 0.1) {
   1259        // doesn't fit a sine, skip to next start point
   1260        fits_sine = false;
   1261        break;
   1262      }
   1263      phase += phase_increment;
   1264      if (phase > 2.0f * M_PI) {
   1265        phase -= 2.0f * M_PI;
   1266      }
   1267    }
   1268 
   1269    if (!fits_sine) {
   1270      skipped++;
   1271      continue;
   1272    }
   1273 
   1274    // Found the start of the sine wave
   1275    size_t sine_start = skipped;
   1276    return sine_start;
   1277  }
   1278 
   1279  return skipped;
   1280 }
   1281 
   1282 // This class tracks the monotonicity of a certain value, and reports if it
   1283 // increases too much monotonically.
   1284 struct monotonic_state {
   1285  explicit monotonic_state(const char * what, int source_rate, int target_rate,
   1286                           int block_size)
   1287      : what(what), source_rate(source_rate), target_rate(target_rate),
   1288        block_size(block_size)
   1289  {
   1290  }
   1291  ~monotonic_state()
   1292  {
   1293    float ratio =
   1294        static_cast<float>(source_rate) / static_cast<float>(target_rate);
   1295    // Only report if there has been a meaningful increase in buffering. Do
   1296    // not warn if the buffering was constant and small.
   1297    if (monotonic && max_value && max_value != max_step) {
   1298      printf("%s is monotonically increasing, max: %zu, max_step: %zu, "
   1299             "in: %dHz, out: "
   1300             "%dHz, block_size: %d, ratio: %lf\n",
   1301             what, max_value, max_step, source_rate, target_rate, block_size,
   1302             ratio);
   1303    }
   1304    // Arbitrary limit: if more than this number of frames has been buffered,
   1305    // print a message.
   1306    constexpr int BUFFER_SIZE_THRESHOLD = 20;
   1307    if (max_value > BUFFER_SIZE_THRESHOLD) {
   1308      printf("%s, unexpected large max buffering value, max: %zu, max_step: "
   1309             "%zu, in: %dHz, out: %dHz, block_size: %d, ratio: %lf\n",
   1310             what, max_value, max_step, source_rate, target_rate, block_size,
   1311             ratio);
   1312    }
   1313  }
   1314  void set_new_value(size_t new_value)
   1315  {
   1316    if (new_value < value) {
   1317      monotonic = false;
   1318    } else {
   1319      max_step = std::max(max_step, new_value - value);
   1320    }
   1321    value = new_value;
   1322    max_value = std::max(value, max_value);
   1323  }
   1324  // Textual representation of this measurement
   1325  const char * what;
   1326  // Resampler parameters for this test case
   1327  int source_rate = 0;
   1328  int target_rate = 0;
   1329  int block_size = 0;
   1330  // Current buffering value
   1331  size_t value = 0;
   1332  // Max buffering value increment
   1333  size_t max_step = 0;
   1334  // Max buffering value observerd
   1335  size_t max_value = 0;
   1336  // Whether the value has only increased or not
   1337  bool monotonic = true;
   1338 };
   1339 
   1340 // Setting this to 1 dumps a bunch of wave file to the local directory for
   1341 // manual inspection of the resampled output
   1342 constexpr int DUMP_OUTPUT = 0;
   1343 
   1344 // Source and target sample-rates in Hz, typical values.
   1345 const int rates[] = {16000, 32000, 44100, 48000, 96000, 192000, 384000};
   1346 // Block size in frames, except the first element, that is in millisecond
   1347 // Power of two are typical on Windows WASAPI IAudioClient3, macOS,
   1348 // Linux Pipewire and Jack. 10ms is typical on Windows IAudioClient and
   1349 // IAudioClient2. 96, 192 are not uncommon on some Android devices.
   1350 constexpr int WASAPI_MS_BLOCK = 10;
   1351 const int block_sizes[] = {WASAPI_MS_BLOCK, 96, 128, 192, 256, 512, 1024, 2048};
   1352 // Enough iterations to catch rounding/drift issues, but not too many to avoid
   1353 // having a test that is too long to run.
   1354 constexpr int ITERATION_COUNT = 1000;
   1355 // 1 kHz input sine wave
   1356 const float input_freq = 1000.0f;
   1357 
   1358 struct ThreadPool {
   1359  std::vector<std::thread> workers;
   1360  std::queue<std::function<void()>> tasks;
   1361  std::mutex queue_mutex;
   1362  std::condition_variable condition;
   1363  bool stop;
   1364 
   1365  ThreadPool(size_t threads) : stop(false)
   1366  {
   1367    for (size_t i = 0; i < threads; ++i) {
   1368      workers.emplace_back([this] {
   1369        while (true) {
   1370          std::function<void()> task;
   1371          {
   1372            std::unique_lock<std::mutex> lock(queue_mutex);
   1373            condition.wait(lock, [this] { return stop || !tasks.empty(); });
   1374            if (stop && tasks.empty())
   1375              return;
   1376            task = std::move(tasks.front());
   1377            tasks.pop();
   1378          }
   1379          task();
   1380        }
   1381      });
   1382    }
   1383  }
   1384 
   1385  void enqueue(std::function<void()> task)
   1386  {
   1387    {
   1388      std::unique_lock<std::mutex> lock(queue_mutex);
   1389      tasks.push(std::move(task));
   1390    }
   1391    condition.notify_one();
   1392  }
   1393 
   1394  ~ThreadPool()
   1395  {
   1396    {
   1397      std::unique_lock<std::mutex> lock(queue_mutex);
   1398      stop = true;
   1399    }
   1400    condition.notify_all();
   1401    for (std::thread & worker : workers) {
   1402      worker.join();
   1403    }
   1404  }
   1405 };
   1406 
   1407 static void
   1408 run_test(int source_rate, int target_rate, int block_size)
   1409 {
   1410  int effective_block_size = block_size;
   1411  // special case: Windows/WASAPI works in blocks of 10ms regardless of
   1412  // the rate.
   1413  if (effective_block_size == WASAPI_MS_BLOCK) {
   1414    effective_block_size = target_rate / 100; // 10ms
   1415  }
   1416  sine_wave_state state(input_freq, source_rate);
   1417  cubeb_stream_params out_params = {};
   1418  out_params.channels = 1;
   1419  out_params.rate = target_rate;
   1420  out_params.format = CUBEB_SAMPLE_FLOAT32NE;
   1421 
   1422  cubeb_audio_dump_session_t session = nullptr;
   1423  cubeb_audio_dump_stream_t dump_stream = nullptr;
   1424  if constexpr (DUMP_OUTPUT) {
   1425    cubeb_audio_dump_init(&session);
   1426    char buf[256];
   1427    snprintf(buf, 256, "test-%dHz-to-%dhz-%d-block.wav", source_rate,
   1428             target_rate, effective_block_size);
   1429    cubeb_audio_dump_stream_init(session, &dump_stream, out_params, buf);
   1430    cubeb_audio_dump_start(session);
   1431  }
   1432  cubeb_resampler * resampler = cubeb_resampler_create(
   1433      nullptr, nullptr, &out_params, source_rate, data_cb, &state,
   1434      CUBEB_RESAMPLER_QUALITY_DEFAULT, CUBEB_RESAMPLER_RECLOCK_NONE);
   1435  ASSERT_NE(resampler, nullptr);
   1436 
   1437  std::vector<float> data(effective_block_size * out_params.channels);
   1438  int i = ITERATION_COUNT;
   1439  // For now this only tests the output side (out_... measurements).
   1440  //  We could expect the resampler to be symmetrical, but we could
   1441  //  test both sides at once.
   1442  // - ..._in is the input buffer of the resampler, containing
   1443  // unresampled  frames
   1444  // - ..._out is the output buffer, containing resampled frames.
   1445  monotonic_state in_in_max("in_in", source_rate, target_rate,
   1446                            effective_block_size);
   1447  monotonic_state in_out_max("in_out", source_rate, target_rate,
   1448                             effective_block_size);
   1449  monotonic_state out_in_max("out_in", source_rate, target_rate,
   1450                             effective_block_size);
   1451  monotonic_state out_out_max("out_out", source_rate, target_rate,
   1452                              effective_block_size);
   1453 
   1454  std::vector<float> resampled;
   1455  resampled.reserve(ITERATION_COUNT * effective_block_size *
   1456                    out_params.channels);
   1457  while (i--) {
   1458    int64_t got = cubeb_resampler_fill(resampler, nullptr, nullptr, data.data(),
   1459                                       effective_block_size);
   1460    ASSERT_EQ(got, effective_block_size);
   1461    cubeb_resampler_stats stats = cubeb_resampler_stats_get(resampler);
   1462 
   1463    resampled.insert(resampled.end(), data.begin(), data.end());
   1464 
   1465    in_in_max.set_new_value(stats.input_input_buffer_size);
   1466    in_out_max.set_new_value(stats.input_output_buffer_size);
   1467    out_in_max.set_new_value(stats.output_input_buffer_size);
   1468    out_out_max.set_new_value(stats.output_output_buffer_size);
   1469  }
   1470 
   1471  cubeb_resampler_destroy(resampler);
   1472 
   1473  // Example of an error, off by one every block or so, resulting in a
   1474  // silent sample. This is enough to make all the tests fail.
   1475  //
   1476  // for (uint32_t i = 0; i < resampled.size(); i++) {
   1477  //   if (!(i % (effective_block_size))) {
   1478  //     resampled[i] = 0.0;
   1479  //   }
   1480  // }
   1481 
   1482  // This roughly finds the start of the sine wave and strips it from
   1483  // data.
   1484  size_t skipped = 0;
   1485  skipped = find_sine_start(resampled, input_freq, target_rate);
   1486 
   1487  resampled.erase(resampled.begin(), resampled.begin() + skipped);
   1488 
   1489  if constexpr (DUMP_OUTPUT) {
   1490    cubeb_audio_dump_write(dump_stream, resampled.data(), resampled.size());
   1491  }
   1492 
   1493  float amplitude = 0;
   1494  float phase = 0;
   1495 
   1496  // Fit our resampled sine wave, get an MSE value
   1497  double sse = fit_sine(resampled, target_rate, input_freq, amplitude, phase);
   1498  double mse = sse / resampled.size();
   1499 
   1500  // Code to print JSON to plot externally
   1501  // printf("\t[%d,%d,%d,%.10e,%lf,%lf],\n", source_rate, target_rate,
   1502  //        effective_block_size, mse, amplitude, phase);
   1503 
   1504  // Value found after running the tests on Linux x64
   1505  ASSERT_LT(mse, 3.22e-07);
   1506 
   1507  if constexpr (DUMP_OUTPUT) {
   1508    cubeb_audio_dump_stop(session);
   1509    cubeb_audio_dump_stream_shutdown(session, dump_stream);
   1510    cubeb_audio_dump_shutdown(session);
   1511  }
   1512 }
   1513 
   1514 // This tests checks three things:
   1515 // - Whenever resampling from a source rate to a target rate with a certain
   1516 //  block size, the correct number of frames is provided back from the
   1517 //  resampler, to the backend.
   1518 // - While resampling, internal buffers are kept under control and aren't
   1519 // growing unbounded.
   1520 // - The output signal is a 1khz sine (as is the input)
   1521 TEST(cubeb, resampler_typical_uses)
   1522 {
   1523  cubeb * ctx;
   1524  common_init(&ctx, "Cubeb resampler test");
   1525 
   1526  size_t concurrency = std::max(1u, std::thread::hardware_concurrency());
   1527  std::condition_variable cv;
   1528  std::mutex mutex;
   1529  size_t task_count = 0;
   1530  ThreadPool pool(concurrency);
   1531 
   1532  for (int source_rate : rates) {
   1533    for (int target_rate : rates) {
   1534      for (int block_size : block_sizes) {
   1535        {
   1536          std::unique_lock<std::mutex> lock(mutex);
   1537          ++task_count;
   1538        }
   1539        pool.enqueue([&, source_rate, target_rate, block_size] {
   1540          run_test(source_rate, target_rate, block_size);
   1541          {
   1542            std::unique_lock<std::mutex> lock(mutex);
   1543            --task_count;
   1544          }
   1545          cv.notify_one();
   1546        });
   1547      }
   1548    }
   1549  }
   1550 
   1551  std::unique_lock<std::mutex> lock(mutex);
   1552  cv.wait(lock, [&] { return task_count == 0; });
   1553  cubeb_destroy(ctx);
   1554 }
   1555 #undef NOMINMAX
   1556 #undef DUMP_ARRAYS