/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "DynamicResampler.h"
#include "gtest/gtest.h"

using namespace mozilla;

TEST(TestDynamicResampler, SameRates_Float1)
{
  const uint32_t in_frames = 100;
  const uint32_t out_frames = 100;
  uint32_t channels = 2;
  uint32_t in_rate = 44100;
  uint32_t out_rate = 44100;

  DynamicResampler dr(in_rate, out_rate);
  dr.SetSampleFormat(AUDIO_FORMAT_FLOAT32);
  EXPECT_EQ(dr.GetInRate(), in_rate);
  EXPECT_EQ(dr.GetChannels(), channels);

  // float in_ch1[] = {.1, .2, .3, .4, .5, .6, .7, .8, .9, 1.0};
  // float in_ch2[] = {.1, .2, .3, .4, .5, .6, .7, .8, .9, 1.0};
  float in_ch1[in_frames] = {};
  float in_ch2[in_frames] = {};
  AutoTArray<const float*, 2> in_buffer;
  in_buffer.AppendElements(channels);
  in_buffer[0] = in_ch1;
  in_buffer[1] = in_ch2;

  float out_ch1[out_frames] = {};
  float out_ch2[out_frames] = {};

  // Warm up with zeros
  dr.AppendInput(in_buffer, in_frames);
  bool hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
  for (uint32_t i = 0; i < out_frames; ++i) {
    EXPECT_FLOAT_EQ(in_ch1[i], out_ch1[i]);
    EXPECT_FLOAT_EQ(in_ch2[i], out_ch2[i]);
  }

  // Continue with non zero
  for (uint32_t i = 0; i < in_frames; ++i) {
    in_ch1[i] = in_ch2[i] = 0.01f * i;
  }
  dr.AppendInput(in_buffer, in_frames);
  hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
  for (uint32_t i = 0; i < out_frames; ++i) {
    EXPECT_FLOAT_EQ(in_ch1[i], out_ch1[i]);
    EXPECT_FLOAT_EQ(in_ch2[i], out_ch2[i]);
  }

  // No more frames in the input buffer
  hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_TRUE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_TRUE(hasUnderrun);
}

TEST(TestDynamicResampler, SameRates_Short1)
{
  uint32_t in_frames = 2;
  uint32_t out_frames = 2;
  uint32_t channels = 2;
  uint32_t in_rate = 44100;
  uint32_t out_rate = 44100;

  DynamicResampler dr(in_rate, out_rate);
  dr.SetSampleFormat(AUDIO_FORMAT_S16);
  EXPECT_EQ(dr.GetInRate(), in_rate);
  EXPECT_EQ(dr.GetChannels(), channels);

  short in_ch1[] = {1, 2, 3};
  short in_ch2[] = {4, 5, 6};
  AutoTArray<const short*, 2> in_buffer;
  in_buffer.AppendElements(channels);
  in_buffer[0] = in_ch1;
  in_buffer[1] = in_ch2;

  short out_ch1[3] = {};
  short out_ch2[3] = {};

  dr.AppendInput(in_buffer, in_frames);
  bool hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
  for (uint32_t i = 0; i < out_frames; ++i) {
    EXPECT_EQ(in_ch1[i], out_ch1[i]);
    EXPECT_EQ(in_ch2[i], out_ch2[i]);
  }

  // No more frames in the input buffer
  hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_TRUE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_TRUE(hasUnderrun);
}

TEST(TestDynamicResampler, SameRates_Float2)
{
  uint32_t in_frames = 3;
  uint32_t out_frames = 2;
  uint32_t channels = 2;
  uint32_t in_rate = 44100;
  uint32_t out_rate = 44100;

  DynamicResampler dr(in_rate, out_rate);
  dr.SetSampleFormat(AUDIO_FORMAT_FLOAT32);

  float in_ch1[] = {0.1, 0.2, 0.3};
  float in_ch2[] = {0.4, 0.5, 0.6};
  AutoTArray<const float*, 2> in_buffer;
  in_buffer.AppendElements(channels);
  in_buffer[0] = in_ch1;
  in_buffer[1] = in_ch2;

  float out_ch1[3] = {};
  float out_ch2[3] = {};

  dr.AppendInput(in_buffer, in_frames);
  bool hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
  for (uint32_t i = 0; i < out_frames; ++i) {
    EXPECT_FLOAT_EQ(in_ch1[i], out_ch1[i]);
    EXPECT_FLOAT_EQ(in_ch2[i], out_ch2[i]);
  }

  out_frames = 1;
  hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
  for (uint32_t i = 0; i < out_frames; ++i) {
    EXPECT_FLOAT_EQ(in_ch1[i + 2], out_ch1[i]);
    EXPECT_FLOAT_EQ(in_ch2[i + 2], out_ch2[i]);
  }

  // No more frames, the input buffer has drained
  hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_TRUE(hasUnderrun);
  dr.Resample(out_ch2, out_frames, 1);
  EXPECT_TRUE(hasUnderrun);
}

