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