| /* |
| * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "webrtc/modules/audio_coding/neteq/audio_vector.h" |
| |
| #include <assert.h> |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include "webrtc/base/checks.h" |
| #include "webrtc/typedefs.h" |
| |
| namespace webrtc { |
| |
| AudioVector::AudioVector() |
| : AudioVector(kDefaultInitialSize) { |
| Clear(); |
| } |
| |
| AudioVector::AudioVector(size_t initial_size) |
| : 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() { |
| end_index_ = begin_index_ = 0; |
| } |
| |
| void AudioVector::CopyTo(AudioVector* copy_to) const { |
| 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) { |
| 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) { |
| 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, 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); |
| 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 == 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()); |
| end_index_ = (end_index_ + capacity_ - length) % capacity_; |
| } |
| |
| void AudioVector::Extend(size_t 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) { |
| 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) { |
| InsertByPushFront(insert_this, length, position); |
| } else { |
| InsertByPushBack(insert_this, length, position); |
| } |
| } |
| |
| void AudioVector::InsertZerosAt(size_t length, |
| size_t position) { |
| 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); |
| |
| 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, |
| size_t fade_length) { |
| // Fade length cannot be longer than the current vector or |append_this|. |
| assert(fade_length <= Size()); |
| 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 + 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 |
| // smoother cross-fade, in particular at the end of the fade. |
| int alpha_step = 16384 / (static_cast<int>(fade_length) + 1); |
| int alpha = 16384; |
| for (size_t i = 0; i < fade_length; ++i) { |
| alpha -= alpha_step; |
| 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, samples_to_push_back, fade_length); |
| } |
| |
| // Returns the number of elements in this AudioVector. |
| size_t AudioVector::Size() const { |
| return (end_index_ + capacity_ - begin_index_) % capacity_; |
| } |
| |
| // Returns true if this AudioVector is empty. |
| bool AudioVector::Empty() const { |
| return begin_index_ == end_index_; |
| } |
| |
| const int16_t& AudioVector::operator[](size_t index) const { |
| return array_[(begin_index_ + index) % capacity_]; |
| } |
| |
| int16_t& AudioVector::operator[](size_t index) { |
| return array_[(begin_index_ + index) % capacity_]; |
| } |
| |
| void AudioVector::Reserve(size_t 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 |