TEST(TestDynamicResampler, SameRates_Short2)
{
  uint32_t in_frames = 3;
  uint32_t out_frames = 2;
  uint32_t channels = 2;
  uint32_t in_rate = 44100;
  uint32_t out_rate = 44100;

  DynamicResampler dr(in_rate, out_rate);
  dr.SetSampleFormat(AUDIO_FORMAT_S16);

  short in_ch1[] = {1, 2, 3};
  short in_ch2[] = {4, 5, 6};
  AutoTArray<const short*, 2> in_buffer;
  in_buffer.AppendElements(channels);
  in_buffer[0] = in_ch1;
  in_buffer[1] = in_ch2;

  short out_ch1[3] = {};
  short out_ch2[3] = {};

  dr.AppendInput(in_buffer, in_frames);
  bool hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
  for (uint32_t i = 0; i < out_frames; ++i) {
    EXPECT_EQ(in_ch1[i], out_ch1[i]);
    EXPECT_EQ(in_ch2[i], out_ch2[i]);
  }

  out_frames = 1;
  hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
  for (uint32_t i = 0; i < out_frames; ++i) {
    EXPECT_EQ(in_ch1[i + 2], out_ch1[i]);
    EXPECT_EQ(in_ch2[i + 2], out_ch2[i]);
  }

  // No more frames, the input buffer has drained
  hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_TRUE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_TRUE(hasUnderrun);
}

