From 79553cb66e4c2d0651a0236e25ad06dd0ee2e59b Mon Sep 17 00:00:00 2001 From: minyue-webrtc Date: Tue, 10 May 2016 19:55:56 +0200 Subject: [PATCH] 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} --- .../audio_coding/neteq/audio_multi_vector.cc | 8 +- .../audio_coding/neteq/audio_vector.cc | 347 ++++++++++++++---- .../modules/audio_coding/neteq/audio_vector.h | 38 +- .../neteq/audio_vector_unittest.cc | 8 - .../audio_coding/neteq/background_noise.cc | 5 +- .../audio_coding/neteq/comfort_noise.cc | 7 +- .../modules/audio_coding/neteq/dsp_helper.cc | 18 +- .../modules/audio_coding/neteq/dsp_helper.h | 7 + webrtc/modules/audio_coding/neteq/expand.cc | 46 ++- .../audio_coding/neteq/expand_unittest.cc | 5 +- webrtc/modules/audio_coding/neteq/merge.cc | 24 +- webrtc/modules/audio_coding/neteq/normal.cc | 17 +- 12 files changed, 399 insertions(+), 131 deletions(-) diff --git a/webrtc/modules/audio_coding/neteq/audio_multi_vector.cc b/webrtc/modules/audio_coding/neteq/audio_multi_vector.cc index bd38c43903..c80909d7b5 100644 --- a/webrtc/modules/audio_coding/neteq/audio_multi_vector.cc +++ b/webrtc/modules/audio_coding/neteq/audio_multi_vector.cc @@ -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); } } } diff --git a/webrtc/modules/audio_coding/neteq/audio_vector.cc b/webrtc/modules/audio_coding/neteq/audio_vector.cc index 013e1d89ad..ea737a5542 100644 --- a/webrtc/modules/audio_coding/neteq/audio_vector.cc +++ b/webrtc/modules/audio_coding/neteq/audio_vector.cc @@ -15,124 +15,236 @@ #include #include +#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 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 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 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 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 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 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 diff --git a/webrtc/modules/audio_coding/neteq/audio_vector.h b/webrtc/modules/audio_coding/neteq/audio_vector.h index 15297f9bc8..756292aa78 100644 --- a/webrtc/modules/audio_coding/neteq/audio_vector.h +++ b/webrtc/modules/audio_coding/neteq/audio_vector.h @@ -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 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); }; diff --git a/webrtc/modules/audio_coding/neteq/audio_vector_unittest.cc b/webrtc/modules/audio_coding/neteq/audio_vector_unittest.cc index 0800986345..cee7e58669 100644 --- a/webrtc/modules/audio_coding/neteq/audio_vector_unittest.cc +++ b/webrtc/modules/audio_coding/neteq/audio_vector_unittest.cc @@ -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; diff --git a/webrtc/modules/audio_coding/neteq/background_noise.cc b/webrtc/modules/audio_coding/neteq/background_noise.cc index c86045eed9..9cfd6cb40e 100644 --- a/webrtc/modules/audio_coding/neteq/background_noise.cc +++ b/webrtc/modules/audio_coding/neteq/background_noise.cc @@ -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); diff --git a/webrtc/modules/audio_coding/neteq/comfort_noise.cc b/webrtc/modules/audio_coding/neteq/comfort_noise.cc index 2a512bd750..90b02daf71 100644 --- a/webrtc/modules/audio_coding/neteq/comfort_noise.cc +++ b/webrtc/modules/audio_coding/neteq/comfort_noise.cc @@ -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 temp(new int16_t[number_of_samples]); if (!cng_decoder->Generate( - rtc::ArrayView(&(*output)[0][0], number_of_samples), + rtc::ArrayView(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. diff --git a/webrtc/modules/audio_coding/neteq/dsp_helper.cc b/webrtc/modules/audio_coding/neteq/dsp_helper.cc index 4188914c86..3275665094 100644 --- a/webrtc/modules/audio_coding/neteq/dsp_helper.cc +++ b/webrtc/modules/audio_coding/neteq/dsp_helper.cc @@ -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; } diff --git a/webrtc/modules/audio_coding/neteq/dsp_helper.h b/webrtc/modules/audio_coding/neteq/dsp_helper.h index 269c2eb0f2..23543fe383 100644 --- a/webrtc/modules/audio_coding/neteq/dsp_helper.h +++ b/webrtc/modules/audio_coding/neteq/dsp_helper.h @@ -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, diff --git a/webrtc/modules/audio_coding/neteq/expand.cc b/webrtc/modules/audio_coding/neteq/expand.cc index 4c17fd558d..963f4bdb6c 100644 --- a/webrtc/modules/audio_coding/neteq/expand.cc +++ b/webrtc/modules/audio_coding/neteq/expand.cc @@ -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 temp_0(new int16_t[temp_length]); + parameters.expand_vector0.CopyTo(temp_length, expansion_vector_position, + temp_0.get()); + std::unique_ptr 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 temp_0(new int16_t[temp_length]); + parameters.expand_vector0.CopyTo(temp_length, expansion_vector_position, + temp_0.get()); + std::unique_ptr 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(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 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 temp_1(new int16_t[expansion_length]); + WebRtcSpl_AffineTransformVector(temp_1.get(), const_cast(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(); diff --git a/webrtc/modules/audio_coding/neteq/expand_unittest.cc b/webrtc/modules/audio_coding/neteq/expand_unittest.cc index cbc6e47562..f19487ab17 100644 --- a/webrtc/modules/audio_coding/neteq/expand_unittest.cc +++ b/webrtc/modules/audio_coding/neteq/expand_unittest.cc @@ -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 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."; } diff --git a/webrtc/modules/audio_coding/neteq/merge.cc b/webrtc/modules/audio_coding/neteq/merge.cc index 94db1129cd..299682f60d 100644 --- a/webrtc/modules/audio_coding/neteq/merge.cc +++ b/webrtc/modules/audio_coding/neteq/merge.cc @@ -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 input_channel( + new int16_t[input_length_per_channel]); + std::unique_ptr 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(DspHelper::RampSignal(input_channel, + static_cast(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(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 diff --git a/webrtc/modules/audio_coding/neteq/normal.cc b/webrtc/modules/audio_coding/neteq/normal.cc index 4400afb3b0..f99b3f200f 100644 --- a/webrtc/modules/audio_coding/neteq/normal.cc +++ b/webrtc/modules/audio_coding/neteq/normal.cc @@ -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 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( (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(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(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(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) {