|  | /* | 
|  | *  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 "modules/audio_coding/neteq/audio_vector.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <memory> | 
|  |  | 
|  | #include "rtc_base/checks.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`. | 
|  | RTC_DCHECK_LE(fade_length, Size()); | 
|  | RTC_DCHECK_LE(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; | 
|  | } | 
|  | RTC_DCHECK_GE(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_; | 
|  | } | 
|  |  | 
|  | 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 |