xref: /aosp_15_r20/external/webrtc/modules/audio_coding/neteq/audio_vector.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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(&copy_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