Add AudioMultiVector::ReadInterleavedFromIndex()
The new method differs from the existing method with the same name in
two respects:
* It reads into an InterleavedView<> instead of pointer.
* It either reads exactly the requested number of samples or fails
After examining the behavior in NetEq, where this method is called from,
it seems that what we need is a method that reads exactly the required
number of samples, not partially.
A follow-up change to NetEqImpl and SyncBuffer will change
SyncBuffer::GetNextAudioInterleaved() to read the requested buffer size
or fail, as well as update NetEqImpl::GetAudioInternal accordingly.
Bug: chromium:335805780
Change-Id: Ia78f888d52c9acb4b53b7042b31b8b0c53199e7b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/392143
Reviewed-by: Per Åhgren <peah@webrtc.org>
Reviewed-by: Jakob Ivarsson <jakobi@webrtc.org>
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#44787}
diff --git a/modules/audio_coding/neteq/audio_multi_vector.cc b/modules/audio_coding/neteq/audio_multi_vector.cc
index baad772..6ba4bc3 100644
--- a/modules/audio_coding/neteq/audio_multi_vector.cc
+++ b/modules/audio_coding/neteq/audio_multi_vector.cc
@@ -13,6 +13,7 @@
#include <algorithm>
#include <cstddef>
#include <cstdint>
+#include <memory>
#include <vector>
#include "api/array_view.h"
@@ -21,23 +22,26 @@
#include "rtc_base/checks.h"
namespace webrtc {
-
-AudioMultiVector::AudioMultiVector(size_t N) : channels_(N) {
- RTC_DCHECK_GT(N, 0);
- RTC_DCHECK_LE(N, kMaxNumberOfAudioChannels);
- for (auto& c : channels_) {
- c.reset(new AudioVector());
+namespace {
+std::vector<std::unique_ptr<AudioVector>> InitializeChannelVector(
+ size_t num_channels,
+ size_t channel_size = 0u) {
+ RTC_DCHECK_GT(num_channels, 0u);
+ RTC_CHECK_LE(num_channels, kMaxNumberOfAudioChannels);
+ std::vector<std::unique_ptr<AudioVector>> channels(num_channels);
+ for (auto& c : channels) {
+ c = channel_size ? std::make_unique<AudioVector>(channel_size)
+ : std::make_unique<AudioVector>();
}
+ return channels;
}
+} // namespace
+
+AudioMultiVector::AudioMultiVector(size_t N)
+ : channels_(InitializeChannelVector(N)) {}
AudioMultiVector::AudioMultiVector(size_t N, size_t initial_size)
- : channels_(N) {
- RTC_DCHECK_GT(N, 0);
- RTC_DCHECK_LE(N, kMaxNumberOfAudioChannels);
- for (auto& c : channels_) {
- c.reset(new AudioVector(initial_size));
- }
-}
+ : channels_(InitializeChannelVector(N, initial_size)) {}
AudioMultiVector::~AudioMultiVector() = default;
@@ -148,6 +152,27 @@
return index;
}
+bool AudioMultiVector::ReadInterleavedFromIndex(
+ const size_t start_index,
+ InterleavedView<int16_t> dst) const {
+ RTC_DCHECK_EQ(dst.num_channels(), Channels());
+ if (start_index + dst.samples_per_channel() > Size()) {
+ return false;
+ }
+ if (Channels() == 1) {
+ // Special case to avoid the nested for loop below.
+ return channels_[0]->CopyTo(start_index, dst.AsMono());
+ }
+ size_t index = 0;
+ for (size_t i = 0; i < dst.samples_per_channel(); ++i) {
+ for (const auto& ch : channels_) {
+ dst[index] = (*ch)[i + start_index];
+ ++index;
+ }
+ }
+ return true;
+}
+
size_t AudioMultiVector::ReadInterleavedFromEnd(size_t length,
int16_t* destination) const {
length = std::min(length, Size()); // Cannot read more than Size() elements.
diff --git a/modules/audio_coding/neteq/audio_multi_vector.h b/modules/audio_coding/neteq/audio_multi_vector.h
index b98e870..486c13a 100644
--- a/modules/audio_coding/neteq/audio_multi_vector.h
+++ b/modules/audio_coding/neteq/audio_multi_vector.h
@@ -18,6 +18,7 @@
#include <vector>
#include "api/array_view.h"
+#include "api/audio/audio_view.h"
#include "modules/audio_coding/neteq/audio_vector.h"
namespace webrtc {
@@ -86,6 +87,19 @@
size_t length,
int16_t* destination) const;
+ // Reads `dst.samples_per_channel()` from each channel into `dst`, a total of
+ // `dst.size()` samples, starting from the position provided by `start_index`.
+ //
+ // If not enough samples are available to read, then *none* will be read and
+ // the function returns false. If enough samples could be read, the return
+ // value will be true.
+ //
+ // This behavior is currently different from the pointer based
+ // `ReadInterleaved*` methods, but intentionally so in order to simplify the
+ // logic at the caller site.
+ bool ReadInterleavedFromIndex(const size_t start_index,
+ InterleavedView<int16_t> dst) const;
+
// Like ReadInterleaved() above, but reads from the end instead of from
// the beginning.
size_t ReadInterleavedFromEnd(size_t length, int16_t* destination) const;
@@ -129,7 +143,7 @@
AudioVector& operator[](size_t index);
protected:
- std::vector<std::unique_ptr<AudioVector>> channels_;
+ const std::vector<std::unique_ptr<AudioVector>> channels_;
};
} // namespace webrtc
diff --git a/modules/audio_coding/neteq/audio_multi_vector_unittest.cc b/modules/audio_coding/neteq/audio_multi_vector_unittest.cc
index 6193847..c9fe230 100644
--- a/modules/audio_coding/neteq/audio_multi_vector_unittest.cc
+++ b/modules/audio_coding/neteq/audio_multi_vector_unittest.cc
@@ -14,8 +14,10 @@
#include <cstdint>
#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"
@@ -187,21 +189,37 @@
TEST_P(AudioMultiVectorTest, ReadInterleaved) {
AudioMultiVector vec(num_channels_);
vec.PushBackInterleaved(array_interleaved_);
- int16_t* output = new int16_t[array_interleaved_.size()];
+ 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));
- EXPECT_EQ(0, memcmp(array_interleaved_.data(), output,
+ 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));
- EXPECT_EQ(0, memcmp(array_interleaved_.data(), output,
+ vec.ReadInterleaved(array_length() + 1, output.get()));
+ EXPECT_EQ(0, memcmp(array_interleaved_.data(), output.get(),
read_samples * sizeof(int16_t)));
+}
- delete[] output;
+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.
diff --git a/modules/audio_coding/neteq/audio_vector.cc b/modules/audio_coding/neteq/audio_vector.cc
index afbd6d4..096c8c4 100644
--- a/modules/audio_coding/neteq/audio_vector.cc
+++ b/modules/audio_coding/neteq/audio_vector.cc
@@ -16,6 +16,7 @@
#include <cstring>
#include <memory>
+#include "api/audio/audio_view.h"
#include "rtc_base/checks.h"
namespace webrtc {
@@ -62,6 +63,24 @@
}
}
+bool AudioVector::CopyTo(size_t position, MonoView<int16_t> dst) const {
+ if ((Size() - position) < dst.size()) {
+ return false;
+ }
+ const size_t copy_index = (begin_index_ + position) % capacity_;
+ const size_t first_chunk_length =
+ std::min(dst.size(), capacity_ - copy_index);
+ auto first_chunk = dst.subview(0, first_chunk_length);
+ CopySamples(first_chunk,
+ MonoView<const int16_t>(&array_[copy_index], first_chunk_length));
+ const size_t remaining_length = dst.size() - first_chunk_length;
+ if (remaining_length > 0) {
+ auto second_chunk = dst.subview(first_chunk_length, remaining_length);
+ CopySamples(second_chunk, MonoView<int16_t>(&array_[0], remaining_length));
+ }
+ return true;
+}
+
void AudioVector::PushFront(const AudioVector& prepend_this) {
const size_t length = prepend_this.Size();
if (length == 0)
diff --git a/modules/audio_coding/neteq/audio_vector.h b/modules/audio_coding/neteq/audio_vector.h
index 6b37aed..b8e7d49 100644
--- a/modules/audio_coding/neteq/audio_vector.h
+++ b/modules/audio_coding/neteq/audio_vector.h
@@ -16,6 +16,7 @@
#include <cstdint>
#include <memory>
+#include "api/audio/audio_view.h"
#include "rtc_base/checks.h"
namespace webrtc {
@@ -44,6 +45,11 @@
// Copies `length` values from `position` in this vector to `copy_to`.
void CopyTo(size_t length, size_t position, int16_t* copy_to) const;
+ // Copies `dst.size()` samples from a given position of the vector.
+ // If not enough samples are available, the function will return
+ // false and not do any copying.
+ bool CopyTo(size_t position, MonoView<int16_t> dst) const;
+
// Prepends the contents of AudioVector `prepend_this` to this object. The
// length of this object is increased with the length of `prepend_this`.
void PushFront(const AudioVector& prepend_this);
diff --git a/modules/audio_coding/neteq/audio_vector_unittest.cc b/modules/audio_coding/neteq/audio_vector_unittest.cc
index 97c1b7e..b215c4a 100644
--- a/modules/audio_coding/neteq/audio_vector_unittest.cc
+++ b/modules/audio_coding/neteq/audio_vector_unittest.cc
@@ -12,8 +12,10 @@
#include <stdlib.h>
+#include <array>
#include <cstdint>
+#include "api/audio/audio_view.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "test/gtest.h"
@@ -28,7 +30,9 @@
}
}
- size_t array_length() const { return sizeof(array_) / sizeof(array_[0]); }
+ constexpr size_t array_length() const {
+ return sizeof(array_) / sizeof(array_[0]);
+ }
int16_t array_[10];
};
@@ -79,6 +83,34 @@
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;