TEST(TestDynamicResampler, SameRates_Float3)
{
  uint32_t in_frames = 2;
  uint32_t out_frames = 3;
  uint32_t channels = 2;
  uint32_t in_rate = 44100;
  uint32_t out_rate = 44100;

  DynamicResampler dr(in_rate, out_rate);
  dr.SetSampleFormat(AUDIO_FORMAT_FLOAT32);

  float in_ch1[] = {0.1, 0.2, 0.3};
  float in_ch2[] = {0.4, 0.5, 0.6};
  AutoTArray<const float*, 2> in_buffer;
  in_buffer.AppendElements(channels);
  in_buffer[0] = in_ch1;
  in_buffer[1] = in_ch2;

  float out_ch1[3] = {};
  float out_ch2[3] = {};

  // Not enough frames in the input buffer
  dr.AppendInput(in_buffer, in_frames);
  bool hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_TRUE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_TRUE(hasUnderrun);

  // Add one frame
  in_buffer[0] = in_ch1 + 2;
  in_buffer[1] = in_ch2 + 2;
  dr.AppendInput(in_buffer, 1);
  out_frames = 1;
  hasUnderrun = dr.Resample(out_ch1 + 2, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2 + 2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
  for (uint32_t i = 0; i < 3; ++i) {
    EXPECT_FLOAT_EQ(in_ch1[i], out_ch1[i]);
    EXPECT_FLOAT_EQ(in_ch2[i], out_ch2[i]);
  }
}

TEST(TestDynamicResampler, SameRates_Short3)
{
  uint32_t in_frames = 2;
  uint32_t out_frames = 3;
  uint32_t channels = 2;
  uint32_t in_rate = 44100;
  uint32_t out_rate = 44100;

  DynamicResampler dr(in_rate, out_rate);
  dr.SetSampleFormat(AUDIO_FORMAT_S16);

  short in_ch1[] = {1, 2, 3};
  short in_ch2[] = {4, 5, 6};
  AutoTArray<const short*, 2> in_buffer;
  in_buffer.AppendElements(channels);
  in_buffer[0] = in_ch1;
  in_buffer[1] = in_ch2;

  short out_ch1[3] = {};
  short out_ch2[3] = {};

  // Not enough frames in the input buffer
  dr.AppendInput(in_buffer, in_frames);
  bool hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_TRUE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_TRUE(hasUnderrun);

  // Add one frame
  in_buffer[0] = in_ch1 + 2;
  in_buffer[1] = in_ch2 + 2;
  dr.AppendInput(in_buffer, 1);
  out_frames = 1;
  hasUnderrun = dr.Resample(out_ch1 + 2, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2 + 2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
  for (uint32_t i = 0; i < 3; ++i) {
    EXPECT_EQ(in_ch1[i], out_ch1[i]);
    EXPECT_EQ(in_ch2[i], out_ch2[i]);
  }
}

TEST(TestDynamicResampler, UpdateOutRate_Float)
{
  uint32_t in_frames = 10;
  uint32_t out_frames = 40;
  uint32_t channels = 2;
  uint32_t in_rate = 24000;
  uint32_t out_rate = 48000;

  uint32_t pre_buffer = 20;

  DynamicResampler dr(in_rate, out_rate);
  dr.SetSampleFormat(AUDIO_FORMAT_FLOAT32);
  EXPECT_EQ(dr.GetInRate(), in_rate);
  EXPECT_EQ(dr.GetChannels(), channels);

  float in_ch1[10] = {};
  float in_ch2[10] = {};
  for (uint32_t i = 0; i < in_frames; ++i) {
    in_ch1[i] = in_ch2[i] = 0.01f * i;
  }
  AutoTArray<const float*, 2> in_buffer;
  in_buffer.AppendElements(channels);
  in_buffer[0] = in_ch1;
  in_buffer[1] = in_ch2;

  float out_ch1[40] = {};
  float out_ch2[40] = {};

  dr.AppendInputSilence(pre_buffer - in_frames);
  dr.AppendInput(in_buffer, in_frames);
  out_frames = 20u;
  bool hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
  for (uint32_t i = 0; i < out_frames; ++i) {
    // Half the input pre-buffer (10) is silence, and half the output (20).
    EXPECT_FLOAT_EQ(out_ch1[i], 0.0);
    EXPECT_FLOAT_EQ(out_ch2[i], 0.0);
  }

  // Update in rate
  in_rate = 26122;
  dr.UpdateResampler(in_rate, channels);
  EXPECT_EQ(dr.GetInRate(), in_rate);
  EXPECT_EQ(dr.GetChannels(), channels);
  out_frames = in_frames * out_rate / in_rate;
  EXPECT_EQ(out_frames, 18u);
  // Even if we provide no input if we have enough buffered input, we can create
  // output
  hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
}

TEST(TestDynamicResampler, UpdateOutRate_Short)
{
  uint32_t in_frames = 10;
  uint32_t out_frames = 40;
  uint32_t channels = 2;
  uint32_t in_rate = 24000;
  uint32_t out_rate = 48000;

  uint32_t pre_buffer = 20;

  DynamicResampler dr(in_rate, out_rate);
  dr.SetSampleFormat(AUDIO_FORMAT_S16);
  EXPECT_EQ(dr.GetInRate(), in_rate);
  EXPECT_EQ(dr.GetChannels(), channels);

  short in_ch1[10] = {};
  short in_ch2[10] = {};
  for (uint32_t i = 0; i < in_frames; ++i) {
    in_ch1[i] = in_ch2[i] = i;
  }
  AutoTArray<const short*, 2> in_buffer;
  in_buffer.AppendElements(channels);
  in_buffer[0] = in_ch1;
  in_buffer[1] = in_ch2;

  short out_ch1[40] = {};
  short out_ch2[40] = {};

  dr.AppendInputSilence(pre_buffer - in_frames);
  dr.AppendInput(in_buffer, in_frames);
  out_frames = 20u;
  bool hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
  for (uint32_t i = 0; i < out_frames; ++i) {
    // Half the input pre-buffer (10) is silence, and half the output (20).
    EXPECT_EQ(out_ch1[i], 0.0);
    EXPECT_EQ(out_ch2[i], 0.0);
  }

  // Update in rate
  in_rate = 26122;
  dr.UpdateResampler(in_rate, channels);
  EXPECT_EQ(dr.GetInRate(), in_rate);
  EXPECT_EQ(dr.GetChannels(), channels);
  out_frames = in_frames * out_rate / in_rate;
  EXPECT_EQ(out_frames, 18u);
  // Even if we provide no input if we have enough buffered input, we can create
  // output
  hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
}

TEST(TestDynamicResampler, BigRangeInRates_Float)
{
  uint32_t in_frames = 10;
  uint32_t out_frames = 10;
  uint32_t channels = 2;
  uint32_t in_rate = 44100;
  uint32_t out_rate = 44100;

  DynamicResampler dr(in_rate, out_rate);
  dr.SetSampleFormat(AUDIO_FORMAT_FLOAT32);

  const uint32_t in_capacity = 40;
  float in_ch1[in_capacity] = {};
  float in_ch2[in_capacity] = {};
  for (uint32_t i = 0; i < in_capacity; ++i) {
    in_ch1[i] = in_ch2[i] = 0.01f * i;
  }
  AutoTArray<const float*, 2> in_buffer;
  in_buffer.AppendElements(channels);
  in_buffer[0] = in_ch1;
  in_buffer[1] = in_ch2;

  const uint32_t out_capacity = 1000;
  float out_ch1[out_capacity] = {};
  float out_ch2[out_capacity] = {};

  // Downsampling at a high enough ratio happens to have enough excess
  // in_frames from rounding in the out_frames calculation to cover the
  // skipped input latency when switching from zero-latency 44100->44100 to a
  // non-1:1 ratio.
  for (uint32_t rate = 100000; rate >= 10000; rate -= 2) {
    in_rate = rate;
    dr.UpdateResampler(in_rate, channels);
    EXPECT_EQ(dr.GetInRate(), in_rate);
    EXPECT_EQ(dr.GetChannels(), channels);
    in_frames = 20;  // more than we need
    out_frames = in_frames * out_rate / in_rate;
    for (uint32_t y = 0; y < 2; ++y) {
      dr.AppendInput(in_buffer, in_frames);
      bool hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
      EXPECT_FALSE(hasUnderrun);
      hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
      EXPECT_FALSE(hasUnderrun);
    }
  }
}

TEST(TestDynamicResampler, DownsamplingToCopying)
{
  uint32_t channel_count = 1;
  // Start with downsampling
  uint32_t in_rate = 48001;
  uint32_t out_rate = 48000;

  DynamicResampler dr(in_rate, out_rate);
  dr.SetSampleFormat(AUDIO_FORMAT_FLOAT32);
  dr.UpdateResampler(in_rate, channel_count);

  const uint32_t in_frame_count = 100;
  float in_frames[in_frame_count];
  float increment = 1.f / in_frame_count;
  for (uint32_t i = 0; i < in_frame_count; ++i) {
    in_frames[i] = static_cast<float>(i) * increment;
  }
  const float* in_channels[] = {in_frames};
  dr.AppendInput(in_channels, in_frame_count);

  const uint32_t block_size = 30;
  const uint32_t out_frame_count = 2 * block_size;
  float out_frames[out_frame_count];
  bool hasUnderrun = dr.Resample(out_frames, block_size, 0);
  EXPECT_FALSE(hasUnderrun);
  // Use out_rate for the input rate so that the DynamicResampler switches
  // from resampling to copying of frames.
  dr.UpdateResampler(out_rate, channel_count);
  hasUnderrun = dr.Resample(out_frames + block_size, block_size, 0);
  EXPECT_FALSE(hasUnderrun);

  // The abrupt change in slope from zero to increment is resampled to some
  // oscillations around the point of change.
  constexpr float tolerance = 0.1f;
  bool latencyHasPassed = false;
  EXPECT_NEAR(out_frames[0], 0.f, tolerance);
  for (uint32_t i = 1; i < out_frame_count; ++i) {
    if (!latencyHasPassed) {
      // Before the latency passes, samples may be approximately zero.
      EXPECT_GT(out_frames[i], -tolerance) << "for i=" << i;
      if (out_frames[i] > tolerance) {
        latencyHasPassed = true;
        EXPECT_LT(out_frames[i], increment + tolerance) << "for i=" << i;
      }
      continue;
    }
    EXPECT_NEAR(out_frames[i], out_frames[i - 1] + increment, tolerance);
  }
}

TEST(TestDynamicResampler, BigRangeInRates_Short)
{
  uint32_t in_frames = 10;
  uint32_t out_frames = 10;
  uint32_t channels = 2;
  uint32_t in_rate = 44100;
  uint32_t out_rate = 44100;

  DynamicResampler dr(in_rate, out_rate);
  dr.SetSampleFormat(AUDIO_FORMAT_S16);

  const uint32_t in_capacity = 40;
  short in_ch1[in_capacity] = {};
  short in_ch2[in_capacity] = {};
  for (uint32_t i = 0; i < in_capacity; ++i) {
    in_ch1[i] = in_ch2[i] = i;
  }
  AutoTArray<const short*, 2> in_buffer;
  in_buffer.AppendElements(channels);
  in_buffer[0] = in_ch1;
  in_buffer[1] = in_ch2;

  const uint32_t out_capacity = 1000;
  short out_ch1[out_capacity] = {};
  short out_ch2[out_capacity] = {};

  for (uint32_t rate = 100000; rate >= 10000; rate -= 2) {
    in_rate = rate;
    dr.UpdateResampler(in_rate, channels);
    in_frames = 20;  // more than we need
    out_frames = in_frames * out_rate / in_rate;
    for (uint32_t y = 0; y < 2; ++y) {
      dr.AppendInput(in_buffer, in_frames);
      bool hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
      EXPECT_FALSE(hasUnderrun);
      hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
      EXPECT_FALSE(hasUnderrun);
    }
  }
}

TEST(TestDynamicResampler, UpdateChannels_Float)
{
  uint32_t in_frames = 10;
  uint32_t out_frames = 10;
  uint32_t channels = 2;
  uint32_t in_rate = 44100;
  uint32_t out_rate = 48000;

  DynamicResampler dr(in_rate, out_rate);
  dr.SetSampleFormat(AUDIO_FORMAT_FLOAT32);

  float in_ch1[10] = {};
  float in_ch2[10] = {};
  for (uint32_t i = 0; i < in_frames; ++i) {
    in_ch1[i] = in_ch2[i] = 0.01f * i;
  }
  AutoTArray<const float*, 2> in_buffer;
  in_buffer.AppendElements(channels);
  in_buffer[0] = in_ch1;
  in_buffer[1] = in_ch2;

  float out_ch1[10] = {};
  float out_ch2[10] = {};

  dr.AppendInput(in_buffer, in_frames);
  bool hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);

  // Add 3rd channel
  dr.UpdateResampler(in_rate, 3);
  EXPECT_EQ(dr.GetInRate(), in_rate);
  EXPECT_EQ(dr.GetChannels(), 3u);

  float in_ch3[10] = {};
  for (uint32_t i = 0; i < in_frames; ++i) {
    in_ch3[i] = 0.01f * i;
  }
  in_buffer.AppendElement();
  in_buffer[2] = in_ch3;
  float out_ch3[10] = {};

  dr.AppendInput(in_buffer, in_frames);

  hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch3, out_frames, 2);
  EXPECT_FALSE(hasUnderrun);

  float in_ch4[10] = {};
  for (uint32_t i = 0; i < in_frames; ++i) {
    in_ch3[i] = 0.01f * i;
  }
  in_buffer.AppendElement();
  in_buffer[3] = in_ch4;
  float out_ch4[10] = {};

  dr.UpdateResampler(in_rate, 4);
  EXPECT_EQ(dr.GetInRate(), in_rate);
  EXPECT_EQ(dr.GetChannels(), 4u);
  dr.AppendInput(in_buffer, in_frames);

  hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch3, out_frames, 2);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch4, out_frames, 3);
  EXPECT_FALSE(hasUnderrun);
}

