mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 13:50:40 +01:00

Usage replaced with stdint.h, rtc_base/system/arch.h and rtc_base/system/unused.h, as appropriate. Bug: webrtc:6854 Change-Id: I97225465d14b969903d92979e2df3c3c05d35f18 Reviewed-on: https://webrtc-review.googlesource.com/90249 Reviewed-by: Niklas Enbom <niklas.enbom@webrtc.org> Reviewed-by: Fredrik Solenberg <solenberg@webrtc.org> Commit-Queue: Niels Moller <nisse@webrtc.org> Cr-Commit-Position: refs/heads/master@{#24100}
382 lines
13 KiB
C++
382 lines
13 KiB
C++
/*
|
|
* 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 <assert.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|.
|
|
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_;
|
|
}
|
|
|
|
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
|