| // 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. |
| |
| #include <limits> |
| |
| #include "base/stringprintf.h" |
| #include "base/time.h" |
| #include "media/audio/audio_parameters.h" |
| #include "media/base/audio_bus.h" |
| #include "media/base/channel_layout.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace media { |
| |
| static const int kChannels = 6; |
| static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_5_1; |
| // Use a buffer size which is intentionally not a multiple of kChannelAlignment. |
| static const int kFrameCount = media::AudioBus::kChannelAlignment * 32 - 1; |
| static const int kSampleRate = 48000; |
| |
| class AudioBusTest : public testing::Test { |
| public: |
| AudioBusTest() {} |
| ~AudioBusTest() { |
| for (size_t i = 0; i < data_.size(); ++i) |
| base::AlignedFree(data_[i]); |
| } |
| |
| // Validate parameters returned by AudioBus v.s. the constructed parameters. |
| void VerifyParams(AudioBus* bus) { |
| EXPECT_EQ(kChannels, bus->channels()); |
| EXPECT_EQ(kFrameCount, bus->frames()); |
| } |
| |
| void VerifyValue(const float data[], int size, float value) { |
| for (int i = 0; i < size; ++i) |
| ASSERT_FLOAT_EQ(value, data[i]) << "i=" << i; |
| } |
| |
| // Verify values for each channel in |result| against |expected|. |
| void VerifyBus(const AudioBus* result, const AudioBus* expected) { |
| ASSERT_EQ(expected->channels(), result->channels()); |
| ASSERT_EQ(expected->frames(), result->frames()); |
| for (int ch = 0; ch < result->channels(); ++ch) { |
| for (int i = 0; i < result->frames(); ++i) { |
| SCOPED_TRACE(base::StringPrintf("ch=%d, i=%d", ch, i)); |
| ASSERT_FLOAT_EQ(expected->channel(ch)[i], result->channel(ch)[i]); |
| } |
| } |
| } |
| |
| // Read and write to the full extent of the allocated channel data. Also test |
| // the Zero() method and verify it does as advertised. Also test data if data |
| // is 16-byte aligned as advertised (see kChannelAlignment in audio_bus.h). |
| void VerifyChannelData(AudioBus* bus) { |
| for (int i = 0; i < bus->channels(); ++i) { |
| ASSERT_EQ(0U, reinterpret_cast<uintptr_t>( |
| bus->channel(i)) & (AudioBus::kChannelAlignment - 1)); |
| std::fill(bus->channel(i), bus->channel(i) + bus->frames(), i); |
| } |
| |
| for (int i = 0; i < bus->channels(); ++i) |
| VerifyValue(bus->channel(i), bus->frames(), i); |
| |
| bus->Zero(); |
| for (int i = 0; i < bus->channels(); ++i) |
| VerifyValue(bus->channel(i), bus->frames(), 0); |
| } |
| |
| // Verify copying to and from |bus1| and |bus2|. |
| void CopyTest(AudioBus* bus1, AudioBus* bus2) { |
| // Fill |bus1| with dummy data. |
| for (int i = 0; i < bus1->channels(); ++i) |
| std::fill(bus1->channel(i), bus1->channel(i) + bus1->frames(), i); |
| |
| // Verify copy from |bus1| to |bus2|. |
| bus2->Zero(); |
| bus1->CopyTo(bus2); |
| VerifyBus(bus1, bus2); |
| |
| // Verify copy from |bus2| to |bus1|. |
| bus1->Zero(); |
| bus2->CopyTo(bus1); |
| VerifyBus(bus2, bus1); |
| } |
| |
| protected: |
| std::vector<float*> data_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AudioBusTest); |
| }; |
| |
| // Verify basic Create(...) method works as advertised. |
| TEST_F(AudioBusTest, Create) { |
| scoped_ptr<AudioBus> bus = AudioBus::Create(kChannels, kFrameCount); |
| VerifyParams(bus.get()); |
| VerifyChannelData(bus.get()); |
| } |
| |
| // Verify Create(...) using AudioParameters works as advertised. |
| TEST_F(AudioBusTest, CreateUsingAudioParameters) { |
| scoped_ptr<AudioBus> bus = AudioBus::Create(AudioParameters( |
| AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate, 32, |
| kFrameCount)); |
| VerifyParams(bus.get()); |
| VerifyChannelData(bus.get()); |
| } |
| |
| // Verify an AudioBus created via wrapping a vector works as advertised. |
| TEST_F(AudioBusTest, WrapVector) { |
| data_.reserve(kChannels); |
| for (int i = 0; i < kChannels; ++i) { |
| data_.push_back(static_cast<float*>(base::AlignedAlloc( |
| sizeof(*data_[i]) * kFrameCount, AudioBus::kChannelAlignment))); |
| } |
| |
| scoped_ptr<AudioBus> bus = AudioBus::WrapVector(kFrameCount, data_); |
| VerifyParams(bus.get()); |
| VerifyChannelData(bus.get()); |
| } |
| |
| // Verify an AudioBus created via wrapping a memory block works as advertised. |
| TEST_F(AudioBusTest, WrapMemory) { |
| AudioParameters params( |
| AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate, 32, |
| kFrameCount); |
| int data_size = AudioBus::CalculateMemorySize(params); |
| scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> data(static_cast<float*>( |
| base::AlignedAlloc(data_size, AudioBus::kChannelAlignment))); |
| |
| // Fill the memory with a test value we can check for after wrapping. |
| static const float kTestValue = 3; |
| std::fill( |
| data.get(), data.get() + data_size / sizeof(*data.get()), kTestValue); |
| |
| scoped_ptr<AudioBus> bus = AudioBus::WrapMemory(params, data.get()); |
| // Verify the test value we filled prior to wrapping. |
| for (int i = 0; i < bus->channels(); ++i) |
| VerifyValue(bus->channel(i), bus->frames(), kTestValue); |
| VerifyParams(bus.get()); |
| VerifyChannelData(bus.get()); |
| |
| // Verify the channel vectors lie within the provided memory block. |
| EXPECT_GE(bus->channel(0), data.get()); |
| EXPECT_LE(bus->channel(bus->channels() - 1) + bus->frames(), |
| data.get() + data_size / sizeof(*data.get())); |
| } |
| |
| // Simulate a shared memory transfer and verify results. |
| TEST_F(AudioBusTest, CopyTo) { |
| // Create one bus with AudioParameters and the other through direct values to |
| // test for parity between the Create() functions. |
| AudioParameters params( |
| AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate, 32, |
| kFrameCount); |
| scoped_ptr<AudioBus> bus1 = AudioBus::Create(kChannels, kFrameCount); |
| scoped_ptr<AudioBus> bus2 = AudioBus::Create(params); |
| |
| { |
| SCOPED_TRACE("Created"); |
| CopyTest(bus1.get(), bus2.get()); |
| } |
| { |
| SCOPED_TRACE("Wrapped Vector"); |
| // Try a copy to an AudioBus wrapping a vector. |
| data_.reserve(kChannels); |
| for (int i = 0; i < kChannels; ++i) { |
| data_.push_back(static_cast<float*>(base::AlignedAlloc( |
| sizeof(*data_[i]) * kFrameCount, AudioBus::kChannelAlignment))); |
| } |
| |
| bus2 = AudioBus::WrapVector(kFrameCount, data_); |
| CopyTest(bus1.get(), bus2.get()); |
| } |
| { |
| SCOPED_TRACE("Wrapped Memory"); |
| // Try a copy to an AudioBus wrapping a memory block. |
| scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> data( |
| static_cast<float*>(base::AlignedAlloc( |
| AudioBus::CalculateMemorySize(params), |
| AudioBus::kChannelAlignment))); |
| |
| bus2 = AudioBus::WrapMemory(params, data.get()); |
| CopyTest(bus1.get(), bus2.get()); |
| } |
| } |
| |
| // Verify Zero() and ZeroFrames(...) utility methods work as advertised. |
| TEST_F(AudioBusTest, Zero) { |
| scoped_ptr<AudioBus> bus = AudioBus::Create(kChannels, kFrameCount); |
| |
| // Fill the bus with dummy data. |
| for (int i = 0; i < bus->channels(); ++i) |
| std::fill(bus->channel(i), bus->channel(i) + bus->frames(), i + 1); |
| |
| // Zero first half the frames of each channel. |
| bus->ZeroFrames(kFrameCount / 2); |
| for (int i = 0; i < bus->channels(); ++i) { |
| SCOPED_TRACE("First Half Zero"); |
| VerifyValue(bus->channel(i), kFrameCount / 2, 0); |
| VerifyValue(bus->channel(i) + kFrameCount / 2, |
| kFrameCount - kFrameCount / 2, i + 1); |
| } |
| |
| // Fill the bus with dummy data. |
| for (int i = 0; i < bus->channels(); ++i) |
| std::fill(bus->channel(i), bus->channel(i) + bus->frames(), i + 1); |
| |
| // Zero the last half of the frames. |
| bus->ZeroFramesPartial(kFrameCount / 2, kFrameCount - kFrameCount / 2); |
| for (int i = 0; i < bus->channels(); ++i) { |
| SCOPED_TRACE("Last Half Zero"); |
| VerifyValue(bus->channel(i) + kFrameCount / 2, |
| kFrameCount - kFrameCount / 2, 0); |
| VerifyValue(bus->channel(i), kFrameCount / 2, i + 1); |
| } |
| |
| // Fill the bus with dummy data. |
| for (int i = 0; i < bus->channels(); ++i) |
| std::fill(bus->channel(i), bus->channel(i) + bus->frames(), i + 1); |
| |
| // Zero all the frames of each channel. |
| bus->Zero(); |
| for (int i = 0; i < bus->channels(); ++i) { |
| SCOPED_TRACE("All Zero"); |
| VerifyValue(bus->channel(i), bus->frames(), 0); |
| } |
| } |
| |
| // Each test vector represents two channels of data in the following arbitrary |
| // layout: <min, zero, max, min, zero, max, zero, zero>. |
| static const int kTestVectorSize = 8; |
| static const uint8 kTestVectorUint8[kTestVectorSize] = { |
| 0, -kint8min, kuint8max, 0, -kint8min, kuint8max, -kint8min, -kint8min }; |
| static const int16 kTestVectorInt16[kTestVectorSize] = { |
| kint16min, 0, kint16max, kint16min, 0, kint16max, 0, 0 }; |
| static const int32 kTestVectorInt32[kTestVectorSize] = { |
| kint32min, 0, kint32max, kint32min, 0, kint32max, 0, 0 }; |
| |
| // Expected results. |
| static const int kTestVectorFrames = kTestVectorSize / 2; |
| static const float kTestVectorResult[][kTestVectorFrames] = { |
| { -1, 1, 0, 0 }, { 0, -1, 1, 0 }}; |
| static const int kTestVectorChannels = arraysize(kTestVectorResult); |
| |
| // Verify FromInterleaved() deinterleaves audio in supported formats correctly. |
| TEST_F(AudioBusTest, FromInterleaved) { |
| scoped_ptr<AudioBus> bus = AudioBus::Create( |
| kTestVectorChannels, kTestVectorFrames); |
| scoped_ptr<AudioBus> expected = AudioBus::Create( |
| kTestVectorChannels, kTestVectorFrames); |
| for (int ch = 0; ch < kTestVectorChannels; ++ch) { |
| memcpy(expected->channel(ch), kTestVectorResult[ch], |
| kTestVectorFrames * sizeof(*expected->channel(ch))); |
| } |
| { |
| SCOPED_TRACE("uint8"); |
| bus->Zero(); |
| bus->FromInterleaved( |
| kTestVectorUint8, kTestVectorFrames, sizeof(*kTestVectorUint8)); |
| VerifyBus(bus.get(), expected.get()); |
| } |
| { |
| SCOPED_TRACE("int16"); |
| bus->Zero(); |
| bus->FromInterleaved( |
| kTestVectorInt16, kTestVectorFrames, sizeof(*kTestVectorInt16)); |
| VerifyBus(bus.get(), expected.get()); |
| } |
| { |
| SCOPED_TRACE("int32"); |
| bus->Zero(); |
| bus->FromInterleaved( |
| kTestVectorInt32, kTestVectorFrames, sizeof(*kTestVectorInt32)); |
| VerifyBus(bus.get(), expected.get()); |
| } |
| } |
| |
| // Verify FromInterleavedPartial() deinterleaves audio correctly. |
| TEST_F(AudioBusTest, FromInterleavedPartial) { |
| // Only deinterleave the middle two frames in each channel. |
| static const int kPartialStart = 1; |
| static const int kPartialFrames = 2; |
| ASSERT_LE(kPartialStart + kPartialFrames, kTestVectorFrames); |
| |
| scoped_ptr<AudioBus> bus = AudioBus::Create( |
| kTestVectorChannels, kTestVectorFrames); |
| scoped_ptr<AudioBus> expected = AudioBus::Create( |
| kTestVectorChannels, kTestVectorFrames); |
| expected->Zero(); |
| for (int ch = 0; ch < kTestVectorChannels; ++ch) { |
| memcpy(expected->channel(ch) + kPartialStart, |
| kTestVectorResult[ch] + kPartialStart, |
| kPartialFrames * sizeof(*expected->channel(ch))); |
| } |
| |
| bus->Zero(); |
| bus->FromInterleavedPartial( |
| kTestVectorInt32 + kPartialStart * bus->channels(), kPartialStart, |
| kPartialFrames, sizeof(*kTestVectorInt32)); |
| VerifyBus(bus.get(), expected.get()); |
| } |
| |
| // Verify ToInterleaved() interleaves audio in suported formats correctly. |
| TEST_F(AudioBusTest, ToInterleaved) { |
| scoped_ptr<AudioBus> bus = AudioBus::Create( |
| kTestVectorChannels, kTestVectorFrames); |
| // Fill the bus with our test vector. |
| for (int ch = 0; ch < kTestVectorChannels; ++ch) { |
| memcpy(bus->channel(ch), kTestVectorResult[ch], |
| kTestVectorFrames * sizeof(*bus->channel(ch))); |
| } |
| { |
| SCOPED_TRACE("uint8"); |
| uint8 test_array[arraysize(kTestVectorUint8)]; |
| bus->ToInterleaved(bus->frames(), sizeof(*kTestVectorUint8), test_array); |
| ASSERT_EQ(memcmp( |
| test_array, kTestVectorUint8, arraysize(kTestVectorUint8)), 0); |
| } |
| { |
| SCOPED_TRACE("int16"); |
| int16 test_array[arraysize(kTestVectorInt16)]; |
| bus->ToInterleaved(bus->frames(), sizeof(*kTestVectorInt16), test_array); |
| ASSERT_EQ(memcmp( |
| test_array, kTestVectorInt16, arraysize(kTestVectorInt16)), 0); |
| } |
| { |
| SCOPED_TRACE("int32"); |
| int32 test_array[arraysize(kTestVectorInt32)]; |
| bus->ToInterleaved(bus->frames(), sizeof(*kTestVectorInt32), test_array); |
| ASSERT_EQ(memcmp( |
| test_array, kTestVectorInt32, arraysize(kTestVectorInt32)), 0); |
| } |
| } |
| |
| // Verify ToInterleavedPartial() interleaves audio correctly. |
| TEST_F(AudioBusTest, ToInterleavedPartial) { |
| // Only interleave the middle two frames in each channel. |
| static const int kPartialStart = 1; |
| static const int kPartialFrames = 2; |
| ASSERT_LE(kPartialStart + kPartialFrames, kTestVectorFrames); |
| |
| scoped_ptr<AudioBus> expected = AudioBus::Create( |
| kTestVectorChannels, kTestVectorFrames); |
| for (int ch = 0; ch < kTestVectorChannels; ++ch) { |
| memcpy(expected->channel(ch), kTestVectorResult[ch], |
| kTestVectorFrames * sizeof(*expected->channel(ch))); |
| } |
| |
| int16 test_array[arraysize(kTestVectorInt16)]; |
| expected->ToInterleavedPartial( |
| kPartialStart, kPartialFrames, sizeof(*kTestVectorInt16), test_array); |
| ASSERT_EQ(memcmp( |
| test_array, kTestVectorInt16 + kPartialStart * kTestVectorChannels, |
| kPartialFrames * sizeof(*kTestVectorInt16)), 0); |
| } |
| |
| } // namespace media |