| // Copyright 2014 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. |
| |
| #include "cobalt/media/base/audio_buffer_converter.h" |
| |
| #include <memory> |
| |
| #include "cobalt/media/base/audio_buffer.h" |
| #include "cobalt/media/base/sinc_resampler.h" |
| #include "cobalt/media/base/test_helpers.h" |
| #include "starboard/types.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace cobalt { |
| namespace media { |
| |
| // Important: Use an odd buffer size here so SIMD issues are caught. |
| const int kOutFrameSize = 441; |
| const int kOutSampleRate = 44100; |
| const ChannelLayout kOutChannelLayout = CHANNEL_LAYOUT_STEREO; |
| const int kOutChannelCount = 2; |
| |
| static scoped_refptr<AudioBuffer> MakeTestBuffer(int sample_rate, |
| ChannelLayout channel_layout, |
| int channel_count, |
| int frames) { |
| return MakeAudioBuffer<uint8_t>(kSampleFormatU8, channel_layout, |
| channel_count, sample_rate, 0, 1, frames, |
| base::TimeDelta::FromSeconds(0)); |
| } |
| |
| class AudioBufferConverterTest : public ::testing::Test { |
| public: |
| AudioBufferConverterTest() |
| : input_frames_(0), |
| expected_output_frames_(0.0), |
| output_frames_(0), |
| output_params_(AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| kOutChannelLayout, kOutSampleRate, 16, kOutFrameSize) { |
| audio_buffer_converter_.reset(new AudioBufferConverter(output_params_)); |
| } |
| |
| void Reset() { |
| audio_buffer_converter_->Reset(); |
| output_frames_ = expected_output_frames_ = input_frames_ = 0; |
| } |
| |
| void AddInput(const scoped_refptr<AudioBuffer>& in) { |
| if (!in->end_of_stream()) { |
| input_frames_ += in->frame_count(); |
| expected_output_frames_ += |
| in->frame_count() * |
| (static_cast<double>(output_params_.sample_rate()) / |
| in->sample_rate()); |
| } |
| audio_buffer_converter_->AddInput(in); |
| } |
| |
| void ConsumeOutput() { |
| ASSERT_TRUE(audio_buffer_converter_->HasNextBuffer()); |
| scoped_refptr<AudioBuffer> out = audio_buffer_converter_->GetNextBuffer(); |
| if (!out->end_of_stream()) { |
| output_frames_ += out->frame_count(); |
| EXPECT_EQ(out->sample_rate(), output_params_.sample_rate()); |
| EXPECT_EQ(out->channel_layout(), output_params_.channel_layout()); |
| EXPECT_EQ(out->channel_count(), output_params_.channels()); |
| } else { |
| EXPECT_FALSE(audio_buffer_converter_->HasNextBuffer()); |
| } |
| } |
| |
| void ConsumeAllOutput() { |
| AddInput(AudioBuffer::CreateEOSBuffer()); |
| while (audio_buffer_converter_->HasNextBuffer()) ConsumeOutput(); |
| EXPECT_EQ(output_frames_, ceil(expected_output_frames_)); |
| } |
| |
| protected: |
| std::unique_ptr<AudioBufferConverter> audio_buffer_converter_; |
| |
| int input_frames_; |
| double expected_output_frames_; |
| int output_frames_; |
| int input_buffers_; |
| AudioParameters output_params_; |
| }; |
| |
| TEST_F(AudioBufferConverterTest, PassThrough) { |
| scoped_refptr<AudioBuffer> in = |
| MakeTestBuffer(kOutSampleRate, kOutChannelLayout, kOutChannelCount, 512); |
| AddInput(in); |
| ConsumeAllOutput(); |
| } |
| |
| TEST_F(AudioBufferConverterTest, Downsample) { |
| scoped_refptr<AudioBuffer> in = |
| MakeTestBuffer(48000, kOutChannelLayout, kOutChannelCount, 512); |
| AddInput(in); |
| ConsumeAllOutput(); |
| } |
| |
| TEST_F(AudioBufferConverterTest, Upsample) { |
| scoped_refptr<AudioBuffer> in = |
| MakeTestBuffer(8000, kOutChannelLayout, kOutChannelCount, 512); |
| AddInput(in); |
| ConsumeAllOutput(); |
| } |
| |
| // Test resampling a buffer smaller than the SincResampler's kernel size. |
| TEST_F(AudioBufferConverterTest, Resample_TinyBuffer) { |
| AddInput(MakeTestBuffer(48000, CHANNEL_LAYOUT_STEREO, 2, |
| SincResampler::kKernelSize - 1)); |
| ConsumeAllOutput(); |
| } |
| |
| TEST_F(AudioBufferConverterTest, Resample_DifferingBufferSizes) { |
| const int input_sample_rate = 48000; |
| AddInput(MakeTestBuffer(input_sample_rate, kOutChannelLayout, |
| kOutChannelCount, 100)); |
| AddInput(MakeTestBuffer(input_sample_rate, kOutChannelLayout, |
| kOutChannelCount, 200)); |
| AddInput(MakeTestBuffer(input_sample_rate, kOutChannelLayout, |
| kOutChannelCount, 300)); |
| AddInput(MakeTestBuffer(input_sample_rate, kOutChannelLayout, |
| kOutChannelCount, 400)); |
| AddInput(MakeTestBuffer(input_sample_rate, kOutChannelLayout, |
| kOutChannelCount, 500)); |
| ConsumeAllOutput(); |
| } |
| |
| TEST_F(AudioBufferConverterTest, ChannelDownmix) { |
| scoped_refptr<AudioBuffer> in = |
| MakeTestBuffer(kOutSampleRate, CHANNEL_LAYOUT_MONO, 1, 512); |
| AddInput(in); |
| ConsumeAllOutput(); |
| } |
| |
| TEST_F(AudioBufferConverterTest, ChannelUpmix) { |
| scoped_refptr<AudioBuffer> in = |
| MakeTestBuffer(kOutSampleRate, CHANNEL_LAYOUT_5_1, 6, 512); |
| AddInput(in); |
| ConsumeAllOutput(); |
| } |
| |
| TEST_F(AudioBufferConverterTest, ResampleAndRemix) { |
| scoped_refptr<AudioBuffer> in = |
| MakeTestBuffer(48000, CHANNEL_LAYOUT_5_1, 6, 512); |
| AddInput(in); |
| ConsumeAllOutput(); |
| } |
| |
| TEST_F(AudioBufferConverterTest, ConfigChange_SampleRate) { |
| AddInput(MakeTestBuffer(48000, kOutChannelLayout, kOutChannelCount, 512)); |
| AddInput(MakeTestBuffer(44100, kOutChannelLayout, kOutChannelCount, 512)); |
| ConsumeAllOutput(); |
| } |
| |
| TEST_F(AudioBufferConverterTest, ConfigChange_ChannelLayout) { |
| AddInput(MakeTestBuffer(kOutSampleRate, CHANNEL_LAYOUT_STEREO, 2, 512)); |
| AddInput(MakeTestBuffer(kOutSampleRate, CHANNEL_LAYOUT_MONO, 1, 512)); |
| ConsumeAllOutput(); |
| } |
| |
| TEST_F(AudioBufferConverterTest, ConfigChange_SampleRateAndChannelLayout) { |
| AddInput(MakeTestBuffer(44100, CHANNEL_LAYOUT_STEREO, 2, 512)); |
| AddInput(MakeTestBuffer(48000, CHANNEL_LAYOUT_MONO, 1, 512)); |
| ConsumeAllOutput(); |
| } |
| |
| TEST_F(AudioBufferConverterTest, ConfigChange_Multiple) { |
| AddInput(MakeTestBuffer(44100, CHANNEL_LAYOUT_STEREO, 2, 512)); |
| AddInput(MakeTestBuffer(48000, CHANNEL_LAYOUT_MONO, 1, 512)); |
| AddInput(MakeTestBuffer(44100, CHANNEL_LAYOUT_5_1, 6, 512)); |
| AddInput(MakeTestBuffer(22050, CHANNEL_LAYOUT_STEREO, 2, 512)); |
| ConsumeAllOutput(); |
| } |
| |
| TEST_F(AudioBufferConverterTest, Reset) { |
| AddInput(MakeTestBuffer(44100, CHANNEL_LAYOUT_STEREO, 2, 512)); |
| Reset(); |
| ConsumeAllOutput(); |
| } |
| |
| TEST_F(AudioBufferConverterTest, ResampleThenReset) { |
| // Resampling is likely to leave some data buffered in AudioConverter's |
| // fifo or resampler, so make sure Reset() cleans that all up. |
| AddInput(MakeTestBuffer(48000, CHANNEL_LAYOUT_STEREO, 2, 512)); |
| Reset(); |
| ConsumeAllOutput(); |
| } |
| |
| TEST_F(AudioBufferConverterTest, ResetThenConvert) { |
| AddInput( |
| MakeTestBuffer(kOutSampleRate, kOutChannelLayout, kOutChannelCount, 512)); |
| Reset(); |
| // Make sure we can keep using the AudioBufferConverter after we've Reset(). |
| AddInput( |
| MakeTestBuffer(kOutSampleRate, kOutChannelLayout, kOutChannelCount, 512)); |
| ConsumeAllOutput(); |
| } |
| |
| TEST_F(AudioBufferConverterTest, DiscreteChannelLayout) { |
| output_params_ = |
| AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| CHANNEL_LAYOUT_DISCRETE, kOutSampleRate, 16, 512); |
| output_params_.set_channels_for_discrete(2); |
| audio_buffer_converter_.reset(new AudioBufferConverter(output_params_)); |
| AddInput(MakeTestBuffer(kOutSampleRate, CHANNEL_LAYOUT_STEREO, 2, 512)); |
| ConsumeAllOutput(); |
| } |
| |
| TEST_F(AudioBufferConverterTest, LargeBuffersResampling) { |
| output_params_ = AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| kOutChannelLayout, kOutSampleRate, 16, 2048); |
| |
| audio_buffer_converter_.reset(new AudioBufferConverter(output_params_)); |
| const int kInputSampleRate = 48000; |
| const int kInputFrameSize = 8192; |
| ASSERT_NE(kInputSampleRate, kOutSampleRate); |
| |
| const int kInputBuffers = 3; |
| for (int i = 0; i < kInputBuffers; ++i) { |
| AddInput(MakeTestBuffer(kInputSampleRate, kOutChannelLayout, |
| kOutChannelCount, kInputFrameSize)); |
| } |
| |
| // Do not add an EOS packet here, as it will invoke flushing. |
| while (audio_buffer_converter_->HasNextBuffer()) ConsumeOutput(); |
| |
| // Since the input buffer size is a multiple of the input request size there |
| // should never be any frames remaining at this point. |
| ASSERT_EQ(kInputFrameSize % |
| audio_buffer_converter_->input_buffer_size_for_testing(), |
| 0); |
| EXPECT_EQ(0, audio_buffer_converter_->input_frames_left_for_testing()); |
| } |
| |
| } // namespace media |
| } // namespace cobalt |