| /* | |
| * Copyright (c) 2012 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 "modules/audio_coding/neteq/audio_multi_vector.h" | |
| #include <cstdint> | |
| #include <cstdlib> | |
| #include <cstring> | |
| #include <memory> | |
| #include <vector> | |
| #include "api/audio/audio_view.h" | |
| #include "modules/audio_coding/neteq/audio_vector.h" | |
| #include "rtc_base/numerics/safe_conversions.h" | |
| #include "test/gtest.h" | |
| namespace webrtc { | |
| // This is a value-parameterized test. The test cases are instantiated with | |
| // different values for the test parameter, which is used to determine the | |
| // number of channels in the AudioMultiBuffer. Note that it is not possible | |
| // to combine typed testing with value-parameterized testing, and since the | |
| // tests for AudioVector already covers a number of different type parameters, | |
| // this test focuses on testing different number of channels, and keeping the | |
| // value type constant. | |
| class AudioMultiVectorTest : public ::testing::TestWithParam<size_t> { | |
| protected: | |
| AudioMultiVectorTest() | |
| : num_channels_(GetParam()), // Get the test parameter. | |
| array_interleaved_(num_channels_ * array_length()) {} | |
| ~AudioMultiVectorTest() override = default; | |
| void SetUp() override { | |
| // Populate test arrays. | |
| for (size_t i = 0; i < array_length(); ++i) { | |
| array_[i] = static_cast<int16_t>(i); | |
| } | |
| int16_t* ptr = array_interleaved_.data(); | |
| // Write 100, 101, 102, ... for first channel. | |
| // Write 200, 201, 202, ... for second channel. | |
| // And so on. | |
| for (size_t i = 0; i < array_length(); ++i) { | |
| for (size_t j = 1; j <= num_channels_; ++j) { | |
| *ptr = checked_cast<int16_t>(j * 100 + i); | |
| ++ptr; | |
| } | |
| } | |
| } | |
| size_t array_length() const { return sizeof(array_) / sizeof(array_[0]); } | |
| const size_t num_channels_; | |
| int16_t array_[10]; | |
| std::vector<int16_t> array_interleaved_; | |
| }; | |
| // Create and destroy AudioMultiVector objects, both empty and with a predefined | |
| // length. | |
| TEST_P(AudioMultiVectorTest, CreateAndDestroy) { | |
| AudioMultiVector vec1(num_channels_); | |
| EXPECT_TRUE(vec1.Empty()); | |
| EXPECT_EQ(num_channels_, vec1.Channels()); | |
| EXPECT_EQ(0u, vec1.Size()); | |
| size_t initial_size = 17; | |
| AudioMultiVector vec2(num_channels_, initial_size); | |
| EXPECT_FALSE(vec2.Empty()); | |
| EXPECT_EQ(num_channels_, vec2.Channels()); | |
| EXPECT_EQ(initial_size, vec2.Size()); | |
| } | |
| // Test the subscript operator [] for getting and setting. | |
| TEST_P(AudioMultiVectorTest, SubscriptOperator) { | |
| AudioMultiVector vec(num_channels_, array_length()); | |
| for (size_t channel = 0; channel < num_channels_; ++channel) { | |
| for (size_t i = 0; i < array_length(); ++i) { | |
| vec[channel][i] = static_cast<int16_t>(i); | |
| // Make sure to use the const version. | |
| const AudioVector& audio_vec = vec[channel]; | |
| EXPECT_EQ(static_cast<int16_t>(i), audio_vec[i]); | |
| } | |
| } | |
| } | |
| // Test the PushBackInterleaved method and the CopyFrom method. The Clear | |
| // method is also invoked. | |
| TEST_P(AudioMultiVectorTest, PushBackInterleavedAndCopy) { | |
| AudioMultiVector vec(num_channels_); | |
| vec.PushBackInterleaved(array_interleaved_); | |
| AudioMultiVector vec_copy(num_channels_); | |
| vec.CopyTo(&vec_copy); // Copy from `vec` to `vec_copy`. | |
| ASSERT_EQ(num_channels_, vec.Channels()); | |
| ASSERT_EQ(array_length(), vec.Size()); | |
| ASSERT_EQ(num_channels_, vec_copy.Channels()); | |
| ASSERT_EQ(array_length(), vec_copy.Size()); | |
| for (size_t channel = 0; channel < vec.Channels(); ++channel) { | |
| for (size_t i = 0; i < array_length(); ++i) { | |
| EXPECT_EQ(static_cast<int16_t>((channel + 1) * 100 + i), vec[channel][i]); | |
| EXPECT_EQ(vec[channel][i], vec_copy[channel][i]); | |
| } | |
| } | |
| // Clear `vec` and verify that it is empty. | |
| vec.Clear(); | |
| EXPECT_TRUE(vec.Empty()); | |
| // Now copy the empty vector and verify that the copy becomes empty too. | |
| vec.CopyTo(&vec_copy); | |
| EXPECT_TRUE(vec_copy.Empty()); | |
| } | |
| // Try to copy to a NULL pointer. Nothing should happen. | |
| TEST_P(AudioMultiVectorTest, CopyToNull) { | |
| AudioMultiVector vec(num_channels_); | |
| AudioMultiVector* vec_copy = nullptr; | |
| vec.PushBackInterleaved(array_interleaved_); | |
| vec.CopyTo(vec_copy); | |
| } | |
| // Test the PushBack method with another AudioMultiVector as input argument. | |
| TEST_P(AudioMultiVectorTest, PushBackVector) { | |
| AudioMultiVector vec1(num_channels_, array_length()); | |
| AudioMultiVector vec2(num_channels_, array_length()); | |
| // Set the first vector to [0, 1, ..., array_length() - 1] + | |
| // 100 * channel_number. | |
| // Set the second vector to [array_length(), array_length() + 1, ..., | |
| // 2 * array_length() - 1] + 100 * channel_number. | |
| for (size_t channel = 0; channel < num_channels_; ++channel) { | |
| for (size_t i = 0; i < array_length(); ++i) { | |
| vec1[channel][i] = static_cast<int16_t>(i + 100 * channel); | |
| vec2[channel][i] = | |
| static_cast<int16_t>(i + 100 * channel + array_length()); | |
| } | |
| } | |
| // Append vec2 to the back of vec1. | |
| vec1.PushBack(vec2); | |
| ASSERT_EQ(2u * array_length(), vec1.Size()); | |
| for (size_t channel = 0; channel < num_channels_; ++channel) { | |
| for (size_t i = 0; i < 2 * array_length(); ++i) { | |
| EXPECT_EQ(static_cast<int16_t>(i + 100 * channel), vec1[channel][i]); | |
| } | |
| } | |
| } | |
| // Test the PushBackFromIndex method. | |
| TEST_P(AudioMultiVectorTest, PushBackFromIndex) { | |
| AudioMultiVector vec1(num_channels_); | |
| vec1.PushBackInterleaved(array_interleaved_); | |
| AudioMultiVector vec2(num_channels_); | |
| // Append vec1 to the back of vec2 (which is empty). Read vec1 from the second | |
| // last element. | |
| vec2.PushBackFromIndex(vec1, array_length() - 2); | |
| ASSERT_EQ(2u, vec2.Size()); | |
| for (size_t channel = 0; channel < num_channels_; ++channel) { | |
| for (size_t i = 0; i < 2; ++i) { | |
| EXPECT_EQ(array_interleaved_[channel + | |
| num_channels_ * (array_length() - 2 + i)], | |
| vec2[channel][i]); | |
| } | |
| } | |
| } | |
| // Starts with pushing some values to the vector, then test the Zeros method. | |
| TEST_P(AudioMultiVectorTest, Zeros) { | |
| AudioMultiVector vec(num_channels_); | |
| vec.PushBackInterleaved(array_interleaved_); | |
| vec.Zeros(2 * array_length()); | |
| ASSERT_EQ(num_channels_, vec.Channels()); | |
| ASSERT_EQ(2u * array_length(), vec.Size()); | |
| for (size_t channel = 0; channel < num_channels_; ++channel) { | |
| for (size_t i = 0; i < 2 * array_length(); ++i) { | |
| EXPECT_EQ(0, vec[channel][i]); | |
| } | |
| } | |
| } | |
| // Test the ReadInterleaved method | |
| TEST_P(AudioMultiVectorTest, ReadInterleaved) { | |
| AudioMultiVector vec(num_channels_); | |
| vec.PushBackInterleaved(array_interleaved_); | |
| std::unique_ptr<int16_t[]> output(new int16_t[array_interleaved_.size()]); | |
| // Read 5 samples. | |
| size_t read_samples = 5; | |
| EXPECT_EQ(num_channels_ * read_samples, | |
| vec.ReadInterleaved(read_samples, output.get())); | |
| EXPECT_EQ(0, memcmp(array_interleaved_.data(), output.get(), | |
| read_samples * sizeof(int16_t))); | |
| // Read too many samples. Expect to get all samples from the vector. | |
| EXPECT_EQ(array_interleaved_.size(), | |
| vec.ReadInterleaved(array_length() + 1, output.get())); | |
| EXPECT_EQ(0, memcmp(array_interleaved_.data(), output.get(), | |
| read_samples * sizeof(int16_t))); | |
| } | |
| TEST_P(AudioMultiVectorTest, ReadInterleavedView) { | |
| AudioMultiVector vec(num_channels_); | |
| vec.PushBackInterleaved(array_interleaved_); | |
| // Read 5 samples. | |
| size_t samples_per_channel = 5; | |
| ASSERT_GT(array_length(), samples_per_channel); | |
| std::unique_ptr<int16_t[]> buffer(new int16_t[array_interleaved_.size()]); | |
| InterleavedView<int16_t> view(buffer.get(), samples_per_channel, | |
| num_channels_); | |
| EXPECT_TRUE(vec.ReadInterleavedFromIndex(0u, view)); | |
| EXPECT_EQ(0, memcmp(array_interleaved_.data(), &view[0], | |
| view.size() * sizeof(int16_t))); | |
| // Trying to read too much should result in failure. | |
| // Attempt to read 5 samples when only 4 can be read. | |
| EXPECT_FALSE(vec.ReadInterleavedFromIndex(vec.Size() - 4u, view)); | |
| } | |
| // Test the PopFront method. | |
| TEST_P(AudioMultiVectorTest, PopFront) { | |
| AudioMultiVector vec(num_channels_); | |
| vec.PushBackInterleaved(array_interleaved_); | |
| vec.PopFront(1); // Remove one element from each channel. | |
| ASSERT_EQ(array_length() - 1u, vec.Size()); | |
| // Let `ptr` point to the second element of the first channel in the | |
| // interleaved array. | |
| int16_t* ptr = &array_interleaved_[num_channels_]; | |
| for (size_t i = 0; i < array_length() - 1; ++i) { | |
| for (size_t channel = 0; channel < num_channels_; ++channel) { | |
| EXPECT_EQ(*ptr, vec[channel][i]); | |
| ++ptr; | |
| } | |
| } | |
| vec.PopFront(array_length()); // Remove more elements than vector size. | |
| EXPECT_EQ(0u, vec.Size()); | |
| } | |
| // Test the PopBack method. | |
| TEST_P(AudioMultiVectorTest, PopBack) { | |
| AudioMultiVector vec(num_channels_); | |
| vec.PushBackInterleaved(array_interleaved_); | |
| vec.PopBack(1); // Remove one element from each channel. | |
| ASSERT_EQ(array_length() - 1u, vec.Size()); | |
| // Let `ptr` point to the first element of the first channel in the | |
| // interleaved array. | |
| int16_t* ptr = array_interleaved_.data(); | |
| for (size_t i = 0; i < array_length() - 1; ++i) { | |
| for (size_t channel = 0; channel < num_channels_; ++channel) { | |
| EXPECT_EQ(*ptr, vec[channel][i]); | |
| ++ptr; | |
| } | |
| } | |
| vec.PopBack(array_length()); // Remove more elements than vector size. | |
| EXPECT_EQ(0u, vec.Size()); | |
| } | |
| // Test the AssertSize method. | |
| TEST_P(AudioMultiVectorTest, AssertSize) { | |
| AudioMultiVector vec(num_channels_, array_length()); | |
| EXPECT_EQ(array_length(), vec.Size()); | |
| // Start with asserting with smaller sizes than already allocated. | |
| vec.AssertSize(0); | |
| vec.AssertSize(array_length() - 1); | |
| // Nothing should have changed. | |
| EXPECT_EQ(array_length(), vec.Size()); | |
| // Assert with one element longer than already allocated. | |
| vec.AssertSize(array_length() + 1); | |
| // Expect vector to have grown. | |
| EXPECT_EQ(array_length() + 1, vec.Size()); | |
| // Also check the individual AudioVectors. | |
| for (size_t channel = 0; channel < vec.Channels(); ++channel) { | |
| EXPECT_EQ(array_length() + 1u, vec[channel].Size()); | |
| } | |
| } | |
| // Test the PushBack method with another AudioMultiVector as input argument. | |
| TEST_P(AudioMultiVectorTest, OverwriteAt) { | |
| AudioMultiVector vec1(num_channels_); | |
| vec1.PushBackInterleaved(array_interleaved_); | |
| AudioMultiVector vec2(num_channels_); | |
| vec2.Zeros(3); // 3 zeros in each channel. | |
| // Overwrite vec2 at position 5. | |
| vec1.OverwriteAt(vec2, 3, 5); | |
| // Verify result. | |
| // Length remains the same. | |
| ASSERT_EQ(array_length(), vec1.Size()); | |
| int16_t* ptr = array_interleaved_.data(); | |
| for (size_t i = 0; i < array_length() - 1; ++i) { | |
| for (size_t channel = 0; channel < num_channels_; ++channel) { | |
| if (i >= 5 && i <= 7) { | |
| // Elements 5, 6, 7 should have been replaced with zeros. | |
| EXPECT_EQ(0, vec1[channel][i]); | |
| } else { | |
| EXPECT_EQ(*ptr, vec1[channel][i]); | |
| } | |
| ++ptr; | |
| } | |
| } | |
| } | |
| // Test the CopyChannel method, when the test is instantiated with at least two | |
| // channels. | |
| TEST_P(AudioMultiVectorTest, CopyChannel) { | |
| if (num_channels_ < 2) | |
| return; | |
| AudioMultiVector vec(num_channels_); | |
| vec.PushBackInterleaved(array_interleaved_); | |
| // Create a reference copy. | |
| AudioMultiVector ref(num_channels_); | |
| ref.PushBack(vec); | |
| // Copy from first to last channel. | |
| vec.CopyChannel(0, num_channels_ - 1); | |
| // Verify that the first and last channels are identical; the others should | |
| // be left untouched. | |
| for (size_t i = 0; i < array_length(); ++i) { | |
| // Verify that all but the last channel are untouched. | |
| for (size_t channel = 0; channel < num_channels_ - 1; ++channel) { | |
| EXPECT_EQ(ref[channel][i], vec[channel][i]); | |
| } | |
| // Verify that the last and the first channels are identical. | |
| EXPECT_EQ(vec[0][i], vec[num_channels_ - 1][i]); | |
| } | |
| } | |
| TEST_P(AudioMultiVectorTest, PushBackEmptyArray) { | |
| AudioMultiVector vec(num_channels_); | |
| vec.PushBackInterleaved({}); | |
| EXPECT_TRUE(vec.Empty()); | |
| } | |
| INSTANTIATE_TEST_SUITE_P(TestNumChannels, | |
| AudioMultiVectorTest, | |
| ::testing::Values(static_cast<size_t>(1), | |
| static_cast<size_t>(2), | |
| static_cast<size_t>(5))); | |
| } // namespace webrtc |