| /* | |
| * 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_vector.h" | |
| #include <array> | |
| #include <cstdint> | |
| #include <cstdlib> | |
| #include "api/audio/audio_view.h" | |
| #include "rtc_base/numerics/safe_conversions.h" | |
| #include "test/gtest.h" | |
| namespace webrtc { | |
| class AudioVectorTest : public ::testing::Test { | |
| protected: | |
| void SetUp() override { | |
| // Populate test array. | |
| for (size_t i = 0; i < array_length(); ++i) { | |
| array_[i] = checked_cast<int16_t>(i); | |
| } | |
| } | |
| constexpr size_t array_length() const { | |
| return sizeof(array_) / sizeof(array_[0]); | |
| } | |
| int16_t array_[10]; | |
| }; | |
| // Create and destroy AudioVector objects, both empty and with a predefined | |
| // length. | |
| TEST_F(AudioVectorTest, CreateAndDestroy) { | |
| AudioVector vec1; | |
| EXPECT_TRUE(vec1.Empty()); | |
| EXPECT_EQ(0u, vec1.Size()); | |
| size_t initial_size = 17; | |
| AudioVector vec2(initial_size); | |
| EXPECT_FALSE(vec2.Empty()); | |
| EXPECT_EQ(initial_size, vec2.Size()); | |
| } | |
| // Test the subscript operator [] for getting and setting. | |
| TEST_F(AudioVectorTest, SubscriptOperator) { | |
| AudioVector vec(array_length()); | |
| for (size_t i = 0; i < array_length(); ++i) { | |
| vec[i] = static_cast<int16_t>(i); | |
| const int16_t& value = vec[i]; // Make sure to use the const version. | |
| EXPECT_EQ(static_cast<int16_t>(i), value); | |
| } | |
| } | |
| // Test the PushBack method and the CopyFrom method. The Clear method is also | |
| // invoked. | |
| TEST_F(AudioVectorTest, PushBackAndCopy) { | |
| AudioVector vec; | |
| AudioVector vec_copy; | |
| vec.PushBack(array_, array_length()); | |
| vec.CopyTo(&vec_copy); // Copy from `vec` to `vec_copy`. | |
| ASSERT_EQ(array_length(), vec.Size()); | |
| ASSERT_EQ(array_length(), vec_copy.Size()); | |
| for (size_t i = 0; i < array_length(); ++i) { | |
| EXPECT_EQ(array_[i], vec[i]); | |
| EXPECT_EQ(array_[i], vec_copy[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()); | |
| } | |
| TEST_F(AudioVectorTest, CopyTo) { | |
| AudioVector vec; | |
| vec.PushBack(array_, array_length()); | |
| ASSERT_EQ(vec.Size(), 10u); | |
| std::array<int16_t, 10> buffer; | |
| MonoView<int16_t> view(&buffer[0], buffer.size()); | |
| // Try to read 10 samples from position 1, which should fail. | |
| EXPECT_FALSE(vec.CopyTo(1, view)); | |
| // Reading 10 samples from position 0, should succeed. | |
| EXPECT_TRUE(vec.CopyTo(0, view)); | |
| EXPECT_EQ(view[5], 5); // sanity check. | |
| // Free up space at the front of the buffer. This changes | |
| // the internal start position without reallocating memory. | |
| vec.PopFront(2); | |
| std::array<int16_t, 2> new_values = {20, 21}; | |
| vec.PushBack(&new_values[0], new_values.size()); | |
| // Now the CopyTo() operation will need to wrap around | |
| // the end of the internal buffer to fill the view. | |
| EXPECT_TRUE(vec.CopyTo(0, view)); | |
| EXPECT_EQ(view[0], 2); | |
| EXPECT_EQ(view[8], 20); | |
| EXPECT_EQ(view[9], 21); | |
| } | |
| // Test the PushBack method with another AudioVector as input argument. | |
| TEST_F(AudioVectorTest, PushBackVector) { | |
| static const size_t kLength = 10; | |
| AudioVector vec1(kLength); | |
| AudioVector vec2(kLength); | |
| // Set the first vector to [0, 1, ..., kLength - 1]. | |
| // Set the second vector to [kLength, kLength + 1, ..., 2 * kLength - 1]. | |
| for (size_t i = 0; i < kLength; ++i) { | |
| vec1[i] = static_cast<int16_t>(i); | |
| vec2[i] = static_cast<int16_t>(i + kLength); | |
| } | |
| // Append vec2 to the back of vec1. | |
| vec1.PushBack(vec2); | |
| ASSERT_EQ(2 * kLength, vec1.Size()); | |
| for (size_t i = 0; i < 2 * kLength; ++i) { | |
| EXPECT_EQ(static_cast<int16_t>(i), vec1[i]); | |
| } | |
| } | |
| // Test the PushFront method. | |
| TEST_F(AudioVectorTest, PushFront) { | |
| AudioVector vec; | |
| vec.PushFront(array_, array_length()); | |
| ASSERT_EQ(array_length(), vec.Size()); | |
| for (size_t i = 0; i < array_length(); ++i) { | |
| EXPECT_EQ(array_[i], vec[i]); | |
| } | |
| } | |
| // Test the PushFront method with another AudioVector as input argument. | |
| TEST_F(AudioVectorTest, PushFrontVector) { | |
| static const size_t kLength = 10; | |
| AudioVector vec1(kLength); | |
| AudioVector vec2(kLength); | |
| // Set the first vector to [0, 1, ..., kLength - 1]. | |
| // Set the second vector to [kLength, kLength + 1, ..., 2 * kLength - 1]. | |
| for (size_t i = 0; i < kLength; ++i) { | |
| vec1[i] = static_cast<int16_t>(i); | |
| vec2[i] = static_cast<int16_t>(i + kLength); | |
| } | |
| // Prepend vec1 to the front of vec2. | |
| vec2.PushFront(vec1); | |
| ASSERT_EQ(2 * kLength, vec2.Size()); | |
| for (size_t i = 0; i < 2 * kLength; ++i) { | |
| EXPECT_EQ(static_cast<int16_t>(i), vec2[i]); | |
| } | |
| } | |
| // Test the PopFront method. | |
| TEST_F(AudioVectorTest, PopFront) { | |
| AudioVector vec; | |
| vec.PushBack(array_, array_length()); | |
| vec.PopFront(1); // Remove one element. | |
| EXPECT_EQ(array_length() - 1u, vec.Size()); | |
| for (size_t i = 0; i < array_length() - 1; ++i) { | |
| EXPECT_EQ(static_cast<int16_t>(i + 1), vec[i]); | |
| } | |
| vec.PopFront(array_length()); // Remove more elements than vector size. | |
| EXPECT_EQ(0u, vec.Size()); | |
| } | |
| // Test the PopBack method. | |
| TEST_F(AudioVectorTest, PopBack) { | |
| AudioVector vec; | |
| vec.PushBack(array_, array_length()); | |
| vec.PopBack(1); // Remove one element. | |
| EXPECT_EQ(array_length() - 1u, vec.Size()); | |
| for (size_t i = 0; i < array_length() - 1; ++i) { | |
| EXPECT_EQ(static_cast<int16_t>(i), vec[i]); | |
| } | |
| vec.PopBack(array_length()); // Remove more elements than vector size. | |
| EXPECT_EQ(0u, vec.Size()); | |
| } | |
| // Test the Extend method. | |
| TEST_F(AudioVectorTest, Extend) { | |
| AudioVector vec; | |
| vec.PushBack(array_, array_length()); | |
| vec.Extend(5); // Extend with 5 elements, which should all be zeros. | |
| ASSERT_EQ(array_length() + 5u, vec.Size()); | |
| // Verify that all are zero. | |
| for (size_t i = array_length(); i < array_length() + 5; ++i) { | |
| EXPECT_EQ(0, vec[i]); | |
| } | |
| } | |
| // Test the InsertAt method with an insert position in the middle of the vector. | |
| TEST_F(AudioVectorTest, InsertAt) { | |
| AudioVector vec; | |
| vec.PushBack(array_, array_length()); | |
| static const int kNewLength = 5; | |
| int16_t new_array[kNewLength]; | |
| // Set array elements to {100, 101, 102, ... }. | |
| for (int i = 0; i < kNewLength; ++i) { | |
| new_array[i] = 100 + i; | |
| } | |
| int insert_position = 5; | |
| vec.InsertAt(new_array, kNewLength, insert_position); | |
| // Verify that the vector looks as follows: | |
| // {0, 1, ..., `insert_position` - 1, 100, 101, ..., 100 + kNewLength - 1, | |
| // `insert_position`, `insert_position` + 1, ..., kLength - 1}. | |
| size_t pos = 0; | |
| for (int i = 0; i < insert_position; ++i) { | |
| EXPECT_EQ(array_[i], vec[pos]); | |
| ++pos; | |
| } | |
| for (int i = 0; i < kNewLength; ++i) { | |
| EXPECT_EQ(new_array[i], vec[pos]); | |
| ++pos; | |
| } | |
| for (size_t i = insert_position; i < array_length(); ++i) { | |
| EXPECT_EQ(array_[i], vec[pos]); | |
| ++pos; | |
| } | |
| } | |
| // Test the InsertZerosAt method with an insert position in the middle of the | |
| // vector. Use the InsertAt method as reference. | |
| TEST_F(AudioVectorTest, InsertZerosAt) { | |
| AudioVector vec; | |
| AudioVector vec_ref; | |
| vec.PushBack(array_, array_length()); | |
| vec_ref.PushBack(array_, array_length()); | |
| static const int kNewLength = 5; | |
| int insert_position = 5; | |
| vec.InsertZerosAt(kNewLength, insert_position); | |
| int16_t new_array[kNewLength] = {0}; // All zero elements. | |
| vec_ref.InsertAt(new_array, kNewLength, insert_position); | |
| // Verify that the vectors are identical. | |
| ASSERT_EQ(vec_ref.Size(), vec.Size()); | |
| for (size_t i = 0; i < vec.Size(); ++i) { | |
| EXPECT_EQ(vec_ref[i], vec[i]); | |
| } | |
| } | |
| // Test the InsertAt method with an insert position at the start of the vector. | |
| TEST_F(AudioVectorTest, InsertAtBeginning) { | |
| AudioVector vec; | |
| vec.PushBack(array_, array_length()); | |
| static const int kNewLength = 5; | |
| int16_t new_array[kNewLength]; | |
| // Set array elements to {100, 101, 102, ... }. | |
| for (int i = 0; i < kNewLength; ++i) { | |
| new_array[i] = 100 + i; | |
| } | |
| int insert_position = 0; | |
| vec.InsertAt(new_array, kNewLength, insert_position); | |
| // Verify that the vector looks as follows: | |
| // {100, 101, ..., 100 + kNewLength - 1, | |
| // 0, 1, ..., kLength - 1}. | |
| size_t pos = 0; | |
| for (int i = 0; i < kNewLength; ++i) { | |
| EXPECT_EQ(new_array[i], vec[pos]); | |
| ++pos; | |
| } | |
| for (size_t i = insert_position; i < array_length(); ++i) { | |
| EXPECT_EQ(array_[i], vec[pos]); | |
| ++pos; | |
| } | |
| } | |
| // Test the InsertAt method with an insert position at the end of the vector. | |
| TEST_F(AudioVectorTest, InsertAtEnd) { | |
| AudioVector vec; | |
| vec.PushBack(array_, array_length()); | |
| static const int kNewLength = 5; | |
| int16_t new_array[kNewLength]; | |
| // Set array elements to {100, 101, 102, ... }. | |
| for (int i = 0; i < kNewLength; ++i) { | |
| new_array[i] = 100 + i; | |
| } | |
| int insert_position = checked_cast<int>(array_length()); | |
| vec.InsertAt(new_array, kNewLength, insert_position); | |
| // Verify that the vector looks as follows: | |
| // {0, 1, ..., kLength - 1, 100, 101, ..., 100 + kNewLength - 1 }. | |
| size_t pos = 0; | |
| for (size_t i = 0; i < array_length(); ++i) { | |
| EXPECT_EQ(array_[i], vec[pos]); | |
| ++pos; | |
| } | |
| for (int i = 0; i < kNewLength; ++i) { | |
| EXPECT_EQ(new_array[i], vec[pos]); | |
| ++pos; | |
| } | |
| } | |
| // Test the InsertAt method with an insert position beyond the end of the | |
| // vector. Verify that a position beyond the end of the vector does not lead to | |
| // an error. The expected outcome is the same as if the vector end was used as | |
| // input position. That is, the input position should be capped at the maximum | |
| // allowed value. | |
| TEST_F(AudioVectorTest, InsertBeyondEnd) { | |
| AudioVector vec; | |
| vec.PushBack(array_, array_length()); | |
| static const int kNewLength = 5; | |
| int16_t new_array[kNewLength]; | |
| // Set array elements to {100, 101, 102, ... }. | |
| for (int i = 0; i < kNewLength; ++i) { | |
| new_array[i] = 100 + i; | |
| } | |
| int insert_position = checked_cast<int>(array_length() + 10); // Too large. | |
| vec.InsertAt(new_array, kNewLength, insert_position); | |
| // Verify that the vector looks as follows: | |
| // {0, 1, ..., kLength - 1, 100, 101, ..., 100 + kNewLength - 1 }. | |
| size_t pos = 0; | |
| for (size_t i = 0; i < array_length(); ++i) { | |
| EXPECT_EQ(array_[i], vec[pos]); | |
| ++pos; | |
| } | |
| for (int i = 0; i < kNewLength; ++i) { | |
| EXPECT_EQ(new_array[i], vec[pos]); | |
| ++pos; | |
| } | |
| } | |
| // Test the OverwriteAt method with a position such that all of the new values | |
| // fit within the old vector. | |
| TEST_F(AudioVectorTest, OverwriteAt) { | |
| AudioVector vec; | |
| vec.PushBack(array_, array_length()); | |
| static const int kNewLength = 5; | |
| int16_t new_array[kNewLength]; | |
| // Set array elements to {100, 101, 102, ... }. | |
| for (int i = 0; i < kNewLength; ++i) { | |
| new_array[i] = 100 + i; | |
| } | |
| size_t insert_position = 2; | |
| vec.OverwriteAt(new_array, kNewLength, insert_position); | |
| // Verify that the vector looks as follows: | |
| // {0, ..., `insert_position` - 1, 100, 101, ..., 100 + kNewLength - 1, | |
| // `insert_position`, `insert_position` + 1, ..., kLength - 1}. | |
| size_t pos = 0; | |
| for (pos = 0; pos < insert_position; ++pos) { | |
| EXPECT_EQ(array_[pos], vec[pos]); | |
| } | |
| for (int i = 0; i < kNewLength; ++i) { | |
| EXPECT_EQ(new_array[i], vec[pos]); | |
| ++pos; | |
| } | |
| for (; pos < array_length(); ++pos) { | |
| EXPECT_EQ(array_[pos], vec[pos]); | |
| } | |
| } | |
| // Test the OverwriteAt method with a position such that some of the new values | |
| // extend beyond the end of the current vector. This is valid, and the vector is | |
| // expected to expand to accommodate the new values. | |
| TEST_F(AudioVectorTest, OverwriteBeyondEnd) { | |
| AudioVector vec; | |
| vec.PushBack(array_, array_length()); | |
| static const int kNewLength = 5; | |
| int16_t new_array[kNewLength]; | |
| // Set array elements to {100, 101, 102, ... }. | |
| for (int i = 0; i < kNewLength; ++i) { | |
| new_array[i] = 100 + i; | |
| } | |
| int insert_position = checked_cast<int>(array_length() - 2); | |
| vec.OverwriteAt(new_array, kNewLength, insert_position); | |
| ASSERT_EQ(array_length() - 2u + kNewLength, vec.Size()); | |
| // Verify that the vector looks as follows: | |
| // {0, ..., `insert_position` - 1, 100, 101, ..., 100 + kNewLength - 1, | |
| // `insert_position`, `insert_position` + 1, ..., kLength - 1}. | |
| int pos = 0; | |
| for (pos = 0; pos < insert_position; ++pos) { | |
| EXPECT_EQ(array_[pos], vec[pos]); | |
| } | |
| for (int i = 0; i < kNewLength; ++i) { | |
| EXPECT_EQ(new_array[i], vec[pos]); | |
| ++pos; | |
| } | |
| // Verify that we checked to the end of `vec`. | |
| EXPECT_EQ(vec.Size(), static_cast<size_t>(pos)); | |
| } | |
| TEST_F(AudioVectorTest, CrossFade) { | |
| static const size_t kLength = 100; | |
| static const size_t kFadeLength = 10; | |
| AudioVector vec1(kLength); | |
| AudioVector vec2(kLength); | |
| // Set all vector elements to 0 in `vec1` and 100 in `vec2`. | |
| for (size_t i = 0; i < kLength; ++i) { | |
| vec1[i] = 0; | |
| vec2[i] = 100; | |
| } | |
| vec1.CrossFade(vec2, kFadeLength); | |
| ASSERT_EQ(2 * kLength - kFadeLength, vec1.Size()); | |
| // First part untouched. | |
| for (size_t i = 0; i < kLength - kFadeLength; ++i) { | |
| EXPECT_EQ(0, vec1[i]); | |
| } | |
| // Check mixing zone. | |
| for (size_t i = 0; i < kFadeLength; ++i) { | |
| EXPECT_NEAR((i + 1) * 100 / (kFadeLength + 1), | |
| vec1[kLength - kFadeLength + i], 1); | |
| } | |
| // Second part untouched. | |
| for (size_t i = kLength; i < vec1.Size(); ++i) { | |
| EXPECT_EQ(100, vec1[i]); | |
| } | |
| } | |
| } // namespace webrtc |