1 /*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "modules/audio_coding/neteq/audio_vector.h"
12
13
14 #include <algorithm>
15 #include <memory>
16
17 #include "rtc_base/checks.h"
18
19 namespace webrtc {
20
AudioVector()21 AudioVector::AudioVector() : AudioVector(kDefaultInitialSize) {
22 Clear();
23 }
24
AudioVector(size_t initial_size)25 AudioVector::AudioVector(size_t initial_size)
26 : array_(new int16_t[initial_size + 1]),
27 capacity_(initial_size + 1),
28 begin_index_(0),
29 end_index_(capacity_ - 1) {
30 memset(array_.get(), 0, capacity_ * sizeof(int16_t));
31 }
32
33 AudioVector::~AudioVector() = default;
34
Clear()35 void AudioVector::Clear() {
36 end_index_ = begin_index_ = 0;
37 }
38
CopyTo(AudioVector * copy_to) const39 void AudioVector::CopyTo(AudioVector* copy_to) const {
40 RTC_DCHECK(copy_to);
41 copy_to->Reserve(Size());
42 CopyTo(Size(), 0, copy_to->array_.get());
43 copy_to->begin_index_ = 0;
44 copy_to->end_index_ = Size();
45 }
46
CopyTo(size_t length,size_t position,int16_t * copy_to) const47 void AudioVector::CopyTo(size_t length,
48 size_t position,
49 int16_t* copy_to) const {
50 if (length == 0)
51 return;
52 length = std::min(length, Size() - position);
53 const size_t copy_index = (begin_index_ + position) % capacity_;
54 const size_t first_chunk_length = std::min(length, capacity_ - copy_index);
55 memcpy(copy_to, &array_[copy_index], first_chunk_length * sizeof(int16_t));
56 const size_t remaining_length = length - first_chunk_length;
57 if (remaining_length > 0) {
58 memcpy(©_to[first_chunk_length], array_.get(),
59 remaining_length * sizeof(int16_t));
60 }
61 }
62
PushFront(const AudioVector & prepend_this)63 void AudioVector::PushFront(const AudioVector& prepend_this) {
64 const size_t length = prepend_this.Size();
65 if (length == 0)
66 return;
67
68 // Although the subsequent calling to PushFront does Reserve in it, it is
69 // always more efficient to do a big Reserve first.
70 Reserve(Size() + length);
71
72 const size_t first_chunk_length =
73 std::min(length, prepend_this.capacity_ - prepend_this.begin_index_);
74 const size_t remaining_length = length - first_chunk_length;
75 if (remaining_length > 0)
76 PushFront(prepend_this.array_.get(), remaining_length);
77 PushFront(&prepend_this.array_[prepend_this.begin_index_],
78 first_chunk_length);
79 }
80
PushFront(const int16_t * prepend_this,size_t length)81 void AudioVector::PushFront(const int16_t* prepend_this, size_t length) {
82 if (length == 0)
83 return;
84 Reserve(Size() + length);
85 const size_t first_chunk_length = std::min(length, begin_index_);
86 memcpy(&array_[begin_index_ - first_chunk_length],
87 &prepend_this[length - first_chunk_length],
88 first_chunk_length * sizeof(int16_t));
89 const size_t remaining_length = length - first_chunk_length;
90 if (remaining_length > 0) {
91 memcpy(&array_[capacity_ - remaining_length], prepend_this,
92 remaining_length * sizeof(int16_t));
93 }
94 begin_index_ = (begin_index_ + capacity_ - length) % capacity_;
95 }
96
PushBack(const AudioVector & append_this)97 void AudioVector::PushBack(const AudioVector& append_this) {
98 PushBack(append_this, append_this.Size(), 0);
99 }
100
PushBack(const AudioVector & append_this,size_t length,size_t position)101 void AudioVector::PushBack(const AudioVector& append_this,
102 size_t length,
103 size_t position) {
104 RTC_DCHECK_LE(position, append_this.Size());
105 RTC_DCHECK_LE(length, append_this.Size() - position);
106
107 if (length == 0)
108 return;
109
110 // Although the subsequent calling to PushBack does Reserve in it, it is
111 // always more efficient to do a big Reserve first.
112 Reserve(Size() + length);
113
114 const size_t start_index =
115 (append_this.begin_index_ + position) % append_this.capacity_;
116 const size_t first_chunk_length =
117 std::min(length, append_this.capacity_ - start_index);
118 PushBack(&append_this.array_[start_index], first_chunk_length);
119
120 const size_t remaining_length = length - first_chunk_length;
121 if (remaining_length > 0)
122 PushBack(append_this.array_.get(), remaining_length);
123 }
124
PushBack(const int16_t * append_this,size_t length)125 void AudioVector::PushBack(const int16_t* append_this, size_t length) {
126 if (length == 0)
127 return;
128 Reserve(Size() + length);
129 const size_t first_chunk_length = std::min(length, capacity_ - end_index_);
130 memcpy(&array_[end_index_], append_this,
131 first_chunk_length * sizeof(int16_t));
132 const size_t remaining_length = length - first_chunk_length;
133 if (remaining_length > 0) {
134 memcpy(array_.get(), &append_this[first_chunk_length],
135 remaining_length * sizeof(int16_t));
136 }
137 end_index_ = (end_index_ + length) % capacity_;
138 }
139
PopFront(size_t length)140 void AudioVector::PopFront(size_t length) {
141 if (length == 0)
142 return;
143 length = std::min(length, Size());
144 begin_index_ = (begin_index_ + length) % capacity_;
145 }
146
PopBack(size_t length)147 void AudioVector::PopBack(size_t length) {
148 if (length == 0)
149 return;
150 // Never remove more than what is in the array.
151 length = std::min(length, Size());
152 end_index_ = (end_index_ + capacity_ - length) % capacity_;
153 }
154
Extend(size_t extra_length)155 void AudioVector::Extend(size_t extra_length) {
156 if (extra_length == 0)
157 return;
158 InsertZerosByPushBack(extra_length, Size());
159 }
160
InsertAt(const int16_t * insert_this,size_t length,size_t position)161 void AudioVector::InsertAt(const int16_t* insert_this,
162 size_t length,
163 size_t position) {
164 if (length == 0)
165 return;
166 // Cap the insert position at the current array length.
167 position = std::min(Size(), position);
168
169 // When inserting to a position closer to the beginning, it is more efficient
170 // to insert by pushing front than to insert by pushing back, since less data
171 // will be moved, vice versa.
172 if (position <= Size() - position) {
173 InsertByPushFront(insert_this, length, position);
174 } else {
175 InsertByPushBack(insert_this, length, position);
176 }
177 }
178
InsertZerosAt(size_t length,size_t position)179 void AudioVector::InsertZerosAt(size_t length, size_t position) {
180 if (length == 0)
181 return;
182 // Cap the insert position at the current array length.
183 position = std::min(Size(), position);
184
185 // When inserting to a position closer to the beginning, it is more efficient
186 // to insert by pushing front than to insert by pushing back, since less data
187 // will be moved, vice versa.
188 if (position <= Size() - position) {
189 InsertZerosByPushFront(length, position);
190 } else {
191 InsertZerosByPushBack(length, position);
192 }
193 }
194
OverwriteAt(const AudioVector & insert_this,size_t length,size_t position)195 void AudioVector::OverwriteAt(const AudioVector& insert_this,
196 size_t length,
197 size_t position) {
198 RTC_DCHECK_LE(length, insert_this.Size());
199 if (length == 0)
200 return;
201
202 // Cap the insert position at the current array length.
203 position = std::min(Size(), position);
204
205 // Although the subsequent calling to OverwriteAt does Reserve in it, it is
206 // always more efficient to do a big Reserve first.
207 size_t new_size = std::max(Size(), position + length);
208 Reserve(new_size);
209
210 const size_t first_chunk_length =
211 std::min(length, insert_this.capacity_ - insert_this.begin_index_);
212 OverwriteAt(&insert_this.array_[insert_this.begin_index_], first_chunk_length,
213 position);
214 const size_t remaining_length = length - first_chunk_length;
215 if (remaining_length > 0) {
216 OverwriteAt(insert_this.array_.get(), remaining_length,
217 position + first_chunk_length);
218 }
219 }
220
OverwriteAt(const int16_t * insert_this,size_t length,size_t position)221 void AudioVector::OverwriteAt(const int16_t* insert_this,
222 size_t length,
223 size_t position) {
224 if (length == 0)
225 return;
226 // Cap the insert position at the current array length.
227 position = std::min(Size(), position);
228
229 size_t new_size = std::max(Size(), position + length);
230 Reserve(new_size);
231
232 const size_t overwrite_index = (begin_index_ + position) % capacity_;
233 const size_t first_chunk_length =
234 std::min(length, capacity_ - overwrite_index);
235 memcpy(&array_[overwrite_index], insert_this,
236 first_chunk_length * sizeof(int16_t));
237 const size_t remaining_length = length - first_chunk_length;
238 if (remaining_length > 0) {
239 memcpy(array_.get(), &insert_this[first_chunk_length],
240 remaining_length * sizeof(int16_t));
241 }
242
243 end_index_ = (begin_index_ + new_size) % capacity_;
244 }
245
CrossFade(const AudioVector & append_this,size_t fade_length)246 void AudioVector::CrossFade(const AudioVector& append_this,
247 size_t fade_length) {
248 // Fade length cannot be longer than the current vector or `append_this`.
249 RTC_DCHECK_LE(fade_length, Size());
250 RTC_DCHECK_LE(fade_length, append_this.Size());
251 fade_length = std::min(fade_length, Size());
252 fade_length = std::min(fade_length, append_this.Size());
253 size_t position = Size() - fade_length + begin_index_;
254 // Cross fade the overlapping regions.
255 // `alpha` is the mixing factor in Q14.
256 // TODO(hlundin): Consider skipping +1 in the denominator to produce a
257 // smoother cross-fade, in particular at the end of the fade.
258 int alpha_step = 16384 / (static_cast<int>(fade_length) + 1);
259 int alpha = 16384;
260 for (size_t i = 0; i < fade_length; ++i) {
261 alpha -= alpha_step;
262 array_[(position + i) % capacity_] =
263 (alpha * array_[(position + i) % capacity_] +
264 (16384 - alpha) * append_this[i] + 8192) >>
265 14;
266 }
267 RTC_DCHECK_GE(alpha, 0); // Verify that the slope was correct.
268 // Append what is left of `append_this`.
269 size_t samples_to_push_back = append_this.Size() - fade_length;
270 if (samples_to_push_back > 0)
271 PushBack(append_this, samples_to_push_back, fade_length);
272 }
273
274 // Returns the number of elements in this AudioVector.
Size() const275 size_t AudioVector::Size() const {
276 return (end_index_ + capacity_ - begin_index_) % capacity_;
277 }
278
279 // Returns true if this AudioVector is empty.
Empty() const280 bool AudioVector::Empty() const {
281 return begin_index_ == end_index_;
282 }
283
Reserve(size_t n)284 void AudioVector::Reserve(size_t n) {
285 if (capacity_ > n)
286 return;
287 const size_t length = Size();
288 // Reserve one more sample to remove the ambiguity between empty vector and
289 // full vector. Therefore `begin_index_` == `end_index_` indicates empty
290 // vector, and `begin_index_` == (`end_index_` + 1) % capacity indicates
291 // full vector.
292 std::unique_ptr<int16_t[]> temp_array(new int16_t[n + 1]);
293 CopyTo(length, 0, temp_array.get());
294 array_.swap(temp_array);
295 begin_index_ = 0;
296 end_index_ = length;
297 capacity_ = n + 1;
298 }
299
InsertByPushBack(const int16_t * insert_this,size_t length,size_t position)300 void AudioVector::InsertByPushBack(const int16_t* insert_this,
301 size_t length,
302 size_t position) {
303 const size_t move_chunk_length = Size() - position;
304 std::unique_ptr<int16_t[]> temp_array(nullptr);
305 if (move_chunk_length > 0) {
306 // TODO(minyue): see if it is possible to avoid copying to a buffer.
307 temp_array.reset(new int16_t[move_chunk_length]);
308 CopyTo(move_chunk_length, position, temp_array.get());
309 PopBack(move_chunk_length);
310 }
311
312 Reserve(Size() + length + move_chunk_length);
313 PushBack(insert_this, length);
314 if (move_chunk_length > 0)
315 PushBack(temp_array.get(), move_chunk_length);
316 }
317
InsertByPushFront(const int16_t * insert_this,size_t length,size_t position)318 void AudioVector::InsertByPushFront(const int16_t* insert_this,
319 size_t length,
320 size_t position) {
321 std::unique_ptr<int16_t[]> temp_array(nullptr);
322 if (position > 0) {
323 // TODO(minyue): see if it is possible to avoid copying to a buffer.
324 temp_array.reset(new int16_t[position]);
325 CopyTo(position, 0, temp_array.get());
326 PopFront(position);
327 }
328
329 Reserve(Size() + length + position);
330 PushFront(insert_this, length);
331 if (position > 0)
332 PushFront(temp_array.get(), position);
333 }
334
InsertZerosByPushBack(size_t length,size_t position)335 void AudioVector::InsertZerosByPushBack(size_t length, size_t position) {
336 const size_t move_chunk_length = Size() - position;
337 std::unique_ptr<int16_t[]> temp_array(nullptr);
338 if (move_chunk_length > 0) {
339 temp_array.reset(new int16_t[move_chunk_length]);
340 CopyTo(move_chunk_length, position, temp_array.get());
341 PopBack(move_chunk_length);
342 }
343
344 Reserve(Size() + length + move_chunk_length);
345
346 const size_t first_zero_chunk_length =
347 std::min(length, capacity_ - end_index_);
348 memset(&array_[end_index_], 0, first_zero_chunk_length * sizeof(int16_t));
349 const size_t remaining_zero_length = length - first_zero_chunk_length;
350 if (remaining_zero_length > 0)
351 memset(array_.get(), 0, remaining_zero_length * sizeof(int16_t));
352 end_index_ = (end_index_ + length) % capacity_;
353
354 if (move_chunk_length > 0)
355 PushBack(temp_array.get(), move_chunk_length);
356 }
357
InsertZerosByPushFront(size_t length,size_t position)358 void AudioVector::InsertZerosByPushFront(size_t length, size_t position) {
359 std::unique_ptr<int16_t[]> temp_array(nullptr);
360 if (position > 0) {
361 temp_array.reset(new int16_t[position]);
362 CopyTo(position, 0, temp_array.get());
363 PopFront(position);
364 }
365
366 Reserve(Size() + length + position);
367
368 const size_t first_zero_chunk_length = std::min(length, begin_index_);
369 memset(&array_[begin_index_ - first_zero_chunk_length], 0,
370 first_zero_chunk_length * sizeof(int16_t));
371 const size_t remaining_zero_length = length - first_zero_chunk_length;
372 if (remaining_zero_length > 0)
373 memset(&array_[capacity_ - remaining_zero_length], 0,
374 remaining_zero_length * sizeof(int16_t));
375 begin_index_ = (begin_index_ + capacity_ - length) % capacity_;
376
377 if (position > 0)
378 PushFront(temp_array.get(), position);
379 }
380
381 } // namespace webrtc
382