TEST(TestDynamicResampler, UpdateChannels_Short)
{
  uint32_t in_frames = 10;
  uint32_t out_frames = 10;
  uint32_t channels = 2;
  uint32_t in_rate = 44100;
  uint32_t out_rate = 48000;

  DynamicResampler dr(in_rate, out_rate);
  dr.SetSampleFormat(AUDIO_FORMAT_S16);

  short in_ch1[10] = {};
  short in_ch2[10] = {};
  for (uint32_t i = 0; i < in_frames; ++i) {
    in_ch1[i] = in_ch2[i] = i;
  }
  AutoTArray<const short*, 2> in_buffer;
  in_buffer.AppendElements(channels);
  in_buffer[0] = in_ch1;
  in_buffer[1] = in_ch2;

  short out_ch1[10] = {};
  short out_ch2[10] = {};

  dr.AppendInput(in_buffer, in_frames);
  bool hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);

  // Add 3rd channel
  dr.UpdateResampler(in_rate, 3);
  EXPECT_EQ(dr.GetInRate(), in_rate);
  EXPECT_EQ(dr.GetChannels(), 3u);

  short in_ch3[10] = {};
  for (uint32_t i = 0; i < in_frames; ++i) {
    in_ch3[i] = i;
  }
  in_buffer.AppendElement();
  in_buffer[2] = in_ch3;
  short out_ch3[10] = {};

  dr.AppendInput(in_buffer, in_frames);

  hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch3, out_frames, 2);
  EXPECT_FALSE(hasUnderrun);

  // Check update with AudioSegment
  short in_ch4[10] = {};
  for (uint32_t i = 0; i < in_frames; ++i) {
    in_ch3[i] = i;
  }
  in_buffer.AppendElement();
  in_buffer[3] = in_ch4;
  short out_ch4[10] = {};

  dr.UpdateResampler(in_rate, 4);
  EXPECT_EQ(dr.GetInRate(), in_rate);
  EXPECT_EQ(dr.GetChannels(), 4u);
  dr.AppendInput(in_buffer, in_frames);

  hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch3, out_frames, 2);
  EXPECT_FALSE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch4, out_frames, 3);
  EXPECT_FALSE(hasUnderrun);
}

