Using ring buffer for AudioVector in NetEq.
AudioVector used NetEq was based on a shift buffer, which has a high complexity, and the complexity is very much dependent on the capacity of the buffer. This CL changes the shift buffer to a ring buffer. Reduction in the CPU usages of NetEq is expected. BUG=608644 R=henrik.lundin@webrtc.org Review URL: https://codereview.webrtc.org/1948483002 . Cr-Commit-Position: refs/heads/master@{#12676}
This commit is contained in:
parent
17fa67214c
commit
79553cb66e
@ -106,7 +106,7 @@ void AudioMultiVector::PushBackFromIndex(const AudioMultiVector& append_this,
|
||||
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);
|
||||
channels_[i]->PushBack(append_this[i], length, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -133,14 +133,14 @@ size_t AudioMultiVector::ReadInterleavedFromIndex(size_t start_index,
|
||||
int16_t* destination) const {
|
||||
RTC_DCHECK(destination);
|
||||
size_t index = 0; // Number of elements written to |destination| so far.
|
||||
assert(start_index <= Size());
|
||||
RTC_DCHECK_LE(start_index, Size());
|
||||
start_index = std::min(start_index, Size());
|
||||
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));
|
||||
(*this)[0].CopyTo(length, start_index, destination);
|
||||
return length;
|
||||
}
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
@ -167,7 +167,7 @@ void AudioMultiVector::OverwriteAt(const AudioMultiVector& insert_this,
|
||||
length = std::min(length, insert_this.Size());
|
||||
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);
|
||||
channels_[i]->OverwriteAt(insert_this[i], length, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,124 +15,236 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "webrtc/base/checks.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
AudioVector::AudioVector()
|
||||
: array_(new int16_t[kDefaultInitialSize]),
|
||||
first_free_ix_(0),
|
||||
capacity_(kDefaultInitialSize) {
|
||||
: AudioVector(kDefaultInitialSize) {
|
||||
Clear();
|
||||
}
|
||||
|
||||
AudioVector::AudioVector(size_t initial_size)
|
||||
: array_(new int16_t[initial_size]),
|
||||
first_free_ix_(initial_size),
|
||||
capacity_(initial_size) {
|
||||
memset(array_.get(), 0, initial_size * sizeof(int16_t));
|
||||
: array_(new int16_t[initial_size + 1]),
|
||||
capacity_(initial_size + 1),
|
||||
begin_index_(0),
|
||||
end_index_(capacity_ - 1) {
|
||||
memset(array_.get(), 0, capacity_ * sizeof(int16_t));
|
||||
}
|
||||
|
||||
AudioVector::~AudioVector() = default;
|
||||
|
||||
void AudioVector::Clear() {
|
||||
first_free_ix_ = 0;
|
||||
end_index_ = begin_index_ = 0;
|
||||
}
|
||||
|
||||
void AudioVector::CopyTo(AudioVector* copy_to) const {
|
||||
if (copy_to) {
|
||||
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_;
|
||||
RTC_DCHECK(copy_to);
|
||||
copy_to->Reserve(Size());
|
||||
CopyTo(Size(), 0, copy_to->array_.get());
|
||||
copy_to->begin_index_ = 0;
|
||||
copy_to->end_index_ = Size();
|
||||
}
|
||||
|
||||
void AudioVector::CopyTo(
|
||||
size_t length, size_t position, int16_t* copy_to) const {
|
||||
if (length == 0)
|
||||
return;
|
||||
length = std::min(length, Size() - position);
|
||||
const size_t copy_index = (begin_index_ + position) % capacity_;
|
||||
const size_t first_chunk_length =
|
||||
std::min(length, capacity_ - copy_index);
|
||||
memcpy(copy_to, &array_[copy_index],
|
||||
first_chunk_length * sizeof(int16_t));
|
||||
const size_t remaining_length = length - first_chunk_length;
|
||||
if (remaining_length > 0) {
|
||||
memcpy(©_to[first_chunk_length], array_.get(),
|
||||
remaining_length * sizeof(int16_t));
|
||||
}
|
||||
}
|
||||
|
||||
void AudioVector::PushFront(const AudioVector& prepend_this) {
|
||||
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;
|
||||
const size_t length = prepend_this.Size();
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
// Although the subsequent calling to PushFront does Reserve in it, it is
|
||||
// always more efficient to do a big Reserve first.
|
||||
Reserve(Size() + length);
|
||||
|
||||
const size_t first_chunk_length =
|
||||
std::min(length, prepend_this.capacity_ - prepend_this.begin_index_);
|
||||
const size_t remaining_length = length - first_chunk_length;
|
||||
if (remaining_length > 0)
|
||||
PushFront(prepend_this.array_.get(), remaining_length);
|
||||
PushFront(&prepend_this.array_[prepend_this.begin_index_],
|
||||
first_chunk_length);
|
||||
}
|
||||
|
||||
void AudioVector::PushFront(const int16_t* prepend_this, size_t length) {
|
||||
// Same operation as InsertAt beginning.
|
||||
InsertAt(prepend_this, length, 0);
|
||||
if (length == 0)
|
||||
return;
|
||||
Reserve(Size() + length);
|
||||
const size_t first_chunk_length = std::min(length, begin_index_);
|
||||
memcpy(&array_[begin_index_ - first_chunk_length],
|
||||
&prepend_this[length - first_chunk_length],
|
||||
first_chunk_length * sizeof(int16_t));
|
||||
const size_t remaining_length = length - first_chunk_length;
|
||||
if (remaining_length > 0) {
|
||||
memcpy(&array_[capacity_ - remaining_length], prepend_this,
|
||||
remaining_length * sizeof(int16_t));
|
||||
}
|
||||
begin_index_ = (begin_index_ + capacity_ - length) % capacity_;
|
||||
}
|
||||
|
||||
void AudioVector::PushBack(const AudioVector& append_this) {
|
||||
PushBack(append_this.array_.get(), append_this.Size());
|
||||
PushBack(append_this, append_this.Size(), 0);
|
||||
}
|
||||
|
||||
void AudioVector::PushBack(
|
||||
const AudioVector& append_this, size_t length, size_t position) {
|
||||
RTC_DCHECK_LE(position, append_this.Size());
|
||||
RTC_DCHECK_LE(length, append_this.Size() - position);
|
||||
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
// Although the subsequent calling to PushBack does Reserve in it, it is
|
||||
// always more efficient to do a big Reserve first.
|
||||
Reserve(Size() + length);
|
||||
|
||||
const size_t start_index =
|
||||
(append_this.begin_index_ + position) % append_this.capacity_;
|
||||
const size_t first_chunk_length = std::min(
|
||||
length, append_this.capacity_ - start_index);
|
||||
PushBack(&append_this.array_[start_index], first_chunk_length);
|
||||
|
||||
const size_t remaining_length = length - first_chunk_length;
|
||||
if (remaining_length > 0)
|
||||
PushBack(append_this.array_.get(), remaining_length);
|
||||
}
|
||||
|
||||
void AudioVector::PushBack(const int16_t* append_this, size_t length) {
|
||||
if (length == 0)
|
||||
return;
|
||||
Reserve(Size() + length);
|
||||
memcpy(&array_[first_free_ix_], append_this, length * sizeof(int16_t));
|
||||
first_free_ix_ += length;
|
||||
const size_t first_chunk_length = std::min(length, capacity_ - end_index_);
|
||||
memcpy(&array_[end_index_], append_this,
|
||||
first_chunk_length * sizeof(int16_t));
|
||||
const size_t remaining_length = length - first_chunk_length;
|
||||
if (remaining_length > 0) {
|
||||
memcpy(array_.get(), &append_this[first_chunk_length],
|
||||
remaining_length * sizeof(int16_t));
|
||||
}
|
||||
end_index_ = (end_index_ + length) % capacity_;
|
||||
}
|
||||
|
||||
void AudioVector::PopFront(size_t length) {
|
||||
if (length >= Size()) {
|
||||
// Remove all elements.
|
||||
Clear();
|
||||
} else {
|
||||
size_t remaining_samples = Size() - length;
|
||||
memmove(&array_[0], &array_[length], remaining_samples * sizeof(int16_t));
|
||||
first_free_ix_ -= length;
|
||||
}
|
||||
if (length == 0)
|
||||
return;
|
||||
length = std::min(length, Size());
|
||||
begin_index_ = (begin_index_ + length) % capacity_;
|
||||
}
|
||||
|
||||
void AudioVector::PopBack(size_t length) {
|
||||
if (length == 0)
|
||||
return;
|
||||
// Never remove more than what is in the array.
|
||||
length = std::min(length, Size());
|
||||
first_free_ix_ -= length;
|
||||
end_index_ = (end_index_ + capacity_ - length) % capacity_;
|
||||
}
|
||||
|
||||
void AudioVector::Extend(size_t extra_length) {
|
||||
Reserve(Size() + extra_length);
|
||||
memset(&array_[first_free_ix_], 0, extra_length * sizeof(int16_t));
|
||||
first_free_ix_ += extra_length;
|
||||
if (extra_length == 0)
|
||||
return;
|
||||
InsertZerosByPushBack(extra_length, Size());
|
||||
}
|
||||
|
||||
void AudioVector::InsertAt(const int16_t* insert_this,
|
||||
size_t length,
|
||||
size_t position) {
|
||||
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.
|
||||
if (length == 0)
|
||||
return;
|
||||
// Cap the insert position at the current array length.
|
||||
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;
|
||||
|
||||
// When inserting to a position closer to the beginning, it is more efficient
|
||||
// to insert by pushing front than to insert by pushing back, since less data
|
||||
// will be moved, vice versa.
|
||||
if (position <= Size() - position) {
|
||||
InsertByPushFront(insert_this, length, position);
|
||||
} else {
|
||||
InsertByPushBack(insert_this, length, position);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioVector::InsertZerosAt(size_t length,
|
||||
size_t position) {
|
||||
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(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;
|
||||
if (length == 0)
|
||||
return;
|
||||
// Cap the insert position at the current array length.
|
||||
position = std::min(Size(), position);
|
||||
|
||||
// When inserting to a position closer to the beginning, it is more efficient
|
||||
// to insert by pushing front than to insert by pushing back, since less data
|
||||
// will be moved, vice versa.
|
||||
if (position <= Size() - position) {
|
||||
InsertZerosByPushFront(length, position);
|
||||
} else {
|
||||
InsertZerosByPushBack(length, position);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioVector::OverwriteAt(const AudioVector& insert_this,
|
||||
size_t length,
|
||||
size_t position) {
|
||||
RTC_DCHECK_LE(length, insert_this.Size());
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
// Cap the insert position at the current array length.
|
||||
position = std::min(Size(), position);
|
||||
|
||||
// Although the subsequent calling to OverwriteAt does Reserve in it, it is
|
||||
// always more efficient to do a big Reserve first.
|
||||
size_t new_size = std::max(Size(), position + length);
|
||||
Reserve(new_size);
|
||||
|
||||
const size_t first_chunk_length =
|
||||
std::min(length, insert_this.capacity_ - insert_this.begin_index_);
|
||||
OverwriteAt(&insert_this.array_[insert_this.begin_index_], first_chunk_length,
|
||||
position);
|
||||
const size_t remaining_length = length - first_chunk_length;
|
||||
if (remaining_length > 0) {
|
||||
OverwriteAt(insert_this.array_.get(), remaining_length,
|
||||
position + first_chunk_length);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioVector::OverwriteAt(const int16_t* insert_this,
|
||||
size_t length,
|
||||
size_t position) {
|
||||
if (length == 0)
|
||||
return;
|
||||
// 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();
|
||||
|
||||
size_t new_size = std::max(Size(), position + length);
|
||||
Reserve(new_size);
|
||||
|
||||
const size_t overwrite_index = (begin_index_ + position) % capacity_;
|
||||
const size_t first_chunk_length =
|
||||
std::min(length, capacity_ - overwrite_index);
|
||||
memcpy(&array_[overwrite_index], insert_this,
|
||||
first_chunk_length * sizeof(int16_t));
|
||||
const size_t remaining_length = length - first_chunk_length;
|
||||
if (remaining_length > 0) {
|
||||
memcpy(array_.get(), &insert_this[first_chunk_length],
|
||||
remaining_length * sizeof(int16_t));
|
||||
}
|
||||
|
||||
end_index_ = (begin_index_ + new_size) % capacity_;
|
||||
}
|
||||
|
||||
void AudioVector::CrossFade(const AudioVector& append_this,
|
||||
@ -142,7 +254,7 @@ void AudioVector::CrossFade(const AudioVector& append_this,
|
||||
assert(fade_length <= append_this.Size());
|
||||
fade_length = std::min(fade_length, Size());
|
||||
fade_length = std::min(fade_length, append_this.Size());
|
||||
size_t position = Size() - fade_length;
|
||||
size_t position = Size() - fade_length + begin_index_;
|
||||
// Cross fade the overlapping regions.
|
||||
// |alpha| is the mixing factor in Q14.
|
||||
// TODO(hlundin): Consider skipping +1 in the denominator to produce a
|
||||
@ -151,41 +263,132 @@ void AudioVector::CrossFade(const AudioVector& append_this,
|
||||
int alpha = 16384;
|
||||
for (size_t i = 0; i < fade_length; ++i) {
|
||||
alpha -= alpha_step;
|
||||
array_[position + i] = (alpha * array_[position + i] +
|
||||
(16384 - alpha) * append_this[i] + 8192) >> 14;
|
||||
array_[(position + i) % capacity_] =
|
||||
(alpha * array_[(position + i) % capacity_] +
|
||||
(16384 - alpha) * append_this[i] + 8192) >> 14;
|
||||
}
|
||||
assert(alpha >= 0); // Verify that the slope was correct.
|
||||
// Append what is left of |append_this|.
|
||||
size_t samples_to_push_back = append_this.Size() - fade_length;
|
||||
if (samples_to_push_back > 0)
|
||||
PushBack(&append_this[fade_length], samples_to_push_back);
|
||||
PushBack(append_this, samples_to_push_back, fade_length);
|
||||
}
|
||||
|
||||
// Returns the number of elements in this AudioVector.
|
||||
size_t AudioVector::Size() const {
|
||||
return first_free_ix_;
|
||||
return (end_index_ + capacity_ - begin_index_) % capacity_;
|
||||
}
|
||||
|
||||
// Returns true if this AudioVector is empty.
|
||||
bool AudioVector::Empty() const {
|
||||
return first_free_ix_ == 0;
|
||||
return begin_index_ == end_index_;
|
||||
}
|
||||
|
||||
const int16_t& AudioVector::operator[](size_t index) const {
|
||||
return array_[index];
|
||||
return array_[(begin_index_ + index) % capacity_];
|
||||
}
|
||||
|
||||
int16_t& AudioVector::operator[](size_t index) {
|
||||
return array_[index];
|
||||
return array_[(begin_index_ + index) % capacity_];
|
||||
}
|
||||
|
||||
void AudioVector::Reserve(size_t n) {
|
||||
if (capacity_ < n) {
|
||||
std::unique_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;
|
||||
if (capacity_ > n)
|
||||
return;
|
||||
const size_t length = Size();
|
||||
// Reserve one more sample to remove the ambiguity between empty vector and
|
||||
// full vector. Therefore |begin_index_| == |end_index_| indicates empty
|
||||
// vector, and |begin_index_| == (|end_index_| + 1) % capacity indicates
|
||||
// full vector.
|
||||
std::unique_ptr<int16_t[]> temp_array(new int16_t[n + 1]);
|
||||
CopyTo(length, 0, temp_array.get());
|
||||
array_.swap(temp_array);
|
||||
begin_index_ = 0;
|
||||
end_index_ = length;
|
||||
capacity_ = n + 1;
|
||||
}
|
||||
|
||||
void AudioVector::InsertByPushBack(const int16_t* insert_this,
|
||||
size_t length,
|
||||
size_t position) {
|
||||
const size_t move_chunk_length = Size() - position;
|
||||
std::unique_ptr<int16_t[]> temp_array(nullptr);
|
||||
if (move_chunk_length > 0) {
|
||||
// TODO(minyue): see if it is possible to avoid copying to a buffer.
|
||||
temp_array.reset(new int16_t[move_chunk_length]);
|
||||
CopyTo(move_chunk_length, position, temp_array.get());
|
||||
PopBack(move_chunk_length);
|
||||
}
|
||||
|
||||
Reserve(Size() + length + move_chunk_length);
|
||||
PushBack(insert_this, length);
|
||||
if (move_chunk_length > 0)
|
||||
PushBack(temp_array.get(), move_chunk_length);
|
||||
}
|
||||
|
||||
void AudioVector::InsertByPushFront(const int16_t* insert_this,
|
||||
size_t length,
|
||||
size_t position) {
|
||||
std::unique_ptr<int16_t[]> temp_array(nullptr);
|
||||
if (position > 0) {
|
||||
// TODO(minyue): see if it is possible to avoid copying to a buffer.
|
||||
temp_array.reset(new int16_t[position]);
|
||||
CopyTo(position, 0, temp_array.get());
|
||||
PopFront(position);
|
||||
}
|
||||
|
||||
Reserve(Size() + length + position);
|
||||
PushFront(insert_this, length);
|
||||
if (position > 0)
|
||||
PushFront(temp_array.get(), position);
|
||||
}
|
||||
|
||||
void AudioVector::InsertZerosByPushBack(size_t length,
|
||||
size_t position) {
|
||||
const size_t move_chunk_length = Size() - position;
|
||||
std::unique_ptr<int16_t[]> temp_array(nullptr);
|
||||
if (move_chunk_length > 0) {
|
||||
temp_array.reset(new int16_t[move_chunk_length]);
|
||||
CopyTo(move_chunk_length, position, temp_array.get());
|
||||
PopBack(move_chunk_length);
|
||||
}
|
||||
|
||||
Reserve(Size() + length + move_chunk_length);
|
||||
|
||||
const size_t first_zero_chunk_length =
|
||||
std::min(length, capacity_ - end_index_);
|
||||
memset(&array_[end_index_], 0, first_zero_chunk_length * sizeof(int16_t));
|
||||
const size_t remaining_zero_length = length - first_zero_chunk_length;
|
||||
if (remaining_zero_length > 0)
|
||||
memset(array_.get(), 0, remaining_zero_length * sizeof(int16_t));
|
||||
end_index_ = (end_index_ + length) % capacity_;
|
||||
|
||||
if (move_chunk_length > 0)
|
||||
PushBack(temp_array.get(), move_chunk_length);
|
||||
}
|
||||
|
||||
void AudioVector::InsertZerosByPushFront(size_t length,
|
||||
size_t position) {
|
||||
std::unique_ptr<int16_t[]> temp_array(nullptr);
|
||||
if (position > 0) {
|
||||
temp_array.reset(new int16_t[position]);
|
||||
CopyTo(position, 0, temp_array.get());
|
||||
PopFront(position);
|
||||
}
|
||||
|
||||
Reserve(Size() + length + position);
|
||||
|
||||
const size_t first_zero_chunk_length = std::min(length, begin_index_);
|
||||
memset(&array_[begin_index_ - first_zero_chunk_length], 0,
|
||||
first_zero_chunk_length * sizeof(int16_t));
|
||||
const size_t remaining_zero_length = length - first_zero_chunk_length;
|
||||
if (remaining_zero_length > 0)
|
||||
memset(&array_[capacity_ - remaining_zero_length], 0,
|
||||
remaining_zero_length * sizeof(int16_t));
|
||||
begin_index_ = (begin_index_ + capacity_ - length) % capacity_;
|
||||
|
||||
if (position > 0)
|
||||
PushFront(temp_array.get(), position);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -37,6 +37,9 @@ class AudioVector {
|
||||
// |copy_to| will be an exact replica of this object.
|
||||
virtual void CopyTo(AudioVector* copy_to) const;
|
||||
|
||||
// Copies |length| values from |position| in this vector to |copy_to|.
|
||||
virtual void CopyTo(size_t length, size_t position, int16_t* copy_to) const;
|
||||
|
||||
// Prepends the contents of AudioVector |prepend_this| to this object. The
|
||||
// length of this object is increased with the length of |prepend_this|.
|
||||
virtual void PushFront(const AudioVector& prepend_this);
|
||||
@ -48,6 +51,12 @@ class AudioVector {
|
||||
// Same as PushFront but will append to the end of this object.
|
||||
virtual void PushBack(const AudioVector& append_this);
|
||||
|
||||
// Appends a segment of |append_this| to the end of this object. The segment
|
||||
// starts from |position| and has |length| samples.
|
||||
virtual void PushBack(const AudioVector& append_this,
|
||||
size_t length,
|
||||
size_t position);
|
||||
|
||||
// Same as PushFront but will append to the end of this object.
|
||||
virtual void PushBack(const int16_t* append_this, size_t length);
|
||||
|
||||
@ -71,6 +80,15 @@ class AudioVector {
|
||||
// Like InsertAt, but inserts |length| zero elements at |position|.
|
||||
virtual void InsertZerosAt(size_t length, size_t position);
|
||||
|
||||
// Overwrites |length| elements of this AudioVector starting from |position|
|
||||
// with first values in |AudioVector|. The definition of |position|
|
||||
// is the same as for InsertAt(). If |length| and |position| are selected
|
||||
// such that the new data extends beyond the end of the current AudioVector,
|
||||
// the vector is extended to accommodate the new data.
|
||||
virtual void OverwriteAt(const AudioVector& insert_this,
|
||||
size_t length,
|
||||
size_t position);
|
||||
|
||||
// Overwrites |length| elements of this AudioVector with values taken from the
|
||||
// array |insert_this|, starting at |position|. The definition of |position|
|
||||
// is the same as for InsertAt(). If |length| and |position| are selected
|
||||
@ -100,11 +118,27 @@ class AudioVector {
|
||||
|
||||
void Reserve(size_t n);
|
||||
|
||||
void InsertByPushBack(const int16_t* insert_this, size_t length,
|
||||
size_t position);
|
||||
|
||||
void InsertByPushFront(const int16_t* insert_this, size_t length,
|
||||
size_t position);
|
||||
|
||||
void InsertZerosByPushBack(size_t length, size_t position);
|
||||
|
||||
void InsertZerosByPushFront(size_t length, size_t position);
|
||||
|
||||
std::unique_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.
|
||||
|
||||
// The index of the first sample in |array_|, except when
|
||||
// |begin_index_ == end_index_|, which indicates an empty buffer.
|
||||
size_t begin_index_;
|
||||
|
||||
// The index of the sample after the last sample in |array_|.
|
||||
size_t end_index_;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(AudioVector);
|
||||
};
|
||||
|
||||
|
||||
@ -82,14 +82,6 @@ TEST_F(AudioVectorTest, PushBackAndCopy) {
|
||||
EXPECT_TRUE(vec_copy.Empty());
|
||||
}
|
||||
|
||||
// Try to copy to a NULL pointer. Nothing should happen.
|
||||
TEST_F(AudioVectorTest, CopyToNull) {
|
||||
AudioVector vec;
|
||||
AudioVector* vec_copy = NULL;
|
||||
vec.PushBack(array_, array_length());
|
||||
vec.CopyTo(vec_copy);
|
||||
}
|
||||
|
||||
// Test the PushBack method with another AudioVector as input argument.
|
||||
TEST_F(AudioVectorTest, PushBackVector) {
|
||||
static const size_t kLength = 10;
|
||||
|
||||
@ -59,10 +59,7 @@ void BackgroundNoise::Update(const AudioMultiVector& input,
|
||||
ChannelParameters& parameters = channel_parameters_[channel_ix];
|
||||
int16_t temp_signal_array[kVecLen + kMaxLpcOrder] = {0};
|
||||
int16_t* temp_signal = &temp_signal_array[kMaxLpcOrder];
|
||||
memcpy(temp_signal,
|
||||
&input[channel_ix][input.Size() - kVecLen],
|
||||
sizeof(int16_t) * kVecLen);
|
||||
|
||||
input[channel_ix].CopyTo(kVecLen, input.Size() - kVecLen, temp_signal);
|
||||
int32_t sample_energy = CalculateAutoCorrelation(temp_signal, kVecLen,
|
||||
auto_correlation);
|
||||
|
||||
|
||||
@ -67,10 +67,10 @@ int ComfortNoise::Generate(size_t requested_length,
|
||||
LOG(LS_ERROR) << "Unknwown payload type";
|
||||
return kUnknownPayloadType;
|
||||
}
|
||||
// The expression &(*output)[0][0] is a pointer to the first element in
|
||||
// the first channel.
|
||||
|
||||
std::unique_ptr<int16_t[]> temp(new int16_t[number_of_samples]);
|
||||
if (!cng_decoder->Generate(
|
||||
rtc::ArrayView<int16_t>(&(*output)[0][0], number_of_samples),
|
||||
rtc::ArrayView<int16_t>(temp.get(), number_of_samples),
|
||||
new_period)) {
|
||||
// Error returned.
|
||||
output->Zeros(requested_length);
|
||||
@ -78,6 +78,7 @@ int ComfortNoise::Generate(size_t requested_length,
|
||||
"ComfortNoiseDecoder::Genererate failed to generate comfort noise";
|
||||
return kInternalError;
|
||||
}
|
||||
(*output)[0].OverwriteAt(temp.get(), number_of_samples, 0);
|
||||
|
||||
if (first_call_) {
|
||||
// Set tapering window parameters. Values are in Q15.
|
||||
|
||||
@ -80,6 +80,22 @@ int DspHelper::RampSignal(int16_t* signal,
|
||||
return RampSignal(signal, length, factor, increment, signal);
|
||||
}
|
||||
|
||||
int DspHelper::RampSignal(AudioVector* signal,
|
||||
size_t start_index,
|
||||
size_t length,
|
||||
int factor,
|
||||
int increment) {
|
||||
int factor_q20 = (factor << 6) + 32;
|
||||
// TODO(hlundin): Add 32 to factor_q20 when converting back to Q14?
|
||||
for (size_t i = start_index; i < start_index + length; ++i) {
|
||||
(*signal)[i] = (factor * (*signal)[i] + 8192) >> 14;
|
||||
factor_q20 += increment;
|
||||
factor_q20 = std::max(factor_q20, 0); // Never go negative.
|
||||
factor = std::min(factor_q20 >> 6, 16384);
|
||||
}
|
||||
return factor;
|
||||
}
|
||||
|
||||
int DspHelper::RampSignal(AudioMultiVector* signal,
|
||||
size_t start_index,
|
||||
size_t length,
|
||||
@ -94,7 +110,7 @@ int DspHelper::RampSignal(AudioMultiVector* signal,
|
||||
// Loop over the channels, starting at the same |factor| each time.
|
||||
for (size_t channel = 0; channel < signal->Channels(); ++channel) {
|
||||
end_factor =
|
||||
RampSignal(&(*signal)[channel][start_index], length, factor, increment);
|
||||
RampSignal(&(*signal)[channel], start_index, length, factor, increment);
|
||||
}
|
||||
return end_factor;
|
||||
}
|
||||
|
||||
@ -67,6 +67,13 @@ class DspHelper {
|
||||
|
||||
// Same as above, but processes |length| samples from |signal|, starting at
|
||||
// |start_index|.
|
||||
static int RampSignal(AudioVector* signal,
|
||||
size_t start_index,
|
||||
size_t length,
|
||||
int factor,
|
||||
int increment);
|
||||
|
||||
// Same as above, but for an AudioMultiVector.
|
||||
static int RampSignal(AudioMultiVector* signal,
|
||||
size_t start_index,
|
||||
size_t length,
|
||||
|
||||
@ -112,25 +112,33 @@ int Expand::Process(AudioMultiVector* output) {
|
||||
// Use only expand_vector0.
|
||||
assert(expansion_vector_position + temp_length <=
|
||||
parameters.expand_vector0.Size());
|
||||
memcpy(voiced_vector_storage,
|
||||
¶meters.expand_vector0[expansion_vector_position],
|
||||
sizeof(int16_t) * temp_length);
|
||||
parameters.expand_vector0.CopyTo(temp_length, expansion_vector_position,
|
||||
voiced_vector_storage);
|
||||
} else if (current_lag_index_ == 1) {
|
||||
std::unique_ptr<int16_t[]> temp_0(new int16_t[temp_length]);
|
||||
parameters.expand_vector0.CopyTo(temp_length, expansion_vector_position,
|
||||
temp_0.get());
|
||||
std::unique_ptr<int16_t[]> temp_1(new int16_t[temp_length]);
|
||||
parameters.expand_vector1.CopyTo(temp_length, expansion_vector_position,
|
||||
temp_1.get());
|
||||
// Mix 3/4 of expand_vector0 with 1/4 of expand_vector1.
|
||||
WebRtcSpl_ScaleAndAddVectorsWithRound(
|
||||
¶meters.expand_vector0[expansion_vector_position], 3,
|
||||
¶meters.expand_vector1[expansion_vector_position], 1, 2,
|
||||
voiced_vector_storage, temp_length);
|
||||
WebRtcSpl_ScaleAndAddVectorsWithRound(temp_0.get(), 3, temp_1.get(), 1, 2,
|
||||
voiced_vector_storage, temp_length);
|
||||
} else if (current_lag_index_ == 2) {
|
||||
// Mix 1/2 of expand_vector0 with 1/2 of expand_vector1.
|
||||
assert(expansion_vector_position + temp_length <=
|
||||
parameters.expand_vector0.Size());
|
||||
assert(expansion_vector_position + temp_length <=
|
||||
parameters.expand_vector1.Size());
|
||||
WebRtcSpl_ScaleAndAddVectorsWithRound(
|
||||
¶meters.expand_vector0[expansion_vector_position], 1,
|
||||
¶meters.expand_vector1[expansion_vector_position], 1, 1,
|
||||
voiced_vector_storage, temp_length);
|
||||
|
||||
std::unique_ptr<int16_t[]> temp_0(new int16_t[temp_length]);
|
||||
parameters.expand_vector0.CopyTo(temp_length, expansion_vector_position,
|
||||
temp_0.get());
|
||||
std::unique_ptr<int16_t[]> temp_1(new int16_t[temp_length]);
|
||||
parameters.expand_vector1.CopyTo(temp_length, expansion_vector_position,
|
||||
temp_1.get());
|
||||
WebRtcSpl_ScaleAndAddVectorsWithRound(temp_0.get(), 1, temp_1.get(), 1, 1,
|
||||
voiced_vector_storage, temp_length);
|
||||
}
|
||||
|
||||
// Get tapering window parameters. Values are in Q15.
|
||||
@ -299,8 +307,7 @@ int Expand::Process(AudioMultiVector* output) {
|
||||
} else {
|
||||
assert(output->Size() == current_lag);
|
||||
}
|
||||
memcpy(&(*output)[channel_ix][0], temp_data,
|
||||
sizeof(temp_data[0]) * current_lag);
|
||||
(*output)[channel_ix].OverwriteAt(temp_data, current_lag, 0);
|
||||
}
|
||||
|
||||
// Increase call number and cap it.
|
||||
@ -384,8 +391,11 @@ void Expand::AnalyzeSignal(int16_t* random_vector) {
|
||||
size_t fs_mult_lpc_analysis_len = fs_mult * kLpcAnalysisLength;
|
||||
|
||||
const size_t signal_length = static_cast<size_t>(256 * fs_mult);
|
||||
const int16_t* audio_history =
|
||||
&(*sync_buffer_)[0][sync_buffer_->Size() - signal_length];
|
||||
|
||||
const size_t audio_history_position = sync_buffer_->Size() - signal_length;
|
||||
std::unique_ptr<int16_t[]> audio_history(new int16_t[signal_length]);
|
||||
(*sync_buffer_)[0].CopyTo(signal_length, audio_history_position,
|
||||
audio_history.get());
|
||||
|
||||
// Initialize.
|
||||
InitializeForAnExpandPeriod();
|
||||
@ -394,7 +404,7 @@ void Expand::AnalyzeSignal(int16_t* random_vector) {
|
||||
size_t correlation_length = 51; // TODO(hlundin): Legacy bit-exactness.
|
||||
// If it is decided to break bit-exactness |correlation_length| should be
|
||||
// initialized to the return value of Correlation().
|
||||
Correlation(audio_history, signal_length, correlation_vector);
|
||||
Correlation(audio_history.get(), signal_length, correlation_vector);
|
||||
|
||||
// Find peaks in correlation vector.
|
||||
DspHelper::PeakDetection(correlation_vector, correlation_length,
|
||||
@ -551,12 +561,14 @@ void Expand::AnalyzeSignal(int16_t* random_vector) {
|
||||
parameters.expand_vector1.Extend(
|
||||
expansion_length - parameters.expand_vector1.Size());
|
||||
}
|
||||
WebRtcSpl_AffineTransformVector(¶meters.expand_vector1[0],
|
||||
std::unique_ptr<int16_t[]> temp_1(new int16_t[expansion_length]);
|
||||
WebRtcSpl_AffineTransformVector(temp_1.get(),
|
||||
const_cast<int16_t*>(vector2),
|
||||
amplitude_ratio,
|
||||
4096,
|
||||
13,
|
||||
expansion_length);
|
||||
parameters.expand_vector1.OverwriteAt(temp_1.get(), expansion_length, 0);
|
||||
} else {
|
||||
// Energy change constraint not fulfilled. Only use last vector.
|
||||
parameters.expand_vector0.Clear();
|
||||
|
||||
@ -93,8 +93,9 @@ class ExpandTest : public ::testing::Test {
|
||||
ASSERT_TRUE(input_file_.Seek(speech_start_samples));
|
||||
|
||||
// Pre-load the sync buffer with speech data.
|
||||
ASSERT_TRUE(
|
||||
input_file_.Read(sync_buffer_.Size(), &sync_buffer_.Channel(0)[0]));
|
||||
std::unique_ptr<int16_t[]> temp(new int16_t[sync_buffer_.Size()]);
|
||||
ASSERT_TRUE(input_file_.Read(sync_buffer_.Size(), temp.get()));
|
||||
sync_buffer_.Channel(0).OverwriteAt(temp.get(), sync_buffer_.Size(), 0);
|
||||
ASSERT_EQ(1u, num_channels_) << "Fix: Must populate all channels.";
|
||||
}
|
||||
|
||||
|
||||
@ -63,11 +63,16 @@ size_t Merge::Process(int16_t* input, size_t input_length,
|
||||
size_t best_correlation_index = 0;
|
||||
size_t output_length = 0;
|
||||
|
||||
std::unique_ptr<int16_t[]> input_channel(
|
||||
new int16_t[input_length_per_channel]);
|
||||
std::unique_ptr<int16_t[]> expanded_channel(new int16_t[expanded_length]);
|
||||
for (size_t channel = 0; channel < num_channels_; ++channel) {
|
||||
int16_t* input_channel = &input_vector[channel][0];
|
||||
int16_t* expanded_channel = &expanded_[channel][0];
|
||||
input_vector[channel].CopyTo(
|
||||
input_length_per_channel, 0, input_channel.get());
|
||||
expanded_[channel].CopyTo(expanded_length, 0, expanded_channel.get());
|
||||
|
||||
int16_t new_mute_factor = SignalScaling(
|
||||
input_channel, input_length_per_channel, expanded_channel);
|
||||
input_channel.get(), input_length_per_channel, expanded_channel.get());
|
||||
|
||||
// Adjust muting factor (product of "main" muting factor and expand muting
|
||||
// factor).
|
||||
@ -85,8 +90,8 @@ size_t Merge::Process(int16_t* input, size_t input_length,
|
||||
// Downsample, correlate, and find strongest correlation period for the
|
||||
// master (i.e., first) channel only.
|
||||
// Downsample to 4kHz sample rate.
|
||||
Downsample(input_channel, input_length_per_channel, expanded_channel,
|
||||
expanded_length);
|
||||
Downsample(input_channel.get(), input_length_per_channel,
|
||||
expanded_channel.get(), expanded_length);
|
||||
|
||||
// Calculate the lag of the strongest correlation period.
|
||||
best_correlation_index = CorrelateAndPeakSearch(
|
||||
@ -108,7 +113,7 @@ size_t Merge::Process(int16_t* input, size_t input_length,
|
||||
// and so on.
|
||||
int increment = 4194 / fs_mult_;
|
||||
*external_mute_factor =
|
||||
static_cast<int16_t>(DspHelper::RampSignal(input_channel,
|
||||
static_cast<int16_t>(DspHelper::RampSignal(input_channel.get(),
|
||||
interpolation_length,
|
||||
*external_mute_factor,
|
||||
increment));
|
||||
@ -128,10 +133,10 @@ size_t Merge::Process(int16_t* input, size_t input_length,
|
||||
int16_t increment =
|
||||
static_cast<int16_t>(16384 / (interpolation_length + 1)); // In Q14.
|
||||
int16_t mute_factor = 16384 - increment;
|
||||
memmove(temp_data_.data(), expanded_channel,
|
||||
memmove(temp_data_.data(), expanded_channel.get(),
|
||||
sizeof(int16_t) * best_correlation_index);
|
||||
DspHelper::CrossFade(&expanded_channel[best_correlation_index],
|
||||
input_channel, interpolation_length,
|
||||
input_channel.get(), interpolation_length,
|
||||
&mute_factor, increment, decoded_output);
|
||||
|
||||
output_length = best_correlation_index + input_length_per_channel;
|
||||
@ -141,8 +146,7 @@ size_t Merge::Process(int16_t* input, size_t input_length,
|
||||
} else {
|
||||
assert(output->Size() == output_length);
|
||||
}
|
||||
memcpy(&(*output)[channel][0], temp_data_.data(),
|
||||
sizeof(temp_data_[0]) * output_length);
|
||||
(*output)[channel].OverwriteAt(temp_data_.data(), output_length, 0);
|
||||
}
|
||||
|
||||
// Copy back the first part of the data to |sync_buffer_| and remove it from
|
||||
|
||||
@ -42,7 +42,6 @@ int Normal::Process(const int16_t* input,
|
||||
return 0;
|
||||
}
|
||||
output->PushBackInterleaved(input, length);
|
||||
int16_t* signal = &(*output)[0][0];
|
||||
|
||||
const int fs_mult = fs_hz_ / 8000;
|
||||
assert(fs_mult > 0);
|
||||
@ -63,24 +62,26 @@ int Normal::Process(const int16_t* input,
|
||||
expand_->Process(&expanded);
|
||||
expand_->Reset();
|
||||
|
||||
size_t length_per_channel = length / output->Channels();
|
||||
std::unique_ptr<int16_t[]> signal(new int16_t[length_per_channel]);
|
||||
for (size_t channel_ix = 0; channel_ix < output->Channels(); ++channel_ix) {
|
||||
// Adjust muting factor (main muting factor times expand muting factor).
|
||||
external_mute_factor_array[channel_ix] = static_cast<int16_t>(
|
||||
(external_mute_factor_array[channel_ix] *
|
||||
expand_->MuteFactor(channel_ix)) >> 14);
|
||||
|
||||
int16_t* signal = &(*output)[channel_ix][0];
|
||||
size_t length_per_channel = length / output->Channels();
|
||||
(*output)[channel_ix].CopyTo(length_per_channel, 0, signal.get());
|
||||
|
||||
// Find largest absolute value in new data.
|
||||
int16_t decoded_max =
|
||||
WebRtcSpl_MaxAbsValueW16(signal, length_per_channel);
|
||||
WebRtcSpl_MaxAbsValueW16(signal.get(), length_per_channel);
|
||||
// Adjust muting factor if needed (to BGN level).
|
||||
size_t energy_length =
|
||||
std::min(static_cast<size_t>(fs_mult * 64), length_per_channel);
|
||||
int scaling = 6 + fs_shift
|
||||
- WebRtcSpl_NormW32(decoded_max * decoded_max);
|
||||
scaling = std::max(scaling, 0); // |scaling| should always be >= 0.
|
||||
int32_t energy = WebRtcSpl_DotProductWithScale(signal, signal,
|
||||
int32_t energy = WebRtcSpl_DotProductWithScale(signal.get(), signal.get(),
|
||||
energy_length, scaling);
|
||||
int32_t scaled_energy_length =
|
||||
static_cast<int32_t>(energy_length >> scaling);
|
||||
@ -159,7 +160,7 @@ int Normal::Process(const int16_t* input,
|
||||
} else {
|
||||
// If no CNG instance is defined, just copy from the decoded data.
|
||||
// (This will result in interpolating the decoded with itself.)
|
||||
memcpy(cng_output, signal, fs_mult * 8 * sizeof(int16_t));
|
||||
(*output)[0].CopyTo(fs_mult * 8, 0, cng_output);
|
||||
}
|
||||
// Interpolate the CNG into the new vector.
|
||||
// (NB/WB/SWB32/SWB48 8/16/32/48 samples.)
|
||||
@ -169,8 +170,8 @@ int Normal::Process(const int16_t* input,
|
||||
for (size_t i = 0; i < static_cast<size_t>(8 * fs_mult); i++) {
|
||||
// TODO(hlundin): Add 16 instead of 8 for correct rounding. Keeping 8 now
|
||||
// for legacy bit-exactness.
|
||||
signal[i] =
|
||||
(fraction * signal[i] + (32 - fraction) * cng_output[i] + 8) >> 5;
|
||||
(*output)[0][i] = (fraction * (*output)[0][i] +
|
||||
(32 - fraction) * cng_output[i] + 8) >> 5;
|
||||
fraction += increment;
|
||||
}
|
||||
} else if (external_mute_factor_array[0] < 16384) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user