Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* 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 "gtest/gtest.h"
#include "DynamicResampler.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;
}
}