TEST(TestDynamicResampler, Underrun)
{
  const uint32_t in_frames = 100;
  const uint32_t out_frames = 200;
  uint32_t channels = 2;
  uint32_t in_rate = 48000;
  uint32_t out_rate = 48000;

  DynamicResampler dr(in_rate, out_rate);
  dr.SetSampleFormat(AUDIO_FORMAT_FLOAT32);
  EXPECT_EQ(dr.GetInRate(), in_rate);
  EXPECT_EQ(dr.GetChannels(), channels);

  float in_ch1[in_frames] = {};
  float in_ch2[in_frames] = {};
  AutoTArray<const float*, 2> in_buffer;
  in_buffer.AppendElements(channels);
  in_buffer[0] = in_ch1;
  in_buffer[1] = in_ch2;

  float out_ch1[out_frames] = {};
  float out_ch2[out_frames] = {};

  for (uint32_t i = 0; i < in_frames; ++i) {
    in_ch1[i] = 0.01f * i;
    in_ch2[i] = -0.01f * i;
  }
  dr.AppendInput(in_buffer, in_frames);
  bool hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_TRUE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_TRUE(hasUnderrun);
  for (uint32_t i = 0; i < in_frames; ++i) {
    EXPECT_EQ(out_ch1[i], in_ch1[i]);
    EXPECT_EQ(out_ch2[i], in_ch2[i]);
  }
  for (uint32_t i = in_frames; i < out_frames; ++i) {
    EXPECT_EQ(out_ch1[i], 0.0f) << "for i=" << i;
    EXPECT_EQ(out_ch2[i], 0.0f) << "for i=" << i;
  }

  // No more frames in the input buffer
  hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_TRUE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_TRUE(hasUnderrun);
  for (uint32_t i = 0; i < out_frames; ++i) {
    EXPECT_EQ(out_ch1[i], 0.0f) << "for i=" << i;
    EXPECT_EQ(out_ch2[i], 0.0f) << "for i=" << i;
  }

  // Now try with resampling.
  dr.UpdateResampler(in_rate * 2, channels);
  dr.AppendInput(in_buffer, in_frames);
  hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_TRUE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_TRUE(hasUnderrun);
  // There is some buffering in the resampler, which is why the below is not
  // exact.
  for (uint32_t i = 0; i < 50; ++i) {
    EXPECT_GT(out_ch1[i], 0.0f) << "for i=" << i;
    EXPECT_LT(out_ch2[i], 0.0f) << "for i=" << i;
  }
  for (uint32_t i = 50; i < 54; ++i) {
    EXPECT_NE(out_ch1[i], 0.0f) << "for i=" << i;
    EXPECT_NE(out_ch2[i], 0.0f) << "for i=" << i;
  }
  for (uint32_t i = 54; i < out_frames; ++i) {
    EXPECT_EQ(out_ch1[i], 0.0f) << "for i=" << i;
    EXPECT_EQ(out_ch2[i], 0.0f) << "for i=" << i;
  }

  // No more frames in the input buffer
  hasUnderrun = dr.Resample(out_ch1, out_frames, 0);
  EXPECT_TRUE(hasUnderrun);
  hasUnderrun = dr.Resample(out_ch2, out_frames, 1);
  EXPECT_TRUE(hasUnderrun);
  for (uint32_t i = 0; i < out_frames; ++i) {
    EXPECT_EQ(out_ch1[i], 0.0f) << "for i=" << i;
    EXPECT_EQ(out_ch2[i], 0.0f) << "for i=" << i;
  }
}
