#include "media/base/audio_bus.h"
#include <stddef.h>
#include <stdint.h>
#include <array>
#include <limits>
#include <memory>
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/memory/aligned_memory.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "media/base/audio_parameters.h"
#include "media/base/audio_sample_types.h"
#include "media/base/channel_layout.h"
#include "media/base/fake_audio_render_callback.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
using AlignedFloatArray = base::AlignedHeapArray<float>;
static const int kChannels = 6;
static constexpr ChannelLayout kChannelLayout = CHANNEL_LAYOUT_5_1;
static const size_t kFrameCount = media::AudioBus::kChannelAlignment * 32 - 1;
static const int kSampleRate = 48000;
class AudioBusTest : public testing::Test {
public:
AudioBusTest() = default;
AudioBusTest(const AudioBusTest&) = delete;
AudioBusTest& operator=(const AudioBusTest&) = delete;
~AudioBusTest() override = default;
void VerifyChannelAndFrameCount(AudioBus* bus) {
EXPECT_EQ(kChannels, bus->channels());
EXPECT_EQ(static_cast<int>(kFrameCount), bus->frames());
}
void VerifySpanIsFilledWithValue(AudioBus::ConstChannel data, float value) {
for (size_t i = 0; i < data.size(); ++i) {
ASSERT_FLOAT_EQ(value, data[i]) << "i=" << i;
}
}
void VerifyAreEqualWithEpsilon(const AudioBus* result,
const AudioBus* expected,
float epsilon) {
ASSERT_EQ(expected->channels(), result->channels());
ASSERT_EQ(expected->frames(), result->frames());
ASSERT_EQ(expected->is_bitstream_format(), result->is_bitstream_format());
if (expected->is_bitstream_format()) {
ASSERT_EQ(expected->GetBitstreamFrames(), result->GetBitstreamFrames());
ASSERT_EQ(expected->bitstream_data(), result->bitstream_data());
return;
}
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));
if (epsilon == 0) {
ASSERT_FLOAT_EQ(expected->channel_span(ch)[i],
result->channel_span(ch)[i]);
} else {
ASSERT_NEAR(expected->channel_span(ch)[i],
result->channel_span(ch)[i], epsilon);
}
}
}
}
void VerifyAreEqual(const AudioBus* result, const AudioBus* expected) {
VerifyAreEqualWithEpsilon(result, expected, 0);
}
void VerifyReadWriteAndAlignment(AudioBus* bus) {
int channel_count = 0;
for (auto channel : bus->AllChannels()) {
ASSERT_TRUE(AudioBus::IsAligned(channel));
std::ranges::fill(channel, channel_count++);
}
for (int i = 0; i < bus->channels(); ++i) {
VerifySpanIsFilledWithValue(bus->channel_span(i), i);
}
bus->Zero();
for (int i = 0; i < bus->channels(); ++i) {
VerifySpanIsFilledWithValue(bus->channel_span(i), 0);
}
}
void CopyTest(AudioBus* bus1, AudioBus* bus2) {
if (bus1->is_bitstream_format()) {
uint8_t filler = 1;
std::ranges::generate(bus1->bitstream_data(),
[filler]() mutable { return filler++; });
} else {
int filler = 1;
for (auto channel : bus1->AllChannels()) {
std::ranges::fill(channel, filler);
}
}
bus2->Zero();
bus1->CopyTo(bus2);
VerifyAreEqual(bus1, bus2);
bus1->Zero();
bus2->CopyTo(bus1);
VerifyAreEqual(bus2, bus1);
}
protected:
void AllocateDataPerChannel() {
data_.reserve(kChannels);
for (int i = 0; i < kChannels; ++i) {
data_.push_back(
base::AlignedUninit<float>(kFrameCount, AudioBus::kChannelAlignment));
}
}
std::vector<AlignedFloatArray> data_;
};
TEST_F(AudioBusTest, Create) {
std::unique_ptr<AudioBus> bus = AudioBus::Create(kChannels, kFrameCount);
VerifyChannelAndFrameCount(bus.get());
VerifyReadWriteAndAlignment(bus.get());
}
TEST_F(AudioBusTest, CreateUsingAudioParameters) {
std::unique_ptr<AudioBus> bus = AudioBus::Create(
AudioParameters(AudioParameters::AUDIO_PCM_LINEAR,
ChannelLayoutConfig::FromLayout<kChannelLayout>(),
kSampleRate, kFrameCount));
VerifyChannelAndFrameCount(bus.get());
VerifyReadWriteAndAlignment(bus.get());
}
TEST_F(AudioBusTest, CreateWrapper) {
AllocateDataPerChannel();
std::unique_ptr<AudioBus> bus = AudioBus::CreateWrapper(kChannels);
bus->set_frames(kFrameCount);
for (int i = 0; i < bus->channels(); ++i) {
bus->SetChannelData(i, data_[i].as_span());
}
bool deleted = false;
bus->SetWrappedDataDeleter(
base::BindLambdaForTesting([&]() { deleted = true; }));
VerifyChannelAndFrameCount(bus.get());
VerifyReadWriteAndAlignment(bus.get());
EXPECT_FALSE(deleted);
bus.reset();
EXPECT_TRUE(deleted);
}
TEST_F(AudioBusTest, AllChannels) {
AllocateDataPerChannel();
std::unique_ptr<AudioBus> bus = AudioBus::CreateWrapper(kChannels);
bus->set_frames(kFrameCount);
AudioBus::ChannelVector channels;
int value = 1;
for (AlignedFloatArray& data : data_) {
AudioBus::Channel channel(data);
std::ranges::fill(channel, value++);
channels.push_back(channel);
}
bus->SetAllChannels(channels);
VerifyChannelAndFrameCount(bus.get());
int current_channel = 0;
for (auto channel : bus->AllChannels()) {
EXPECT_EQ(channel, bus->channel_span(current_channel++));
}
EXPECT_EQ(current_channel, kChannels);
}
TEST_F(AudioBusTest, AllChannelsSubspan) {
AllocateDataPerChannel();
std::unique_ptr<AudioBus> bus = AudioBus::CreateWrapper(kChannels);
bus->set_frames(kFrameCount);
AudioBus::ChannelVector channels;
int value = 1;
for (AlignedFloatArray& data : data_) {
AudioBus::Channel channel(data);
for (float& sample : channel) {
sample = value++;
}
channels.push_back(channel);
}
bus->SetAllChannels(channels);
int current_channel = 0;
constexpr size_t kOffset = 3;
constexpr size_t kCount = 25;
for (auto channel : bus->AllChannelsSubspan(kOffset, kCount)) {
EXPECT_EQ(channel,
bus->channel_span(current_channel++).subspan(kOffset, kCount));
}
EXPECT_EQ(current_channel, kChannels);
}
TEST_F(AudioBusTest, WrapMemory) {
auto verify_wrapped_memory = [&](bool use_byte_span) {
AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR,
ChannelLayoutConfig::FromLayout<kChannelLayout>(),
kSampleRate, kFrameCount);
const size_t total_frame_count =
AudioBus::CalculateMemorySize(params) / sizeof(float);
auto float_data = base::AlignedUninit<float>(total_frame_count,
AudioBus::kChannelAlignment);
base::span<float> float_span = base::span(float_data);
static constexpr float kTestValue = 3;
std::ranges::fill(float_data, kTestValue);
std::unique_ptr<AudioBus> bus;
if (use_byte_span) {
base::span<uint8_t> byte_span =
base::as_writable_bytes(base::allow_nonunique_obj, float_span);
bus = AudioBus::WrapMemory(params, byte_span);
} else {
bus = AudioBus::WrapMemory(params, float_span);
}
for (auto channel : bus->AllChannels()) {
VerifySpanIsFilledWithValue(channel, kTestValue);
}
VerifyChannelAndFrameCount(bus.get());
VerifyReadWriteAndAlignment(bus.get());
auto all_channels = bus->AllChannels();
auto first_channel = all_channels.front();
auto last_channel = all_channels.back();
EXPECT_GE(&first_channel.front(), &float_span.front());
EXPECT_LT(&last_channel.back(), &float_span.back());
};
{
SCOPED_TRACE("uint8_t span");
verify_wrapped_memory(true);
}
{
SCOPED_TRACE("float span");
verify_wrapped_memory(false);
}
}
TEST_F(AudioBusTest, CopyTo) {
AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR,
ChannelLayoutConfig::FromLayout<kChannelLayout>(),
kSampleRate, kFrameCount);
std::unique_ptr<AudioBus> bus1 = AudioBus::Create(kChannels, kFrameCount);
std::unique_ptr<AudioBus> bus2 = AudioBus::Create(params);
const size_t memory_size = AudioBus::CalculateMemorySize(params);
{
SCOPED_TRACE("Created");
CopyTest(bus1.get(), bus2.get());
}
{
SCOPED_TRACE("Wrapped Memory - byte span");
auto data =
base::AlignedUninit<uint8_t>(memory_size, AudioBus::kChannelAlignment);
bus2 = AudioBus::WrapMemory(params, data.as_span());
CopyTest(bus1.get(), bus2.get());
}
{
SCOPED_TRACE("Wrapped Memory - float span");
auto data = base::AlignedUninit<float>(memory_size / sizeof(float),
AudioBus::kChannelAlignment);
bus2 = AudioBus::WrapMemory(params, data.as_span());
CopyTest(bus1.get(), bus2.get());
}
}
TEST_F(AudioBusTest, Zero) {
std::unique_ptr<AudioBus> bus = AudioBus::Create(kChannels, kFrameCount);
int value = 1;
for (auto channel : bus->AllChannels()) {
std::ranges::fill(channel, value++);
}
EXPECT_FALSE(bus->AreFramesZero());
bus->ZeroFrames(kFrameCount / 2);
value = 1;
for (auto channel : bus->AllChannels()) {
SCOPED_TRACE("First Half Zero");
auto [first_half, second_half] = channel.split_at(kFrameCount / 2);
VerifySpanIsFilledWithValue(first_half, 0);
VerifySpanIsFilledWithValue(second_half, value++);
}
EXPECT_FALSE(bus->AreFramesZero());
value = 1;
for (auto channel : bus->AllChannels()) {
std::ranges::fill(channel, value++);
}
static constexpr size_t kRemainingFrames = kFrameCount - kFrameCount / 2;
bus->ZeroFramesPartial(kFrameCount / 2, kRemainingFrames);
value = 1;
for (auto channel : bus->AllChannels()) {
SCOPED_TRACE("Last Half Zero");
auto [first_half, second_half] = channel.split_at(kFrameCount / 2);
VerifySpanIsFilledWithValue(first_half, value++);
VerifySpanIsFilledWithValue(second_half, 0);
}
EXPECT_FALSE(bus->AreFramesZero());
value = 1;
for (auto channel : bus->AllChannels()) {
std::ranges::fill(channel, value++);
}
bus->Zero();
for (auto channel : bus->AllChannels()) {
SCOPED_TRACE("All Zero");
VerifySpanIsFilledWithValue(channel, 0);
}
EXPECT_TRUE(bus->AreFramesZero());
}
static constexpr int kTestVectorSize = 10;
static const uint8_t kTestVectorUint8[kTestVectorSize] = {
0, -INT8_MIN, UINT8_MAX,
0, INT8_MAX / 2 + 128, INT8_MIN / 2 + 128,
-INT8_MIN, UINT8_MAX, -INT8_MIN,
-INT8_MIN};
static const int16_t kTestVectorInt16[kTestVectorSize] = {
INT16_MIN, 0, INT16_MAX, INT16_MIN, INT16_MAX / 2,
INT16_MIN / 2, 0, INT16_MAX, 0, 0};
static const int32_t kTestVectorInt32[kTestVectorSize] = {
INT32_MIN, 0, INT32_MAX, INT32_MIN, INT32_MAX / 2,
INT32_MIN / 2, 0, INT32_MAX, 0, 0};
static const float kTestVectorFloat32[kTestVectorSize] = {
-1.0f, 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f};
static const float kTestVectorFloat32Invalid[kTestVectorSize] = {
-5.0f,
0.0f,
5.0f,
-1.0f,
0.5f,
-0.5f,
0.0f,
std::numeric_limits<float>::infinity(),
std::numeric_limits<float>::signaling_NaN(),
std::numeric_limits<float>::quiet_NaN()};
static const float kTestVectorFloat32Sanitized[kTestVectorSize] = {
-1.0f, 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f};
static constexpr size_t kTestVectorFrameCount = kTestVectorSize / 2;
static const auto kTestVectorResult =
std::to_array<std::array<const float, kTestVectorFrameCount>>(
{{-1.0f, 1.0f, 0.5f, 0.0f, 0.0f}, {0.0f, -1.0f, -0.5f, 1.0f, 0.0f}});
static const int kTestVectorChannelCount = std::size(kTestVectorResult);
TEST_F(AudioBusTest, FromInterleaved) {
std::unique_ptr<AudioBus> bus =
AudioBus::Create(kTestVectorChannelCount, kTestVectorFrameCount);
std::unique_ptr<AudioBus> expected =
AudioBus::Create(kTestVectorChannelCount, kTestVectorFrameCount);
for (int ch = 0; ch < kTestVectorChannelCount; ++ch) {
expected->channel_span(ch).copy_from(base::span(kTestVectorResult[ch]));
}
{
SCOPED_TRACE("UnsignedInt8SampleTypeTraits");
bus->Zero();
bus->FromInterleaved<UnsignedInt8SampleTypeTraits>(kTestVectorUint8,
kTestVectorFrameCount);
VerifyAreEqualWithEpsilon(bus.get(), expected.get(),
1.0f / (std::numeric_limits<uint8_t>::max() - 1));
}
{
SCOPED_TRACE("SignedInt16SampleTypeTraits");
bus->Zero();
bus->FromInterleaved<SignedInt16SampleTypeTraits>(kTestVectorInt16,
kTestVectorFrameCount);
VerifyAreEqualWithEpsilon(
bus.get(), expected.get(),
1.0f /
(static_cast<float>(std::numeric_limits<uint16_t>::max()) + 1.0f));
}
{
SCOPED_TRACE("SignedInt32SampleTypeTraits");
bus->Zero();
bus->FromInterleaved<SignedInt32SampleTypeTraits>(kTestVectorInt32,
kTestVectorFrameCount);
VerifyAreEqualWithEpsilon(
bus.get(), expected.get(),
1.0f / static_cast<float>(std::numeric_limits<uint32_t>::max()));
}
{
SCOPED_TRACE("Float32SampleTypeTraits");
bus->Zero();
bus->FromInterleaved<Float32SampleTypeTraits>(kTestVectorFloat32,
kTestVectorFrameCount);
VerifyAreEqual(bus.get(), expected.get());
}
}
TEST_F(AudioBusTest, FromInterleavedPartial) {
static constexpr size_t kPartialStart = 1;
static constexpr size_t kPartialFrames = 2;
ASSERT_LE(kPartialStart + kPartialFrames, kTestVectorFrameCount);
std::unique_ptr<AudioBus> bus =
AudioBus::Create(kTestVectorChannelCount, kTestVectorFrameCount);
std::unique_ptr<AudioBus> expected =
AudioBus::Create(kTestVectorChannelCount, kTestVectorFrameCount);
expected->Zero();
int current_channel = 0;
for (auto partial_channel :
expected->AllChannelsSubspan(kPartialStart, kPartialFrames)) {
partial_channel.copy_from(base::span(kTestVectorResult[current_channel++])
.subspan(kPartialStart, kPartialFrames));
}
{
SCOPED_TRACE("SignedInt32SampleTypeTraits");
bus->Zero();
bus->FromInterleavedPartial<SignedInt32SampleTypeTraits>(
UNSAFE_TODO(kTestVectorInt32 + kPartialStart * bus->channels()),
kPartialStart, kPartialFrames);
VerifyAreEqual(bus.get(), expected.get());
}
}
TEST_F(AudioBusTest, ToInterleaved) {
std::unique_ptr<AudioBus> bus =
AudioBus::Create(kTestVectorChannelCount, kTestVectorFrameCount);
for (int ch = 0; ch < bus->channels(); ++ch) {
bus->channel_span(ch).copy_from(base::span(kTestVectorResult[ch]));
}
{
SCOPED_TRACE("UnsignedInt8SampleTypeTraits");
uint8_t test_array[std::size(kTestVectorUint8)];
bus->ToInterleaved<UnsignedInt8SampleTypeTraits>(bus->frames(), test_array);
UNSAFE_TODO(ASSERT_EQ(
0, memcmp(test_array, kTestVectorUint8, sizeof(kTestVectorUint8))));
}
{
SCOPED_TRACE("SignedInt16SampleTypeTraits");
int16_t test_array[std::size(kTestVectorInt16)];
bus->ToInterleaved<SignedInt16SampleTypeTraits>(bus->frames(), test_array);
UNSAFE_TODO(ASSERT_EQ(
0, memcmp(test_array, kTestVectorInt16, sizeof(kTestVectorInt16))));
}
{
SCOPED_TRACE("SignedInt32SampleTypeTraits");
int32_t test_array[std::size(kTestVectorInt32)];
bus->ToInterleaved<SignedInt32SampleTypeTraits>(bus->frames(), test_array);
int32_t alternative_acceptable_result[std::size(kTestVectorInt32)];
UNSAFE_TODO(memcpy(alternative_acceptable_result, kTestVectorInt32,
sizeof(kTestVectorInt32)));
ASSERT_EQ(alternative_acceptable_result[4],
std::numeric_limits<int32_t>::max() / 2);
alternative_acceptable_result[4]++;
UNSAFE_TODO(ASSERT_TRUE(
memcmp(test_array, kTestVectorInt32, sizeof(kTestVectorInt32)) == 0 ||
memcmp(test_array, alternative_acceptable_result,
sizeof(alternative_acceptable_result)) == 0));
}
{
SCOPED_TRACE("Float32SampleTypeTraits");
float test_array[std::size(kTestVectorFloat32)];
bus->ToInterleaved<Float32SampleTypeTraits>(bus->frames(), test_array);
UNSAFE_TODO(ASSERT_EQ(
0, memcmp(test_array, kTestVectorFloat32, sizeof(kTestVectorFloat32))));
}
}
TEST_F(AudioBusTest, ToInterleavedSanitized) {
std::unique_ptr<AudioBus> bus =
AudioBus::Create(kTestVectorChannelCount, kTestVectorFrameCount);
bus->FromInterleaved<Float32SampleTypeTraits>(kTestVectorFloat32Invalid,
bus->frames());
ASSERT_EQ(bus->channel_span(0)[0], kTestVectorFloat32Invalid[0]);
std::array<float, std::size(kTestVectorFloat32Sanitized)> test_array;
bus->ToInterleaved<Float32SampleTypeTraits>(bus->frames(), test_array.data());
for (size_t i = 0; i < std::size(kTestVectorFloat32Sanitized); ++i)
UNSAFE_TODO(ASSERT_EQ(kTestVectorFloat32Sanitized[i], test_array[i]));
bus->ToInterleaved<Float32SampleTypeTraitsNoClip>(bus->frames(),
test_array.data());
for (int i = 0; i < kTestVectorSize; ++i) {
if (std::isnan(test_array[i]))
UNSAFE_TODO(EXPECT_TRUE(std::isnan(kTestVectorFloat32Invalid[i])));
else
UNSAFE_TODO(EXPECT_FLOAT_EQ(test_array[i], kTestVectorFloat32Invalid[i]));
}
}
TEST_F(AudioBusTest, CopyAndClipTo) {
auto bus = AudioBus::Create(kTestVectorChannelCount, kTestVectorFrameCount);
bus->FromInterleaved<Float32SampleTypeTraits>(kTestVectorFloat32Invalid,
bus->frames());
auto expected =
AudioBus::Create(kTestVectorChannelCount, kTestVectorFrameCount);
expected->FromInterleaved<Float32SampleTypeTraits>(
kTestVectorFloat32Sanitized, bus->frames());
ASSERT_EQ(bus->channel_span(0)[0], kTestVectorFloat32Invalid[0]);
std::unique_ptr<AudioBus> copy_to_bus =
AudioBus::Create(kTestVectorChannelCount, kTestVectorFrameCount);
bus->CopyAndClipTo(copy_to_bus.get());
for (int ch = 0; ch < expected->channels(); ++ch) {
for (int i = 0; i < expected->frames(); ++i)
ASSERT_EQ(copy_to_bus->channel_span(ch)[i],
expected->channel_span(ch)[i]);
}
ASSERT_EQ(expected->channels(), copy_to_bus->channels());
ASSERT_EQ(expected->frames(), copy_to_bus->frames());
ASSERT_EQ(expected->is_bitstream_format(),
copy_to_bus->is_bitstream_format());
}
TEST_F(AudioBusTest, ToInterleavedPartial) {
static constexpr size_t kPartialStart = 1;
static constexpr size_t kPartialFrames = 2;
ASSERT_LE(kPartialStart + kPartialFrames, kTestVectorFrameCount);
std::unique_ptr<AudioBus> expected =
AudioBus::Create(kTestVectorChannelCount, kTestVectorFrameCount);
for (int ch = 0; ch < kTestVectorChannelCount; ++ch) {
expected->channel_span(ch).copy_from(base::span(kTestVectorResult[ch]));
}
{
SCOPED_TRACE("Float32SampleTypeTraits");
float test_array[std::size(kTestVectorFloat32)];
expected->ToInterleavedPartial<Float32SampleTypeTraits>(
kPartialStart, kPartialFrames, test_array);
UNSAFE_TODO(ASSERT_EQ(
0, memcmp(test_array,
kTestVectorFloat32 + kPartialStart * kTestVectorChannelCount,
kPartialFrames * sizeof(*kTestVectorFloat32) *
kTestVectorChannelCount)));
}
}
struct ZeroingOutTestData {
static constexpr int kChannelCount = 2;
static constexpr int kFrameCount = 10;
static constexpr int kInterleavedFrameCount = 3;
std::unique_ptr<AudioBus> bus_under_test;
std::vector<float> interleaved_dummy_frames;
ZeroingOutTestData() {
bus_under_test = AudioBus::Create(kChannelCount, kFrameCount);
for (auto channel : bus_under_test->AllChannels()) {
int value = 1;
for (float& sample : channel) {
sample = value++;
}
}
static const float kDummySampleValue = 0.123f;
interleaved_dummy_frames.resize(kChannelCount * kInterleavedFrameCount);
std::fill(interleaved_dummy_frames.begin(), interleaved_dummy_frames.end(),
kDummySampleValue);
}
};
TEST_F(AudioBusTest, FromInterleavedZerosOutUntouchedFrames) {
ZeroingOutTestData test_data;
test_data.bus_under_test->FromInterleaved<Float32SampleTypeTraits>(
&test_data.interleaved_dummy_frames[0], test_data.kInterleavedFrameCount);
const size_t untouched_frame_count =
test_data.kFrameCount - test_data.kInterleavedFrameCount;
for (auto partial_channel : test_data.bus_under_test->AllChannelsSubspan(
test_data.kInterleavedFrameCount, untouched_frame_count)) {
for (auto sample : partial_channel) {
ASSERT_EQ(0.0f, sample);
}
}
}
TEST_F(AudioBusTest, FromInterleavedPartialDoesNotZeroOutUntouchedFrames) {
{
SCOPED_TRACE("Zero write offset");
ZeroingOutTestData test_data;
static const int kWriteOffsetInFrames = 0;
test_data.bus_under_test->FromInterleavedPartial<Float32SampleTypeTraits>(
&test_data.interleaved_dummy_frames[0], kWriteOffsetInFrames,
test_data.kInterleavedFrameCount);
for (int ch = 0; ch < test_data.kChannelCount; ++ch) {
auto sample_array_for_current_channel =
test_data.bus_under_test->channel_span(ch);
for (int frame_index =
test_data.kInterleavedFrameCount + kWriteOffsetInFrames;
frame_index < test_data.kFrameCount; frame_index++) {
ASSERT_EQ(frame_index + 1,
sample_array_for_current_channel[frame_index]);
}
}
}
{
SCOPED_TRACE("Positive write offset");
ZeroingOutTestData test_data;
static const int kWriteOffsetInFrames = 2;
test_data.bus_under_test->FromInterleavedPartial<Float32SampleTypeTraits>(
&test_data.interleaved_dummy_frames[0], kWriteOffsetInFrames,
test_data.kInterleavedFrameCount);
for (int ch = 0; ch < test_data.kChannelCount; ++ch) {
auto sample_array_for_current_channel =
test_data.bus_under_test->channel_span(ch);
for (int frame_index = 0; frame_index < kWriteOffsetInFrames;
frame_index++) {
ASSERT_EQ(frame_index + 1,
sample_array_for_current_channel[frame_index]);
}
for (int frame_index =
test_data.kInterleavedFrameCount + kWriteOffsetInFrames;
frame_index < test_data.kFrameCount; frame_index++) {
ASSERT_EQ(frame_index + 1,
sample_array_for_current_channel[frame_index]);
}
}
}
}
TEST_F(AudioBusTest, Scale) {
std::unique_ptr<AudioBus> bus = AudioBus::Create(kChannels, kFrameCount);
static constexpr float kFillValue = 1;
for (auto channel : bus->AllChannels()) {
std::ranges::fill(channel, kFillValue);
}
bus->Scale(-1);
for (auto channel : bus->AllChannels()) {
SCOPED_TRACE("Invalid Scale");
VerifySpanIsFilledWithValue(channel, kFillValue);
}
static constexpr float kVolume = 0.5;
bus->Scale(kVolume);
for (auto channel : bus->AllChannels()) {
SCOPED_TRACE("Half Scale");
VerifySpanIsFilledWithValue(channel, kFillValue * kVolume);
}
bus->Scale(0);
for (auto channel : bus->AllChannels()) {
SCOPED_TRACE("Zero Scale");
VerifySpanIsFilledWithValue(channel, 0);
}
}
TEST_F(AudioBusTest, Bitstream) {
static const size_t kDataSize = kFrameCount / 2;
std::unique_ptr<AudioBus> bus = AudioBus::Create(1, kFrameCount);
EXPECT_FALSE(bus->is_bitstream_format());
bus->set_is_bitstream_format(true);
EXPECT_TRUE(bus->is_bitstream_format());
EXPECT_EQ(0u, bus->bitstream_data().size());
bus->SetBitstreamSize(kDataSize);
EXPECT_EQ(kDataSize, bus->bitstream_data().size());
EXPECT_EQ(0, bus->GetBitstreamFrames());
bus->SetBitstreamFrames(kFrameCount);
EXPECT_EQ(static_cast<int>(kFrameCount), bus->GetBitstreamFrames());
std::unique_ptr<AudioBus> bus2 = AudioBus::Create(1, kFrameCount);
CopyTest(bus.get(), bus2.get());
bus->Zero();
EXPECT_EQ(0u, bus->bitstream_data().size());
EXPECT_EQ(0, bus->GetBitstreamFrames());
}
}