Reimplementing NetEq4's AudioVector
The current implementation using std::vector is too slow. This CL introduces a new implementation, using a regular array as data container. In AudioMultiVector::ReadInterleavedFromIndex, a special case for 1 channel was implemented, to further reduce runtime. Finally, AudioMultiVector::Channels was reimplemented. The changes in this CL reduces the runtime of neteq4_speed_test by 33%. BUG=1363 R=turaj@webrtc.org Review URL: https://webrtc-codereview.appspot.com/3509004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5115 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
38599510df
commit
e8433eb115
@ -24,6 +24,7 @@ AudioMultiVector::AudioMultiVector(size_t N) {
|
||||
for (size_t n = 0; n < N; ++n) {
|
||||
channels_.push_back(new AudioVector);
|
||||
}
|
||||
num_channels_ = N;
|
||||
}
|
||||
|
||||
AudioMultiVector::AudioMultiVector(size_t N, size_t initial_size) {
|
||||
@ -32,6 +33,7 @@ AudioMultiVector::AudioMultiVector(size_t N, size_t initial_size) {
|
||||
for (size_t n = 0; n < N; ++n) {
|
||||
channels_.push_back(new AudioVector(initial_size));
|
||||
}
|
||||
num_channels_ = N;
|
||||
}
|
||||
|
||||
AudioMultiVector::~AudioMultiVector() {
|
||||
@ -43,13 +45,13 @@ AudioMultiVector::~AudioMultiVector() {
|
||||
}
|
||||
|
||||
void AudioMultiVector::Clear() {
|
||||
for (size_t i = 0; i < Channels(); ++i) {
|
||||
for (size_t i = 0; i < num_channels_; ++i) {
|
||||
channels_[i]->Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioMultiVector::Zeros(size_t length) {
|
||||
for (size_t i = 0; i < Channels(); ++i) {
|
||||
for (size_t i = 0; i < num_channels_; ++i) {
|
||||
channels_[i]->Clear();
|
||||
channels_[i]->Extend(length);
|
||||
}
|
||||
@ -57,7 +59,7 @@ void AudioMultiVector::Zeros(size_t length) {
|
||||
|
||||
void AudioMultiVector::CopyFrom(AudioMultiVector* copy_to) const {
|
||||
if (copy_to) {
|
||||
for (size_t i = 0; i < Channels(); ++i) {
|
||||
for (size_t i = 0; i < num_channels_; ++i) {
|
||||
channels_[i]->CopyFrom(&(*copy_to)[i]);
|
||||
}
|
||||
}
|
||||
@ -65,22 +67,22 @@ void AudioMultiVector::CopyFrom(AudioMultiVector* copy_to) const {
|
||||
|
||||
void AudioMultiVector::PushBackInterleaved(const int16_t* append_this,
|
||||
size_t length) {
|
||||
assert(length % Channels() == 0);
|
||||
if (Channels() == 1) {
|
||||
assert(length % num_channels_ == 0);
|
||||
if (num_channels_ == 1) {
|
||||
// Special case to avoid extra allocation and data shuffling.
|
||||
channels_[0]->PushBack(append_this, length);
|
||||
return;
|
||||
}
|
||||
size_t length_per_channel = length / Channels();
|
||||
size_t length_per_channel = length / num_channels_;
|
||||
int16_t* temp_array =
|
||||
new int16_t[length_per_channel]; // Intermediate storage.
|
||||
for (size_t channel = 0; channel < Channels(); ++channel) {
|
||||
for (size_t channel = 0; channel < num_channels_; ++channel) {
|
||||
// Copy elements to |temp_array|.
|
||||
// Set |source_ptr| to first element of this channel.
|
||||
const int16_t* source_ptr = &append_this[channel];
|
||||
for (size_t i = 0; i < length_per_channel; ++i) {
|
||||
temp_array[i] = *source_ptr;
|
||||
source_ptr += Channels(); // Jump to next element of this channel.
|
||||
source_ptr += num_channels_; // Jump to next element of this channel.
|
||||
}
|
||||
channels_[channel]->PushBack(temp_array, length_per_channel);
|
||||
}
|
||||
@ -88,9 +90,9 @@ void AudioMultiVector::PushBackInterleaved(const int16_t* append_this,
|
||||
}
|
||||
|
||||
void AudioMultiVector::PushBack(const AudioMultiVector& append_this) {
|
||||
assert(Channels() == append_this.Channels());
|
||||
if (Channels() == append_this.Channels()) {
|
||||
for (size_t i = 0; i < Channels(); ++i) {
|
||||
assert(num_channels_ == append_this.num_channels_);
|
||||
if (num_channels_ == append_this.num_channels_) {
|
||||
for (size_t i = 0; i < num_channels_; ++i) {
|
||||
channels_[i]->PushBack(append_this[i]);
|
||||
}
|
||||
}
|
||||
@ -101,22 +103,22 @@ void AudioMultiVector::PushBackFromIndex(const AudioMultiVector& append_this,
|
||||
assert(index < append_this.Size());
|
||||
index = std::min(index, append_this.Size() - 1);
|
||||
size_t length = append_this.Size() - index;
|
||||
assert(Channels() == append_this.Channels());
|
||||
if (Channels() == append_this.Channels()) {
|
||||
for (size_t i = 0; i < Channels(); ++i) {
|
||||
assert(num_channels_ == append_this.num_channels_);
|
||||
if (num_channels_ == append_this.num_channels_) {
|
||||
for (size_t i = 0; i < num_channels_; ++i) {
|
||||
channels_[i]->PushBack(&append_this[i][index], length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioMultiVector::PopFront(size_t length) {
|
||||
for (size_t i = 0; i < Channels(); ++i) {
|
||||
for (size_t i = 0; i < num_channels_; ++i) {
|
||||
channels_[i]->PopFront(length);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioMultiVector::PopBack(size_t length) {
|
||||
for (size_t i = 0; i < Channels(); ++i) {
|
||||
for (size_t i = 0; i < num_channels_; ++i) {
|
||||
channels_[i]->PopBack(length);
|
||||
}
|
||||
}
|
||||
@ -138,8 +140,13 @@ size_t AudioMultiVector::ReadInterleavedFromIndex(size_t start_index,
|
||||
if (length + start_index > Size()) {
|
||||
length = Size() - start_index;
|
||||
}
|
||||
if (num_channels_ == 1) {
|
||||
// Special case to avoid the nested for loop below.
|
||||
memcpy(destination, &(*this)[0][start_index], length * sizeof(int16_t));
|
||||
return length;
|
||||
}
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
for (size_t channel = 0; channel < Channels(); ++channel) {
|
||||
for (size_t channel = 0; channel < num_channels_; ++channel) {
|
||||
destination[index] = (*this)[channel][i + start_index];
|
||||
++index;
|
||||
}
|
||||
@ -156,12 +163,12 @@ size_t AudioMultiVector::ReadInterleavedFromEnd(size_t length,
|
||||
void AudioMultiVector::OverwriteAt(const AudioMultiVector& insert_this,
|
||||
size_t length,
|
||||
size_t position) {
|
||||
assert(Channels() == insert_this.Channels());
|
||||
assert(num_channels_ == insert_this.num_channels_);
|
||||
// Cap |length| at the length of |insert_this|.
|
||||
assert(length <= insert_this.Size());
|
||||
length = std::min(length, insert_this.Size());
|
||||
if (Channels() == insert_this.Channels()) {
|
||||
for (size_t i = 0; i < Channels(); ++i) {
|
||||
if (num_channels_ == insert_this.num_channels_) {
|
||||
for (size_t i = 0; i < num_channels_; ++i) {
|
||||
channels_[i]->OverwriteAt(&insert_this[i][0], length, position);
|
||||
}
|
||||
}
|
||||
@ -169,9 +176,9 @@ void AudioMultiVector::OverwriteAt(const AudioMultiVector& insert_this,
|
||||
|
||||
void AudioMultiVector::CrossFade(const AudioMultiVector& append_this,
|
||||
size_t fade_length) {
|
||||
assert(Channels() == append_this.Channels());
|
||||
if (Channels() == append_this.Channels()) {
|
||||
for (size_t i = 0; i < Channels(); ++i) {
|
||||
assert(num_channels_ == append_this.num_channels_);
|
||||
if (num_channels_ == append_this.num_channels_) {
|
||||
for (size_t i = 0; i < num_channels_; ++i) {
|
||||
channels_[i]->CrossFade(append_this[i], fade_length);
|
||||
}
|
||||
}
|
||||
@ -185,7 +192,7 @@ size_t AudioMultiVector::Size() const {
|
||||
void AudioMultiVector::AssertSize(size_t required_size) {
|
||||
if (Size() < required_size) {
|
||||
size_t extend_length = required_size - Size();
|
||||
for (size_t channel = 0; channel < Channels(); ++channel) {
|
||||
for (size_t channel = 0; channel < num_channels_; ++channel) {
|
||||
channels_[channel]->Extend(extend_length);
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ class AudioMultiVector {
|
||||
size_t fade_length);
|
||||
|
||||
// Returns the number of channels.
|
||||
virtual size_t Channels() const { return channels_.size(); }
|
||||
virtual size_t Channels() const { return num_channels_; }
|
||||
|
||||
// Returns the number of elements per channel in this AudioMultiVector.
|
||||
virtual size_t Size() const;
|
||||
@ -124,6 +124,7 @@ class AudioMultiVector {
|
||||
|
||||
protected:
|
||||
std::vector<AudioVector*> channels_;
|
||||
size_t num_channels_;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(AudioMultiVector);
|
||||
|
@ -19,18 +19,24 @@
|
||||
namespace webrtc {
|
||||
|
||||
void AudioVector::Clear() {
|
||||
vector_.clear();
|
||||
first_free_ix_ = 0;
|
||||
}
|
||||
|
||||
void AudioVector::CopyFrom(AudioVector* copy_to) const {
|
||||
if (copy_to) {
|
||||
copy_to->vector_.assign(vector_.begin(), vector_.end());
|
||||
copy_to->Reserve(Size());
|
||||
assert(copy_to->capacity_ >= Size());
|
||||
memcpy(copy_to->array_.get(), array_.get(), Size() * sizeof(int16_t));
|
||||
copy_to->first_free_ix_ = first_free_ix_;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioVector::PushFront(const AudioVector& prepend_this) {
|
||||
vector_.insert(vector_.begin(), prepend_this.vector_.begin(),
|
||||
prepend_this.vector_.end());
|
||||
size_t insert_length = prepend_this.Size();
|
||||
Reserve(Size() + insert_length);
|
||||
memmove(&array_[insert_length], &array_[0], Size() * sizeof(int16_t));
|
||||
memcpy(&array_[0], &prepend_this.array_[0], insert_length * sizeof(int16_t));
|
||||
first_free_ix_ += insert_length;
|
||||
}
|
||||
|
||||
void AudioVector::PushFront(const int16_t* prepend_this, size_t length) {
|
||||
@ -39,83 +45,77 @@ void AudioVector::PushFront(const int16_t* prepend_this, size_t length) {
|
||||
}
|
||||
|
||||
void AudioVector::PushBack(const AudioVector& append_this) {
|
||||
vector_.reserve(vector_.size() + append_this.Size());
|
||||
for (size_t i = 0; i < append_this.Size(); ++i) {
|
||||
vector_.push_back(append_this[i]);
|
||||
}
|
||||
PushBack(append_this.array_.get(), append_this.Size());
|
||||
}
|
||||
|
||||
void AudioVector::PushBack(const int16_t* append_this, size_t length) {
|
||||
vector_.reserve(vector_.size() + length);
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
vector_.push_back(append_this[i]);
|
||||
}
|
||||
Reserve(Size() + length);
|
||||
memcpy(&array_[first_free_ix_], append_this, length * sizeof(int16_t));
|
||||
first_free_ix_ += length;
|
||||
}
|
||||
|
||||
void AudioVector::PopFront(size_t length) {
|
||||
if (length >= vector_.size()) {
|
||||
if (length >= Size()) {
|
||||
// Remove all elements.
|
||||
vector_.clear();
|
||||
Clear();
|
||||
} else {
|
||||
std::vector<int16_t>::iterator end_range = vector_.begin();
|
||||
end_range += length;
|
||||
// Erase all elements in range vector_.begin() and |end_range| (not
|
||||
// including |end_range|).
|
||||
vector_.erase(vector_.begin(), end_range);
|
||||
size_t remaining_samples = Size() - length;
|
||||
memmove(&array_[0], &array_[length], remaining_samples * sizeof(int16_t));
|
||||
first_free_ix_ -= length;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioVector::PopBack(size_t length) {
|
||||
// Make sure that new_size is never negative (which causes wrap-around).
|
||||
size_t new_size = vector_.size() - std::min(length, vector_.size());
|
||||
vector_.resize(new_size);
|
||||
// Never remove more than what is in the array.
|
||||
length = std::min(length, Size());
|
||||
first_free_ix_ -= length;
|
||||
}
|
||||
|
||||
void AudioVector::Extend(size_t extra_length) {
|
||||
vector_.insert(vector_.end(), extra_length, 0);
|
||||
Reserve(Size() + extra_length);
|
||||
memset(&array_[first_free_ix_], 0, extra_length * sizeof(int16_t));
|
||||
first_free_ix_ += extra_length;
|
||||
}
|
||||
|
||||
void AudioVector::InsertAt(const int16_t* insert_this,
|
||||
size_t length,
|
||||
size_t position) {
|
||||
std::vector<int16_t>::iterator insert_position = vector_.begin();
|
||||
Reserve(Size() + length);
|
||||
// Cap the position at the current vector length, to be sure the iterator
|
||||
// does not extend beyond the end of the vector.
|
||||
position = std::min(vector_.size(), position);
|
||||
insert_position += position;
|
||||
// First, insert zeros at the position. This makes the vector longer (and
|
||||
// invalidates the iterator |insert_position|.
|
||||
vector_.insert(insert_position, length, 0);
|
||||
// Write the new values into the vector.
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
vector_[position + i] = insert_this[i];
|
||||
}
|
||||
position = std::min(Size(), position);
|
||||
int16_t* insert_position_ptr = &array_[position];
|
||||
size_t samples_to_move = Size() - position;
|
||||
memmove(insert_position_ptr + length, insert_position_ptr,
|
||||
samples_to_move * sizeof(int16_t));
|
||||
memcpy(insert_position_ptr, insert_this, length * sizeof(int16_t));
|
||||
first_free_ix_ += length;
|
||||
}
|
||||
|
||||
void AudioVector::InsertZerosAt(size_t length,
|
||||
size_t position) {
|
||||
std::vector<int16_t>::iterator insert_position = vector_.begin();
|
||||
Reserve(Size() + length);
|
||||
// Cap the position at the current vector length, to be sure the iterator
|
||||
// does not extend beyond the end of the vector.
|
||||
position = std::min(vector_.size(), position);
|
||||
insert_position += position;
|
||||
// Insert zeros at the position. This makes the vector longer (and
|
||||
// invalidates the iterator |insert_position|.
|
||||
vector_.insert(insert_position, length, 0);
|
||||
position = std::min(capacity_, position);
|
||||
int16_t* insert_position_ptr = &array_[position];
|
||||
size_t samples_to_move = Size() - position;
|
||||
memmove(insert_position_ptr + length, insert_position_ptr,
|
||||
samples_to_move * sizeof(int16_t));
|
||||
memset(insert_position_ptr, 0, length * sizeof(int16_t));
|
||||
first_free_ix_ += length;
|
||||
}
|
||||
|
||||
void AudioVector::OverwriteAt(const int16_t* insert_this,
|
||||
size_t length,
|
||||
size_t position) {
|
||||
// Cap the insert position at the current vector length.
|
||||
position = std::min(vector_.size(), position);
|
||||
// Extend the vector if needed. (It is valid to overwrite beyond the current
|
||||
// end of the vector.)
|
||||
if (position + length > vector_.size()) {
|
||||
Extend(position + length - vector_.size());
|
||||
}
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
vector_[position + i] = insert_this[i];
|
||||
// Cap the insert position at the current array length.
|
||||
position = std::min(Size(), position);
|
||||
Reserve(position + length);
|
||||
memcpy(&array_[position], insert_this, length * sizeof(int16_t));
|
||||
if (position + length > Size()) {
|
||||
// Array was expanded.
|
||||
first_free_ix_ += position + length - Size();
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,7 +135,7 @@ void AudioVector::CrossFade(const AudioVector& append_this,
|
||||
int alpha = 16384;
|
||||
for (size_t i = 0; i < fade_length; ++i) {
|
||||
alpha -= alpha_step;
|
||||
vector_[position + i] = (alpha * vector_[position + i] +
|
||||
array_[position + i] = (alpha * array_[position + i] +
|
||||
(16384 - alpha) * append_this[i] + 8192) >> 14;
|
||||
}
|
||||
assert(alpha >= 0); // Verify that the slope was correct.
|
||||
@ -146,11 +146,20 @@ void AudioVector::CrossFade(const AudioVector& append_this,
|
||||
}
|
||||
|
||||
const int16_t& AudioVector::operator[](size_t index) const {
|
||||
return vector_[index];
|
||||
return array_[index];
|
||||
}
|
||||
|
||||
int16_t& AudioVector::operator[](size_t index) {
|
||||
return vector_[index];
|
||||
return array_[index];
|
||||
}
|
||||
|
||||
void AudioVector::Reserve(size_t n) {
|
||||
if (capacity_ < n) {
|
||||
scoped_ptr<int16_t[]> temp_array(new int16_t[n]);
|
||||
memcpy(temp_array.get(), array_.get(), Size() * sizeof(int16_t));
|
||||
array_.swap(temp_array);
|
||||
capacity_ = n;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -13,9 +13,8 @@
|
||||
|
||||
#include <string.h> // Access to size_t.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/system_wrappers/interface/constructor_magic.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
@ -23,11 +22,18 @@ namespace webrtc {
|
||||
class AudioVector {
|
||||
public:
|
||||
// Creates an empty AudioVector.
|
||||
AudioVector() {}
|
||||
AudioVector()
|
||||
: array_(new int16_t[kDefaultInitialSize]),
|
||||
first_free_ix_(0),
|
||||
capacity_(kDefaultInitialSize) {}
|
||||
|
||||
// Creates an AudioVector with an initial size.
|
||||
explicit AudioVector(size_t initial_size)
|
||||
: vector_(initial_size, 0) {}
|
||||
: array_(new int16_t[initial_size]),
|
||||
first_free_ix_(initial_size),
|
||||
capacity_(initial_size) {
|
||||
memset(array_.get(), 0, initial_size * sizeof(int16_t));
|
||||
}
|
||||
|
||||
virtual ~AudioVector() {}
|
||||
|
||||
@ -88,17 +94,24 @@ class AudioVector {
|
||||
virtual void CrossFade(const AudioVector& append_this, size_t fade_length);
|
||||
|
||||
// Returns the number of elements in this AudioVector.
|
||||
virtual size_t Size() const { return vector_.size(); }
|
||||
virtual size_t Size() const { return first_free_ix_; }
|
||||
|
||||
// Returns true if this AudioVector is empty.
|
||||
virtual bool Empty() const { return vector_.empty(); }
|
||||
virtual bool Empty() const { return (first_free_ix_ == 0); }
|
||||
|
||||
// Accesses and modifies an element of AudioVector.
|
||||
const int16_t& operator[](size_t index) const;
|
||||
int16_t& operator[](size_t index);
|
||||
|
||||
private:
|
||||
std::vector<int16_t> vector_;
|
||||
static const size_t kDefaultInitialSize = 10;
|
||||
|
||||
void Reserve(size_t n);
|
||||
|
||||
scoped_ptr<int16_t[]> array_;
|
||||
size_t first_free_ix_; // The first index after the last sample in array_.
|
||||
// Note that this index may point outside of array_.
|
||||
size_t capacity_; // Allocated number of samples in the array.
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AudioVector);
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user