blob: 156fb397b134b9a95dff907c3c5bda630d9d938f [file] [log] [blame]
/*
* Copyright 2024 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "api/audio/audio_view.h"
#include <array>
#include "test/gtest.h"
namespace webrtc {
namespace {
constexpr const float kFloatStepIncrease = 0.5f;
constexpr const int16_t kIntStepIncrease = 1;
template <typename T>
void Increment(float& t) {
t += kFloatStepIncrease;
}
template <typename T>
void Increment(int16_t& t) {
t += kIntStepIncrease;
}
// Fills a given buffer with monotonically increasing values.
template <typename T>
void FillBuffer(rtc::ArrayView<T> buffer) {
T value = {};
for (T& t : buffer) {
Increment<T>(value);
t = value;
}
}
} // namespace
TEST(AudioViewTest, MonoView) {
const size_t kArraySize = 100u;
int16_t arr[kArraySize];
FillBuffer(rtc::ArrayView<int16_t>(arr));
MonoView<int16_t> mono(arr);
MonoView<const int16_t> const_mono(arr);
EXPECT_EQ(mono.size(), kArraySize);
EXPECT_EQ(const_mono.size(), kArraySize);
EXPECT_EQ(&mono[0], &const_mono[0]);
EXPECT_EQ(mono[0], arr[0]);
EXPECT_EQ(1u, NumChannels(mono));
EXPECT_EQ(1u, NumChannels(const_mono));
EXPECT_EQ(100u, SamplesPerChannel(mono));
EXPECT_TRUE(IsMono(mono));
EXPECT_TRUE(IsMono(const_mono));
}
TEST(AudioViewTest, InterleavedView) {
const size_t kArraySize = 100u;
int16_t arr[kArraySize];
FillBuffer(rtc::ArrayView<int16_t>(arr));
InterleavedView<int16_t> interleaved(arr, kArraySize, 1);
EXPECT_EQ(NumChannels(interleaved), 1u);
EXPECT_TRUE(IsMono(interleaved));
EXPECT_EQ(SamplesPerChannel(interleaved), kArraySize);
EXPECT_EQ(interleaved.AsMono().size(), kArraySize);
EXPECT_EQ(&interleaved.AsMono()[0], &arr[0]);
EXPECT_EQ(interleaved.AsMono(), interleaved.data());
// Basic iterator test.
int i = 0;
for (auto s : interleaved) {
EXPECT_EQ(s, arr[i++]);
}
interleaved = InterleavedView<int16_t>(arr, kArraySize / 2, 2);
InterleavedView<const int16_t> const_interleaved(arr, 50, 2);
EXPECT_EQ(NumChannels(interleaved), 2u);
EXPECT_EQ(NumChannels(const_interleaved), 2u);
EXPECT_EQ(&const_interleaved[0], &interleaved[0]);
EXPECT_TRUE(!IsMono(interleaved));
EXPECT_TRUE(!IsMono(const_interleaved));
EXPECT_EQ(SamplesPerChannel(interleaved), 50u);
EXPECT_EQ(SamplesPerChannel(const_interleaved), 50u);
interleaved = InterleavedView<int16_t>(arr, 4);
EXPECT_EQ(NumChannels(interleaved), 4u);
InterleavedView<const int16_t> const_interleaved2(interleaved);
EXPECT_EQ(NumChannels(const_interleaved2), 4u);
EXPECT_EQ(SamplesPerChannel(interleaved), 25u);
const_interleaved2 = interleaved;
EXPECT_EQ(NumChannels(const_interleaved2), 4u);
EXPECT_EQ(&const_interleaved2[0], &interleaved[0]);
}
TEST(AudioViewTest, DeinterleavedView) {
const size_t kArraySize = 100u;
int16_t arr[kArraySize] = {};
DeinterleavedView<int16_t> di(arr, 10, 10);
DeinterleavedView<const int16_t> const_di(arr, 10, 10);
EXPECT_EQ(NumChannels(di), 10u);
EXPECT_EQ(SamplesPerChannel(di), 10u);
EXPECT_TRUE(!IsMono(di));
EXPECT_EQ(const_di[5][1], di[5][1]); // Spot check.
// For deinterleaved views, although they may hold multiple channels,
// the AsMono() method is still available and returns the first channel
// in the view.
auto mono_ch = di.AsMono();
EXPECT_EQ(NumChannels(mono_ch), 1u);
EXPECT_EQ(SamplesPerChannel(mono_ch), 10u);
EXPECT_EQ(di[0], mono_ch); // first channel should be same as mono.
di = DeinterleavedView<int16_t>(arr, 50, 2);
// Test assignment.
const_di = di;
EXPECT_EQ(&di.AsMono()[0], &const_di.AsMono()[0]);
// Access the second channel in the deinterleaved view.
// The start of the second channel should be directly after the first channel.
// The memory width of each channel is held by the `stride()` member which
// by default is the same value as samples per channel.
mono_ch = di[1];
EXPECT_EQ(SamplesPerChannel(mono_ch), 50u);
EXPECT_EQ(&mono_ch[0], &arr[di.samples_per_channel()]);
}
TEST(AudioViewTest, CopySamples) {
const size_t kArraySize = 100u;
int16_t source_arr[kArraySize] = {};
int16_t dest_arr[kArraySize] = {};
FillBuffer(rtc::ArrayView<int16_t>(source_arr));
InterleavedView<const int16_t> source(source_arr, 2);
InterleavedView<int16_t> destination(dest_arr, 2);
static_assert(IsInterleavedView(source) == IsInterleavedView(destination),
"");
// Values in `dest_arr` should all be 0, none of the values in `source_arr`
// should be 0.
for (size_t i = 0; i < kArraySize; ++i) {
ASSERT_EQ(dest_arr[i], 0);
ASSERT_NE(source_arr[i], 0);
}
CopySamples(destination, source);
for (size_t i = 0; i < kArraySize; ++i) {
ASSERT_EQ(dest_arr[i], source_arr[i]) << "i == " << i;
}
}
TEST(AudioViewTest, ClearSamples) {
std::array<int16_t, 100u> samples = {};
FillBuffer(rtc::ArrayView<int16_t>(samples));
ASSERT_NE(samples[0], 0);
ClearSamples(samples);
for (const auto s : samples) {
ASSERT_EQ(s, 0);
}
std::array<float, 100u> samples_f = {};
FillBuffer(rtc::ArrayView<float>(samples_f));
ASSERT_NE(samples_f[0], 0.0);
ClearSamples(samples_f);
for (const auto s : samples_f) {
ASSERT_EQ(s, 0.0);
}
// Clear only half of the buffer
FillBuffer(rtc::ArrayView<int16_t>(samples));
const auto half_way = samples.size() / 2;
ClearSamples(samples, half_way);
for (size_t i = 0u; i < samples.size(); ++i) {
if (i < half_way) {
ASSERT_EQ(samples[i], 0);
} else {
ASSERT_NE(samples[i], 0);
}
}
}
} // namespace webrtc