blob: d5119c00c2b116022f095c868bd059b7f5d67a5b [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// The format of these tests are to enqueue a known amount of data and then
// request the exact amount we expect in order to dequeue the known amount of
// data. This ensures that for any rate we are consuming input data at the
// correct rate. We always pass in a very large destination buffer with the
// expectation that FillBuffer() will fill as much as it can but no more.
#include <cmath>
#include "base/bind.h"
#include "base/callback.h"
#include "media/base/audio_buffer.h"
#include "media/base/audio_bus.h"
#include "media/base/buffers.h"
#include "media/base/channel_layout.h"
#include "media/base/test_helpers.h"
#include "media/filters/audio_renderer_algorithm.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
static const int kFrameSize = 250;
static const int kSamplesPerSecond = 3000;
static const SampleFormat kSampleFormat = kSampleFormatS16;
class AudioRendererAlgorithmTest : public testing::Test {
public:
AudioRendererAlgorithmTest()
: frames_enqueued_(0),
channels_(0),
sample_format_(kUnknownSampleFormat),
bytes_per_sample_(0) {
}
virtual ~AudioRendererAlgorithmTest() {}
void Initialize() {
Initialize(CHANNEL_LAYOUT_STEREO, kSampleFormatS16, 3000);
}
void Initialize(ChannelLayout channel_layout,
SampleFormat sample_format,
int samples_per_second) {
channels_ = ChannelLayoutToChannelCount(channel_layout);
sample_format_ = sample_format;
bytes_per_sample_ = SampleFormatToBytesPerChannel(sample_format);
AudioParameters params(media::AudioParameters::AUDIO_PCM_LINEAR,
channel_layout,
samples_per_second,
bytes_per_sample_ * 8,
samples_per_second / 100);
algorithm_.Initialize(1, params);
FillAlgorithmQueue();
}
void FillAlgorithmQueue() {
// The value of the data is meaningless; we just want non-zero data to
// differentiate it from muted data.
scoped_refptr<AudioBuffer> buffer;
while (!algorithm_.IsQueueFull()) {
switch (sample_format_) {
case kSampleFormatU8:
buffer = MakeInterleavedAudioBuffer<uint8>(sample_format_,
channels_,
1,
1,
kFrameSize,
kNoTimestamp(),
kNoTimestamp());
break;
case kSampleFormatS16:
buffer = MakeInterleavedAudioBuffer<int16>(sample_format_,
channels_,
1,
1,
kFrameSize,
kNoTimestamp(),
kNoTimestamp());
break;
case kSampleFormatS32:
buffer = MakeInterleavedAudioBuffer<int32>(sample_format_,
channels_,
1,
1,
kFrameSize,
kNoTimestamp(),
kNoTimestamp());
break;
default:
NOTREACHED() << "Unrecognized format " << sample_format_;
}
algorithm_.EnqueueBuffer(buffer);
frames_enqueued_ += kFrameSize;
}
}
void CheckFakeData(AudioBus* audio_data, int frames_written) {
// Check each channel individually.
for (int ch = 0; ch < channels_; ++ch) {
bool all_zero = true;
for (int i = 0; i < frames_written && all_zero; ++i)
all_zero = audio_data->channel(ch)[i] == 0.0f;
ASSERT_EQ(algorithm_.is_muted(), all_zero) << " for channel " << ch;
}
}
int ComputeConsumedFrames(int initial_frames_enqueued,
int initial_frames_buffered) {
int frame_delta = frames_enqueued_ - initial_frames_enqueued;
int buffered_delta = algorithm_.frames_buffered() - initial_frames_buffered;
int consumed = frame_delta - buffered_delta;
CHECK_GE(consumed, 0);
return consumed;
}
void TestPlaybackRate(double playback_rate) {
const int kDefaultBufferSize = algorithm_.samples_per_second() / 100;
const int kDefaultFramesRequested = 2 * algorithm_.samples_per_second();
TestPlaybackRate(
playback_rate, kDefaultBufferSize, kDefaultFramesRequested);
}
void TestPlaybackRate(double playback_rate,
int buffer_size_in_frames,
int total_frames_requested) {
int initial_frames_enqueued = frames_enqueued_;
int initial_frames_buffered = algorithm_.frames_buffered();
algorithm_.SetPlaybackRate(static_cast<float>(playback_rate));
scoped_ptr<AudioBus> bus =
AudioBus::Create(channels_, buffer_size_in_frames);
if (playback_rate == 0.0) {
int frames_written =
algorithm_.FillBuffer(bus.get(), buffer_size_in_frames);
EXPECT_EQ(0, frames_written);
return;
}
int frames_remaining = total_frames_requested;
while (frames_remaining > 0) {
int frames_requested = std::min(buffer_size_in_frames, frames_remaining);
int frames_written = algorithm_.FillBuffer(bus.get(), frames_requested);
ASSERT_GT(frames_written, 0) << "Requested: " << frames_requested
<< ", playing at " << playback_rate;
CheckFakeData(bus.get(), frames_written);
frames_remaining -= frames_written;
FillAlgorithmQueue();
}
int frames_consumed =
ComputeConsumedFrames(initial_frames_enqueued, initial_frames_buffered);
// If playing back at normal speed, we should always get back the same
// number of bytes requested.
if (playback_rate == 1.0) {
EXPECT_EQ(total_frames_requested, frames_consumed);
return;
}
// Otherwise, allow |kMaxAcceptableDelta| difference between the target and
// actual playback rate.
// When |kSamplesPerSecond| and |total_frames_requested| are reasonably
// large, one can expect less than a 1% difference in most cases. In our
// current implementation, sped up playback is less accurate than slowed
// down playback, and for playback_rate > 1, playback rate generally gets
// less and less accurate the farther it drifts from 1 (though this is
// nonlinear).
double actual_playback_rate =
1.0 * frames_consumed / total_frames_requested;
EXPECT_NEAR(playback_rate, actual_playback_rate, playback_rate / 100.0);
}
protected:
AudioRendererAlgorithm algorithm_;
int frames_enqueued_;
int channels_;
SampleFormat sample_format_;
int bytes_per_sample_;
};
TEST_F(AudioRendererAlgorithmTest, FillBuffer_NormalRate) {
Initialize();
TestPlaybackRate(1.0);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_NearlyNormalFasterRate) {
Initialize();
TestPlaybackRate(1.0001);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_NearlyNormalSlowerRate) {
Initialize();
TestPlaybackRate(0.9999);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_OneAndAQuarterRate) {
Initialize();
TestPlaybackRate(1.25);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_OneAndAHalfRate) {
Initialize();
TestPlaybackRate(1.5);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_DoubleRate) {
Initialize();
TestPlaybackRate(2.0);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_EightTimesRate) {
Initialize();
TestPlaybackRate(8.0);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_ThreeQuartersRate) {
Initialize();
TestPlaybackRate(0.75);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_HalfRate) {
Initialize();
TestPlaybackRate(0.5);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_QuarterRate) {
Initialize();
TestPlaybackRate(0.25);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_Pause) {
Initialize();
TestPlaybackRate(0.0);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_SlowDown) {
Initialize();
TestPlaybackRate(4.5);
TestPlaybackRate(3.0);
TestPlaybackRate(2.0);
TestPlaybackRate(1.0);
TestPlaybackRate(0.5);
TestPlaybackRate(0.25);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_SpeedUp) {
Initialize();
TestPlaybackRate(0.25);
TestPlaybackRate(0.5);
TestPlaybackRate(1.0);
TestPlaybackRate(2.0);
TestPlaybackRate(3.0);
TestPlaybackRate(4.5);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_JumpAroundSpeeds) {
Initialize();
TestPlaybackRate(2.1);
TestPlaybackRate(0.9);
TestPlaybackRate(0.6);
TestPlaybackRate(1.4);
TestPlaybackRate(0.3);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_SmallBufferSize) {
Initialize();
static const int kBufferSizeInFrames = 1;
static const int kFramesRequested = 2 * kSamplesPerSecond;
TestPlaybackRate(1.0, kBufferSizeInFrames, kFramesRequested);
TestPlaybackRate(0.5, kBufferSizeInFrames, kFramesRequested);
TestPlaybackRate(1.5, kBufferSizeInFrames, kFramesRequested);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_LargeBufferSize) {
Initialize(CHANNEL_LAYOUT_STEREO, kSampleFormatS16, 44100);
TestPlaybackRate(1.0);
TestPlaybackRate(0.5);
TestPlaybackRate(1.5);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_LowerQualityAudio) {
Initialize(CHANNEL_LAYOUT_MONO, kSampleFormatU8, kSamplesPerSecond);
TestPlaybackRate(1.0);
TestPlaybackRate(0.5);
TestPlaybackRate(1.5);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_HigherQualityAudio) {
Initialize(CHANNEL_LAYOUT_STEREO, kSampleFormatS32, kSamplesPerSecond);
TestPlaybackRate(1.0);
TestPlaybackRate(0.5);
TestPlaybackRate(1.5);
}
} // namespace media