|  | /* | 
|  | *  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 <cstddef> | 
|  | #include <cstdint> | 
|  | #include <vector> | 
|  |  | 
|  | #include "api/array_view.h" | 
|  | #include "rtc_base/arraysize.h" | 
|  | #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(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(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(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(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(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(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(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); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(AudioViewTest, DeinterleavedViewPointerArray) { | 
|  | // Create vectors of varying sizes to guarantee that they don't end up | 
|  | // aligned in memory. | 
|  | std::vector<float> v1(100), v2(200), v3(300), v4(400); | 
|  | std::vector<float*> channels = {&v1[0], &v2[0], &v3[0], &v4[0]}; | 
|  |  | 
|  | DeinterleavedView<float> di(channels, v1.size()); | 
|  | EXPECT_EQ(NumChannels(di), channels.size()); | 
|  | EXPECT_EQ(SamplesPerChannel(di), v1.size()); | 
|  | EXPECT_EQ(di[0].data(), v1.data()); | 
|  | EXPECT_EQ(di[1].data(), v2.data()); | 
|  | EXPECT_EQ(di[2].data(), v3.data()); | 
|  | EXPECT_EQ(di[3].data(), v4.data()); | 
|  |  | 
|  | // Test that the same thing works with T* const *. | 
|  | float* channel_array[] = {&v1[0], &v2[0], &v3[0], &v4[0]}; | 
|  | di = DeinterleavedView<float>(channel_array, v1.size(), | 
|  | arraysize(channel_array)); | 
|  | EXPECT_EQ(NumChannels(di), channels.size()); | 
|  | EXPECT_EQ(SamplesPerChannel(di), v1.size()); | 
|  | EXPECT_EQ(di[0].data(), v1.data()); | 
|  | EXPECT_EQ(di[1].data(), v2.data()); | 
|  | EXPECT_EQ(di[2].data(), v3.data()); | 
|  | EXPECT_EQ(di[3].data(), v4.data()); | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |