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 "AudioResampler.h"
#include "nsContentUtils.h"
using namespace mozilla;
template <class T>
AudioChunk CreateAudioChunk(uint32_t aFrames, uint32_t aChannels,
AudioSampleFormat aSampleFormat) {
AudioChunk chunk;
nsTArray<nsTArray<T>> buffer;
buffer.AppendElements(aChannels);
nsTArray<const T*> bufferPtrs;
bufferPtrs.AppendElements(aChannels);
for (uint32_t i = 0; i < aChannels; ++i) {
T* ptr = buffer[i].AppendElements(aFrames);
bufferPtrs[i] = ptr;
for (uint32_t j = 0; j < aFrames; ++j) {
if (aSampleFormat == AUDIO_FORMAT_FLOAT32) {
ptr[j] = 0.01 * j;
} else {
ptr[j] = j;
}
}
}
chunk.mBuffer = new mozilla::SharedChannelArrayBuffer(std::move(buffer));
chunk.mBufferFormat = aSampleFormat;
chunk.mChannelData.AppendElements(aChannels);
for (uint32_t i = 0; i < aChannels; ++i) {
chunk.mChannelData[i] = bufferPtrs[i];
}
chunk.mDuration = aFrames;
return chunk;
}
template <class T>
AudioSegment CreateAudioSegment(uint32_t aFrames, uint32_t aChannels,
AudioSampleFormat aSampleFormat) {
AudioSegment segment;
AudioChunk chunk = CreateAudioChunk<T>(aFrames, aChannels, aSampleFormat);
segment.AppendAndConsumeChunk(std::move(chunk));
return segment;
}
TEST(TestAudioResampler, OutAudioSegment_Float)
{
const PrincipalHandle testPrincipal =
MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
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 = 21;
AudioResampler dr(in_rate, out_rate, pre_buffer, testPrincipal);
AudioSegment inSegment =
CreateAudioSegment<float>(in_frames, channels, AUDIO_FORMAT_FLOAT32);
dr.AppendInput(inSegment);
out_frames = 20u;
bool hasUnderrun = false;
AudioSegment s = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
EXPECT_EQ(s.GetDuration(), 20);
EXPECT_EQ(s.GetType(), MediaSegment::AUDIO);
for (AudioSegment::ChunkIterator ci(s); !ci.IsEnded(); ci.Next()) {
AudioChunk& c = *ci;
EXPECT_EQ(c.mPrincipalHandle, testPrincipal);
EXPECT_EQ(c.ChannelCount(), 2u);
for (uint32_t i = 0; i < out_frames; ++i) {
// The first input segment is part of the pre buffer, so 21-10=11 of the
// input is silence. They make up 22 silent output frames after
// resampling.
EXPECT_FLOAT_EQ(c.ChannelData<float>()[0][i], 0.0);
EXPECT_FLOAT_EQ(c.ChannelData<float>()[1][i], 0.0);
}
}
// Update in rate
in_rate = 26122;
dr.UpdateInRate(in_rate);
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 = false;
AudioSegment s1 = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
EXPECT_EQ(s1.GetDuration(), out_frames);
EXPECT_EQ(s1.GetType(), MediaSegment::AUDIO);
for (AudioSegment::ConstChunkIterator ci(s1); !ci.IsEnded(); ci.Next()) {
EXPECT_EQ(ci->mPrincipalHandle, testPrincipal);
}
}
TEST(TestAudioResampler, OutAudioSegment_Short)
{
const PrincipalHandle testPrincipal =
MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
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 = 21;
AudioResampler dr(in_rate, out_rate, pre_buffer, testPrincipal);
AudioSegment inSegment =
CreateAudioSegment<short>(in_frames, channels, AUDIO_FORMAT_S16);
dr.AppendInput(inSegment);
out_frames = 20u;
bool hasUnderrun = false;
AudioSegment s = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
EXPECT_EQ(s.GetDuration(), 20);
EXPECT_EQ(s.GetType(), MediaSegment::AUDIO);
for (AudioSegment::ChunkIterator ci(s); !ci.IsEnded(); ci.Next()) {
AudioChunk& c = *ci;
EXPECT_EQ(c.mPrincipalHandle, testPrincipal);
EXPECT_EQ(c.ChannelCount(), 2u);
for (uint32_t i = 0; i < out_frames; ++i) {
// The first input segment is part of the pre buffer, so 21-10=11 of the
// input is silence. They make up 22 silent output frames after
// resampling.
EXPECT_FLOAT_EQ(c.ChannelData<short>()[0][i], 0.0);
EXPECT_FLOAT_EQ(c.ChannelData<short>()[1][i], 0.0);
}
}
// Update in rate
in_rate = 26122;
dr.UpdateInRate(out_rate);
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 = false;
AudioSegment s1 = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
EXPECT_EQ(s1.GetDuration(), out_frames);
EXPECT_EQ(s1.GetType(), MediaSegment::AUDIO);
for (AudioSegment::ConstChunkIterator ci(s1); !ci.IsEnded(); ci.Next()) {
EXPECT_EQ(ci->mPrincipalHandle, testPrincipal);
}
}
TEST(TestAudioResampler, OutAudioSegmentLargerThanResampledInput_Float)
{
const uint32_t in_frames = 130;
const uint32_t out_frames = 300;
uint32_t channels = 2;
uint32_t in_rate = 24000;
uint32_t out_rate = 48000;
uint32_t pre_buffer = 5;
AudioResampler dr(in_rate, out_rate, pre_buffer, PRINCIPAL_HANDLE_NONE);
AudioSegment inSegment =
CreateAudioSegment<float>(in_frames, channels, AUDIO_FORMAT_FLOAT32);
// Set the pre-buffer.
dr.AppendInput(inSegment);
bool hasUnderrun = false;
AudioSegment s = dr.Resample(300, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
EXPECT_EQ(s.GetDuration(), 300);
EXPECT_EQ(s.GetType(), MediaSegment::AUDIO);
dr.AppendInput(inSegment);
AudioSegment s2 = dr.Resample(out_frames, &hasUnderrun);
EXPECT_TRUE(hasUnderrun);
EXPECT_EQ(s2.GetDuration(), 300);
EXPECT_EQ(s2.GetType(), MediaSegment::AUDIO);
}
TEST(TestAudioResampler, InAudioSegment_Float)
{
const PrincipalHandle testPrincipal =
MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
uint32_t in_frames = 10;
uint32_t out_frames = 20;
uint32_t channels = 2;
uint32_t in_rate = 24000;
uint32_t out_rate = 48000;
uint32_t pre_buffer = 10;
AudioResampler dr(in_rate, out_rate, pre_buffer, testPrincipal);
AudioSegment inSegment;
AudioChunk chunk1;
chunk1.SetNull(in_frames / 2);
inSegment.AppendAndConsumeChunk(std::move(chunk1));
AudioChunk chunk2;
nsTArray<nsTArray<float>> buffer;
buffer.AppendElements(channels);
nsTArray<const float*> bufferPtrs;
bufferPtrs.AppendElements(channels);
for (uint32_t i = 0; i < channels; ++i) {
float* ptr = buffer[i].AppendElements(5);
bufferPtrs[i] = ptr;
for (uint32_t j = 0; j < 5; ++j) {
ptr[j] = 0.01f * j;
}
}
chunk2.mBuffer = new mozilla::SharedChannelArrayBuffer(std::move(buffer));
chunk2.mBufferFormat = AUDIO_FORMAT_FLOAT32;
chunk2.mChannelData.AppendElements(channels);
for (uint32_t i = 0; i < channels; ++i) {
chunk2.mChannelData[i] = bufferPtrs[i];
}
chunk2.mDuration = in_frames / 2;
inSegment.AppendAndConsumeChunk(std::move(chunk2));
dr.AppendInput(inSegment);
bool hasUnderrun = false;
AudioSegment outSegment = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
// inSegment contains 10 frames, 5 null, 5 non-null. They're part of the pre
// buffer which is 10, meaning there are no extra pre buffered silence frames.
EXPECT_EQ(outSegment.GetDuration(), out_frames);
EXPECT_EQ(outSegment.MaxChannelCount(), 2u);
// Add another 5 null and 5 non-null frames.
dr.AppendInput(inSegment);
AudioSegment outSegment2 = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
EXPECT_EQ(outSegment2.GetDuration(), out_frames);
EXPECT_EQ(outSegment2.MaxChannelCount(), 2u);
for (AudioSegment::ConstChunkIterator ci(outSegment2); !ci.IsEnded();
ci.Next()) {
EXPECT_EQ(ci->mPrincipalHandle, testPrincipal);
}
}
TEST(TestAudioResampler, InAudioSegment_Short)
{
const PrincipalHandle testPrincipal =
MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
uint32_t in_frames = 10;
uint32_t out_frames = 20;
uint32_t channels = 2;
uint32_t in_rate = 24000;
uint32_t out_rate = 48000;
uint32_t pre_buffer = 10;
AudioResampler dr(in_rate, out_rate, pre_buffer, testPrincipal);
AudioSegment inSegment;
// The null chunk at the beginning will be ignored.
AudioChunk chunk1;
chunk1.SetNull(in_frames / 2);
inSegment.AppendAndConsumeChunk(std::move(chunk1));
AudioChunk chunk2;
nsTArray<nsTArray<short>> buffer;
buffer.AppendElements(channels);
nsTArray<const short*> bufferPtrs;
bufferPtrs.AppendElements(channels);
for (uint32_t i = 0; i < channels; ++i) {
short* ptr = buffer[i].AppendElements(5);
bufferPtrs[i] = ptr;
for (uint32_t j = 0; j < 5; ++j) {
ptr[j] = j;
}
}
chunk2.mBuffer = new mozilla::SharedChannelArrayBuffer(std::move(buffer));
chunk2.mBufferFormat = AUDIO_FORMAT_S16;
chunk2.mChannelData.AppendElements(channels);
for (uint32_t i = 0; i < channels; ++i) {
chunk2.mChannelData[i] = bufferPtrs[i];
}
chunk2.mDuration = in_frames / 2;
inSegment.AppendAndConsumeChunk(std::move(chunk2));
dr.AppendInput(inSegment);
bool hasUnderrun = false;
AudioSegment outSegment = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
// inSegment contains 10 frames, 5 null, 5 non-null. They're part of the pre
// buffer which is 10, meaning there are no extra pre buffered silence frames.
EXPECT_EQ(outSegment.GetDuration(), out_frames);
EXPECT_EQ(outSegment.MaxChannelCount(), 2u);
// Add another 5 null and 5 non-null frames.
dr.AppendInput(inSegment);
AudioSegment outSegment2 = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
EXPECT_EQ(outSegment2.GetDuration(), out_frames);
EXPECT_EQ(outSegment2.MaxChannelCount(), 2u);
for (AudioSegment::ConstChunkIterator ci(outSegment2); !ci.IsEnded();
ci.Next()) {
EXPECT_EQ(ci->mPrincipalHandle, testPrincipal);
}
}
TEST(TestAudioResampler, ChannelChange_MonoToStereo)
{
const PrincipalHandle testPrincipal =
MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
uint32_t in_frames = 10;
uint32_t out_frames = 40;
uint32_t in_rate = 24000;
uint32_t out_rate = 48000;
uint32_t pre_buffer = 0;
AudioResampler dr(in_rate, out_rate, pre_buffer, testPrincipal);
AudioChunk monoChunk =
CreateAudioChunk<float>(in_frames, 1, AUDIO_FORMAT_FLOAT32);
AudioChunk stereoChunk =
CreateAudioChunk<float>(in_frames, 2, AUDIO_FORMAT_FLOAT32);
AudioSegment inSegment;
inSegment.AppendAndConsumeChunk(std::move(monoChunk));
inSegment.AppendAndConsumeChunk(std::move(stereoChunk));
dr.AppendInput(inSegment);
bool hasUnderrun = false;
AudioSegment s = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
EXPECT_EQ(s.GetDuration(), 40);
EXPECT_EQ(s.GetType(), MediaSegment::AUDIO);
EXPECT_EQ(s.MaxChannelCount(), 2u);
for (AudioSegment::ConstChunkIterator ci(s); !ci.IsEnded(); ci.Next()) {
EXPECT_EQ(ci->mPrincipalHandle, testPrincipal);
}
}
TEST(TestAudioResampler, ChannelChange_StereoToMono)
{
const PrincipalHandle testPrincipal =
MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
uint32_t in_frames = 10;
uint32_t out_frames = 40;
uint32_t in_rate = 24000;
uint32_t out_rate = 48000;
uint32_t pre_buffer = 0;
AudioResampler dr(in_rate, out_rate, pre_buffer, testPrincipal);
AudioChunk monoChunk =
CreateAudioChunk<float>(in_frames, 1, AUDIO_FORMAT_FLOAT32);
AudioChunk stereoChunk =
CreateAudioChunk<float>(in_frames, 2, AUDIO_FORMAT_FLOAT32);
AudioSegment inSegment;
inSegment.AppendAndConsumeChunk(std::move(stereoChunk));
inSegment.AppendAndConsumeChunk(std::move(monoChunk));
dr.AppendInput(inSegment);
bool hasUnderrun = false;
AudioSegment s = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
EXPECT_EQ(s.GetDuration(), 40);
EXPECT_EQ(s.GetType(), MediaSegment::AUDIO);
EXPECT_EQ(s.MaxChannelCount(), 1u);
for (AudioSegment::ConstChunkIterator ci(s); !ci.IsEnded(); ci.Next()) {
EXPECT_EQ(ci->mPrincipalHandle, testPrincipal);
}
}
TEST(TestAudioResampler, ChannelChange_StereoToQuad)
{
const PrincipalHandle testPrincipal =
MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
uint32_t in_frames = 10;
uint32_t out_frames = 40;
uint32_t in_rate = 24000;
uint32_t out_rate = 48000;
uint32_t pre_buffer = 0;
AudioResampler dr(in_rate, out_rate, pre_buffer, testPrincipal);
AudioChunk stereoChunk =
CreateAudioChunk<float>(in_frames, 2, AUDIO_FORMAT_FLOAT32);
AudioChunk quadChunk =
CreateAudioChunk<float>(in_frames, 4, AUDIO_FORMAT_FLOAT32);
AudioSegment inSegment;
inSegment.AppendAndConsumeChunk(std::move(stereoChunk));
inSegment.AppendAndConsumeChunk(std::move(quadChunk));
dr.AppendInput(inSegment);
bool hasUnderrun = false;
AudioSegment s = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
EXPECT_EQ(s.GetDuration(), 40u);
EXPECT_EQ(s.GetType(), MediaSegment::AUDIO);
AudioSegment s2 = dr.Resample(out_frames / 2, &hasUnderrun);
EXPECT_TRUE(hasUnderrun);
EXPECT_EQ(s2.GetDuration(), 20u);
EXPECT_EQ(s2.GetType(), MediaSegment::AUDIO);
for (AudioSegment::ConstChunkIterator ci(s); !ci.IsEnded(); ci.Next()) {
EXPECT_EQ(ci->mPrincipalHandle, testPrincipal);
}
}
TEST(TestAudioResampler, ChannelChange_QuadToStereo)
{
const PrincipalHandle testPrincipal =
MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
uint32_t in_frames = 10;
uint32_t out_frames = 40;
uint32_t in_rate = 24000;
uint32_t out_rate = 48000;
AudioResampler dr(in_rate, out_rate, 0, testPrincipal);
AudioChunk stereoChunk =
CreateAudioChunk<float>(in_frames, 2, AUDIO_FORMAT_FLOAT32);
AudioChunk quadChunk =
CreateAudioChunk<float>(in_frames, 4, AUDIO_FORMAT_FLOAT32);
AudioSegment inSegment;
inSegment.AppendAndConsumeChunk(std::move(quadChunk));
inSegment.AppendAndConsumeChunk(std::move(stereoChunk));
dr.AppendInput(inSegment);
bool hasUnderrun = false;
AudioSegment s = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
EXPECT_EQ(s.GetDuration(), 40u);
EXPECT_EQ(s.GetType(), MediaSegment::AUDIO);
AudioSegment s2 = dr.Resample(out_frames / 2, &hasUnderrun);
EXPECT_TRUE(hasUnderrun);
EXPECT_EQ(s2.GetDuration(), 20u);
EXPECT_EQ(s2.GetType(), MediaSegment::AUDIO);
for (AudioSegment::ConstChunkIterator ci(s); !ci.IsEnded(); ci.Next()) {
EXPECT_EQ(ci->mPrincipalHandle, testPrincipal);
}
}
void printAudioSegment(const AudioSegment& segment);
TEST(TestAudioResampler, ChannelChange_Discontinuity)
{
const PrincipalHandle testPrincipal =
MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
uint32_t in_rate = 24000;
uint32_t out_rate = 48000;
const float amplitude = 0.5;
const float frequency = 200;
const float phase = 0.0;
float time = 0.0;
const float deltaTime = 1.0f / static_cast<float>(in_rate);
uint32_t in_frames = in_rate / 100;
uint32_t out_frames = out_rate / 100;
AudioResampler dr(in_rate, out_rate, 0, testPrincipal);
AudioChunk monoChunk =
CreateAudioChunk<float>(in_frames, 1, AUDIO_FORMAT_FLOAT32);
for (uint32_t i = 0; i < monoChunk.GetDuration(); ++i) {
double value = amplitude * sin(2 * M_PI * frequency * time + phase);
monoChunk.ChannelDataForWrite<float>(0)[i] = static_cast<float>(value);
time += deltaTime;
}
AudioChunk stereoChunk =
CreateAudioChunk<float>(in_frames, 2, AUDIO_FORMAT_FLOAT32);
for (uint32_t i = 0; i < stereoChunk.GetDuration(); ++i) {
double value = amplitude * sin(2 * M_PI * frequency * time + phase);
stereoChunk.ChannelDataForWrite<float>(0)[i] = static_cast<float>(value);
if (stereoChunk.ChannelCount() == 2) {
stereoChunk.ChannelDataForWrite<float>(1)[i] = value;
}
time += deltaTime;
}
AudioSegment inSegment;
inSegment.AppendAndConsumeChunk(std::move(stereoChunk));
// printAudioSegment(inSegment);
dr.AppendInput(inSegment);
bool hasUnderrun = false;
AudioSegment s = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
// printAudioSegment(s);
AudioSegment inSegment2;
inSegment2.AppendAndConsumeChunk(std::move(monoChunk));
// The resampler here is updated due to the channel change and that creates
// discontinuity.
dr.AppendInput(inSegment2);
AudioSegment s2 = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
// printAudioSegment(s2);
EXPECT_EQ(s2.GetDuration(), 480);
EXPECT_EQ(s2.GetType(), MediaSegment::AUDIO);
EXPECT_EQ(s2.MaxChannelCount(), 1u);
for (AudioSegment::ConstChunkIterator ci(s2); !ci.IsEnded(); ci.Next()) {
EXPECT_EQ(ci->mPrincipalHandle, testPrincipal);
}
}
TEST(TestAudioResampler, ChannelChange_Discontinuity2)
{
const PrincipalHandle testPrincipal =
MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
uint32_t in_rate = 24000;
uint32_t out_rate = 48000;
const float amplitude = 0.5;
const float frequency = 200;
const float phase = 0.0;
float time = 0.0;
const float deltaTime = 1.0f / static_cast<float>(in_rate);
uint32_t in_frames = in_rate / 100;
uint32_t out_frames = out_rate / 100;
AudioResampler dr(in_rate, out_rate, 10, testPrincipal);
AudioChunk monoChunk =
CreateAudioChunk<float>(in_frames / 2, 1, AUDIO_FORMAT_FLOAT32);
for (uint32_t i = 0; i < monoChunk.GetDuration(); ++i) {
double value = amplitude * sin(2 * M_PI * frequency * time + phase);
monoChunk.ChannelDataForWrite<float>(0)[i] = static_cast<float>(value);
time += deltaTime;
}
AudioChunk stereoChunk =
CreateAudioChunk<float>(in_frames / 2, 2, AUDIO_FORMAT_FLOAT32);
for (uint32_t i = 0; i < stereoChunk.GetDuration(); ++i) {
double value = amplitude * sin(2 * M_PI * frequency * time + phase);
stereoChunk.ChannelDataForWrite<float>(0)[i] = static_cast<float>(value);
if (stereoChunk.ChannelCount() == 2) {
stereoChunk.ChannelDataForWrite<float>(1)[i] = value;
}
time += deltaTime;
}
AudioSegment inSegment;
inSegment.AppendAndConsumeChunk(std::move(monoChunk));
inSegment.AppendAndConsumeChunk(std::move(stereoChunk));
// printAudioSegment(inSegment);
dr.AppendInput(inSegment);
bool hasUnderrun = false;
AudioSegment s1 = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
// printAudioSegment(s1);
EXPECT_EQ(s1.GetDuration(), 480);
EXPECT_EQ(s1.GetType(), MediaSegment::AUDIO);
EXPECT_EQ(s1.MaxChannelCount(), 2u);
for (AudioSegment::ConstChunkIterator ci(s1); !ci.IsEnded(); ci.Next()) {
EXPECT_EQ(ci->mPrincipalHandle, testPrincipal);
}
// The resampler here is updated due to the channel change and that creates
// discontinuity.
dr.AppendInput(inSegment);
AudioSegment s2 = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
// printAudioSegment(s2);
EXPECT_EQ(s2.GetDuration(), 480);
EXPECT_EQ(s2.GetType(), MediaSegment::AUDIO);
EXPECT_EQ(s2.MaxChannelCount(), 2u);
for (AudioSegment::ConstChunkIterator ci(s2); !ci.IsEnded(); ci.Next()) {
EXPECT_EQ(ci->mPrincipalHandle, testPrincipal);
}
}
TEST(TestAudioResampler, ChannelChange_Discontinuity3)
{
const PrincipalHandle testPrincipal =
MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
uint32_t in_rate = 48000;
uint32_t out_rate = 48000;
const float amplitude = 0.5;
const float frequency = 200;
const float phase = 0.0;
float time = 0.0;
const float deltaTime = 1.0f / static_cast<float>(in_rate);
uint32_t in_frames = in_rate / 100;
uint32_t out_frames = out_rate / 100;
AudioResampler dr(in_rate, out_rate, 10, testPrincipal);
AudioChunk stereoChunk =
CreateAudioChunk<float>(in_frames, 2, AUDIO_FORMAT_FLOAT32);
for (uint32_t i = 0; i < stereoChunk.GetDuration(); ++i) {
double value = amplitude * sin(2 * M_PI * frequency * time + phase);
stereoChunk.ChannelDataForWrite<float>(0)[i] = static_cast<float>(value);
if (stereoChunk.ChannelCount() == 2) {
stereoChunk.ChannelDataForWrite<float>(1)[i] = value;
}
time += deltaTime;
}
AudioSegment inSegment;
inSegment.AppendAndConsumeChunk(std::move(stereoChunk));
// printAudioSegment(inSegment);
dr.AppendInput(inSegment);
bool hasUnderrun = false;
AudioSegment s = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
// printAudioSegment(s);
EXPECT_EQ(s.GetDuration(), 480);
EXPECT_EQ(s.GetType(), MediaSegment::AUDIO);
EXPECT_EQ(s.MaxChannelCount(), 2u);
// The resampler here is updated due to the rate change. This is because the
// in and out rate was the same so a pass through logic was used. By updating
// the in rate to something different than the out rate, the resampler will
// start being used and discontinuity will exist.
dr.UpdateInRate(in_rate - 400);
dr.AppendInput(inSegment);
AudioSegment s2 = dr.Resample(out_frames, &hasUnderrun);
EXPECT_FALSE(hasUnderrun);
// printAudioSegment(s2);
EXPECT_EQ(s2.GetDuration(), 480);
EXPECT_EQ(s2.GetType(), MediaSegment::AUDIO);
EXPECT_EQ(s2.MaxChannelCount(), 2u);
for (AudioSegment::ConstChunkIterator ci(s2); !ci.IsEnded(); ci.Next()) {
EXPECT_EQ(ci->mPrincipalHandle, testPrincipal);
}
}