andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2013 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 | // Modified from the Chromium original: |
| 12 | // src/media/base/sinc_resampler.cc |
| 13 | |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 14 | // Initial input buffer layout, dividing into regions r0_ to r4_ (note: r0_, r3_ |
| 15 | // and r4_ will move after the first load): |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 16 | // |
| 17 | // |----------------|-----------------------------------------|----------------| |
| 18 | // |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 19 | // request_frames_ |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 20 | // <---------------------------------------------------------> |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 21 | // r0_ (during first load) |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 22 | // |
| 23 | // kKernelSize / 2 kKernelSize / 2 kKernelSize / 2 kKernelSize / 2 |
| 24 | // <---------------> <---------------> <---------------> <---------------> |
| 25 | // r1_ r2_ r3_ r4_ |
| 26 | // |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 27 | // block_size_ == r4_ - r2_ |
| 28 | // <---------------------------------------> |
| 29 | // |
| 30 | // request_frames_ |
| 31 | // <------------------ ... -----------------> |
| 32 | // r0_ (during second load) |
| 33 | // |
| 34 | // On the second request r0_ slides to the right by kKernelSize / 2 and r3_, r4_ |
| 35 | // and block_size_ are reinitialized via step (3) in the algorithm below. |
| 36 | // |
| 37 | // These new regions remain constant until a Flush() occurs. While complicated, |
| 38 | // this allows us to reduce jitter by always requesting the same amount from the |
| 39 | // provided callback. |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 40 | // |
| 41 | // The algorithm: |
| 42 | // |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 43 | // 1) Allocate input_buffer of size: request_frames_ + kKernelSize; this ensures |
| 44 | // there's enough room to read request_frames_ from the callback into region |
| 45 | // r0_ (which will move between the first and subsequent passes). |
| 46 | // |
| 47 | // 2) Let r1_, r2_ each represent half the kernel centered around r0_: |
| 48 | // |
| 49 | // r0_ = input_buffer_ + kKernelSize / 2 |
| 50 | // r1_ = input_buffer_ |
| 51 | // r2_ = r0_ |
| 52 | // |
| 53 | // r0_ is always request_frames_ in size. r1_, r2_ are kKernelSize / 2 in |
| 54 | // size. r1_ must be zero initialized to avoid convolution with garbage (see |
| 55 | // step (5) for why). |
| 56 | // |
| 57 | // 3) Let r3_, r4_ each represent half the kernel right aligned with the end of |
| 58 | // r0_ and choose block_size_ as the distance in frames between r4_ and r2_: |
| 59 | // |
| 60 | // r3_ = r0_ + request_frames_ - kKernelSize |
| 61 | // r4_ = r0_ + request_frames_ - kKernelSize / 2 |
| 62 | // block_size_ = r4_ - r2_ = request_frames_ - kKernelSize / 2 |
| 63 | // |
| 64 | // 4) Consume request_frames_ frames into r0_. |
| 65 | // |
| 66 | // 5) Position kernel centered at start of r2_ and generate output frames until |
| 67 | // the kernel is centered at the start of r4_ or we've finished generating |
| 68 | // all the output frames. |
| 69 | // |
| 70 | // 6) Wrap left over data from the r3_ to r1_ and r4_ to r2_. |
| 71 | // |
| 72 | // 7) If we're on the second load, in order to avoid overwriting the frames we |
| 73 | // just wrapped from r4_ we need to slide r0_ to the right by the size of |
| 74 | // r4_, which is kKernelSize / 2: |
| 75 | // |
| 76 | // r0_ = r0_ + kKernelSize / 2 = input_buffer_ + kKernelSize |
| 77 | // |
| 78 | // r3_, r4_, and block_size_ then need to be reinitialized, so goto (3). |
| 79 | // |
| 80 | // 8) Else, if we're not on the second load, goto (4). |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 81 | // |
| 82 | // Note: we're glossing over how the sub-sample handling works with |
| 83 | // |virtual_source_idx_|, etc. |
| 84 | |
| 85 | // MSVC++ requires this to be set before any other includes to get M_PI. |
| 86 | #define _USE_MATH_DEFINES |
| 87 | |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 88 | #include "webrtc/common_audio/resampler/sinc_resampler.h" |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 89 | |
pbos@webrtc.org | 12dc1a3 | 2013-08-05 16:22:53 +0000 | [diff] [blame] | 90 | #include <math.h> |
| 91 | #include <string.h> |
| 92 | |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 93 | #include <limits> |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 94 | |
Edward Lemur | c20978e | 2017-07-06 19:44:34 +0200 | [diff] [blame] | 95 | #include "webrtc/rtc_base/checks.h" |
Henrik Kjellander | 98f5351 | 2015-10-28 18:17:40 +0100 | [diff] [blame] | 96 | #include "webrtc/system_wrappers/include/cpu_features_wrapper.h" |
Peter Kasting | 728d903 | 2015-06-11 14:31:38 -0700 | [diff] [blame] | 97 | #include "webrtc/typedefs.h" |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 98 | |
Peter Kasting | 728d903 | 2015-06-11 14:31:38 -0700 | [diff] [blame] | 99 | namespace webrtc { |
pkasting | b297c5a | 2015-07-22 15:17:22 -0700 | [diff] [blame] | 100 | |
Peter Kasting | 728d903 | 2015-06-11 14:31:38 -0700 | [diff] [blame] | 101 | namespace { |
| 102 | |
| 103 | double SincScaleFactor(double io_ratio) { |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 104 | // |sinc_scale_factor| is basically the normalized cutoff frequency of the |
| 105 | // low-pass filter. |
| 106 | double sinc_scale_factor = io_ratio > 1.0 ? 1.0 / io_ratio : 1.0; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 107 | |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 108 | // The sinc function is an idealized brick-wall filter, but since we're |
| 109 | // windowing it the transition from pass to stop does not happen right away. |
| 110 | // So we should adjust the low pass filter cutoff slightly downward to avoid |
| 111 | // some aliasing at the very high-end. |
| 112 | // TODO(crogers): this value is empirical and to be more exact should vary |
| 113 | // depending on kKernelSize. |
| 114 | sinc_scale_factor *= 0.9; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 115 | |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 116 | return sinc_scale_factor; |
| 117 | } |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 118 | |
Peter Kasting | 728d903 | 2015-06-11 14:31:38 -0700 | [diff] [blame] | 119 | } // namespace |
| 120 | |
kwiberg | b890c95c | 2016-11-29 05:30:40 -0800 | [diff] [blame] | 121 | const size_t SincResampler::kKernelSize; |
| 122 | |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 123 | // If we know the minimum architecture at compile time, avoid CPU detection. |
andrew@webrtc.org | 00073aa | 2014-02-27 04:12:34 +0000 | [diff] [blame] | 124 | #if defined(WEBRTC_ARCH_X86_FAMILY) |
| 125 | #if defined(__SSE2__) |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 126 | #define CONVOLVE_FUNC Convolve_SSE |
| 127 | void SincResampler::InitializeCPUSpecificFeatures() {} |
| 128 | #else |
andrew@webrtc.org | 00073aa | 2014-02-27 04:12:34 +0000 | [diff] [blame] | 129 | // x86 CPU detection required. Function will be set by |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 130 | // InitializeCPUSpecificFeatures(). |
| 131 | // TODO(dalecurtis): Once Chrome moves to an SSE baseline this can be removed. |
| 132 | #define CONVOLVE_FUNC convolve_proc_ |
| 133 | |
| 134 | void SincResampler::InitializeCPUSpecificFeatures() { |
| 135 | convolve_proc_ = WebRtc_GetCPUInfo(kSSE2) ? Convolve_SSE : Convolve_C; |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 136 | } |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 137 | #endif |
Andrew MacDonald | cb7f8ce | 2015-05-19 22:20:17 -0700 | [diff] [blame] | 138 | #elif defined(WEBRTC_HAS_NEON) |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 139 | #define CONVOLVE_FUNC Convolve_NEON |
| 140 | void SincResampler::InitializeCPUSpecificFeatures() {} |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 141 | #else |
| 142 | // Unknown architecture. |
| 143 | #define CONVOLVE_FUNC Convolve_C |
| 144 | void SincResampler::InitializeCPUSpecificFeatures() {} |
| 145 | #endif |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 146 | |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 147 | SincResampler::SincResampler(double io_sample_rate_ratio, |
Peter Kasting | dce40cf | 2015-08-24 14:52:23 -0700 | [diff] [blame] | 148 | size_t request_frames, |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 149 | SincResamplerCallback* read_cb) |
| 150 | : io_sample_rate_ratio_(io_sample_rate_ratio), |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 151 | read_cb_(read_cb), |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 152 | request_frames_(request_frames), |
| 153 | input_buffer_size_(request_frames_ + kKernelSize), |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 154 | // Create input buffers with a 16-byte alignment for SSE optimizations. |
| 155 | kernel_storage_(static_cast<float*>( |
| 156 | AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))), |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 157 | kernel_pre_sinc_storage_(static_cast<float*>( |
| 158 | AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))), |
| 159 | kernel_window_storage_(static_cast<float*>( |
| 160 | AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))), |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 161 | input_buffer_(static_cast<float*>( |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 162 | AlignedMalloc(sizeof(float) * input_buffer_size_, 16))), |
kjellander | bdf3072 | 2017-09-08 11:00:21 -0700 | [diff] [blame] | 163 | #if defined(WEBRTC_CPU_DETECTION) |
deadbeef | 922246a | 2017-02-26 04:18:12 -0800 | [diff] [blame] | 164 | convolve_proc_(nullptr), |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 165 | #endif |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 166 | r1_(input_buffer_.get()), |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 167 | r2_(input_buffer_.get() + kKernelSize / 2) { |
kjellander | bdf3072 | 2017-09-08 11:00:21 -0700 | [diff] [blame] | 168 | #if defined(WEBRTC_CPU_DETECTION) |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 169 | InitializeCPUSpecificFeatures(); |
kwiberg | b890c95c | 2016-11-29 05:30:40 -0800 | [diff] [blame] | 170 | RTC_DCHECK(convolve_proc_); |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 171 | #endif |
kwiberg | b890c95c | 2016-11-29 05:30:40 -0800 | [diff] [blame] | 172 | RTC_DCHECK_GT(request_frames_, 0); |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 173 | Flush(); |
kwiberg | b890c95c | 2016-11-29 05:30:40 -0800 | [diff] [blame] | 174 | RTC_DCHECK_GT(block_size_, kKernelSize); |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 175 | |
| 176 | memset(kernel_storage_.get(), 0, |
| 177 | sizeof(*kernel_storage_.get()) * kKernelStorageSize); |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 178 | memset(kernel_pre_sinc_storage_.get(), 0, |
| 179 | sizeof(*kernel_pre_sinc_storage_.get()) * kKernelStorageSize); |
| 180 | memset(kernel_window_storage_.get(), 0, |
| 181 | sizeof(*kernel_window_storage_.get()) * kKernelStorageSize); |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 182 | |
| 183 | InitializeKernel(); |
| 184 | } |
| 185 | |
| 186 | SincResampler::~SincResampler() {} |
| 187 | |
| 188 | void SincResampler::UpdateRegions(bool second_load) { |
| 189 | // Setup various region pointers in the buffer (see diagram above). If we're |
| 190 | // on the second load we need to slide r0_ to the right by kKernelSize / 2. |
| 191 | r0_ = input_buffer_.get() + (second_load ? kKernelSize : kKernelSize / 2); |
| 192 | r3_ = r0_ + request_frames_ - kKernelSize; |
| 193 | r4_ = r0_ + request_frames_ - kKernelSize / 2; |
| 194 | block_size_ = r4_ - r2_; |
| 195 | |
| 196 | // r1_ at the beginning of the buffer. |
kwiberg | b890c95c | 2016-11-29 05:30:40 -0800 | [diff] [blame] | 197 | RTC_DCHECK_EQ(r1_, input_buffer_.get()); |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 198 | // r1_ left of r2_, r4_ left of r3_ and size correct. |
kwiberg | b890c95c | 2016-11-29 05:30:40 -0800 | [diff] [blame] | 199 | RTC_DCHECK_EQ(r2_ - r1_, r4_ - r3_); |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 200 | // r2_ left of r3. |
kwiberg | b890c95c | 2016-11-29 05:30:40 -0800 | [diff] [blame] | 201 | RTC_DCHECK_LT(r2_, r3_); |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 202 | } |
| 203 | |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 204 | void SincResampler::InitializeKernel() { |
| 205 | // Blackman window parameters. |
| 206 | static const double kAlpha = 0.16; |
| 207 | static const double kA0 = 0.5 * (1.0 - kAlpha); |
| 208 | static const double kA1 = 0.5; |
| 209 | static const double kA2 = 0.5 * kAlpha; |
| 210 | |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 211 | // Generates a set of windowed sinc() kernels. |
| 212 | // We generate a range of sub-sample offsets from 0.0 to 1.0. |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 213 | const double sinc_scale_factor = SincScaleFactor(io_sample_rate_ratio_); |
Peter Kasting | dce40cf | 2015-08-24 14:52:23 -0700 | [diff] [blame] | 214 | for (size_t offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) { |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 215 | const float subsample_offset = |
| 216 | static_cast<float>(offset_idx) / kKernelOffsetCount; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 217 | |
Peter Kasting | dce40cf | 2015-08-24 14:52:23 -0700 | [diff] [blame] | 218 | for (size_t i = 0; i < kKernelSize; ++i) { |
| 219 | const size_t idx = i + offset_idx * kKernelSize; |
| 220 | const float pre_sinc = static_cast<float>(M_PI * |
| 221 | (static_cast<int>(i) - static_cast<int>(kKernelSize / 2) - |
| 222 | subsample_offset)); |
andrew@webrtc.org | 2038920 | 2014-02-26 18:14:54 +0000 | [diff] [blame] | 223 | kernel_pre_sinc_storage_[idx] = pre_sinc; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 224 | |
| 225 | // Compute Blackman window, matching the offset of the sinc(). |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 226 | const float x = (i - subsample_offset) / kKernelSize; |
bjornv@webrtc.org | 3cbd6c2 | 2014-09-04 13:21:44 +0000 | [diff] [blame] | 227 | const float window = static_cast<float>(kA0 - kA1 * cos(2.0 * M_PI * x) + |
| 228 | kA2 * cos(4.0 * M_PI * x)); |
andrew@webrtc.org | 2038920 | 2014-02-26 18:14:54 +0000 | [diff] [blame] | 229 | kernel_window_storage_[idx] = window; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 230 | |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 231 | // Compute the sinc with offset, then window the sinc() function and store |
| 232 | // at the correct offset. |
bjornv@webrtc.org | 3cbd6c2 | 2014-09-04 13:21:44 +0000 | [diff] [blame] | 233 | kernel_storage_[idx] = static_cast<float>(window * |
| 234 | ((pre_sinc == 0) ? |
| 235 | sinc_scale_factor : |
| 236 | (sin(sinc_scale_factor * pre_sinc) / pre_sinc))); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 237 | } |
| 238 | } |
| 239 | } |
| 240 | |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 241 | void SincResampler::SetRatio(double io_sample_rate_ratio) { |
| 242 | if (fabs(io_sample_rate_ratio_ - io_sample_rate_ratio) < |
| 243 | std::numeric_limits<double>::epsilon()) { |
| 244 | return; |
| 245 | } |
| 246 | |
| 247 | io_sample_rate_ratio_ = io_sample_rate_ratio; |
| 248 | |
| 249 | // Optimize reinitialization by reusing values which are independent of |
| 250 | // |sinc_scale_factor|. Provides a 3x speedup. |
| 251 | const double sinc_scale_factor = SincScaleFactor(io_sample_rate_ratio_); |
Peter Kasting | dce40cf | 2015-08-24 14:52:23 -0700 | [diff] [blame] | 252 | for (size_t offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) { |
| 253 | for (size_t i = 0; i < kKernelSize; ++i) { |
| 254 | const size_t idx = i + offset_idx * kKernelSize; |
andrew@webrtc.org | 2038920 | 2014-02-26 18:14:54 +0000 | [diff] [blame] | 255 | const float window = kernel_window_storage_[idx]; |
| 256 | const float pre_sinc = kernel_pre_sinc_storage_[idx]; |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 257 | |
bjornv@webrtc.org | 3cbd6c2 | 2014-09-04 13:21:44 +0000 | [diff] [blame] | 258 | kernel_storage_[idx] = static_cast<float>(window * |
| 259 | ((pre_sinc == 0) ? |
| 260 | sinc_scale_factor : |
| 261 | (sin(sinc_scale_factor * pre_sinc) / pre_sinc))); |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 262 | } |
| 263 | } |
| 264 | } |
| 265 | |
Peter Kasting | dce40cf | 2015-08-24 14:52:23 -0700 | [diff] [blame] | 266 | void SincResampler::Resample(size_t frames, float* destination) { |
| 267 | size_t remaining_frames = frames; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 268 | |
| 269 | // Step (1) -- Prime the input buffer at the start of the input stream. |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 270 | if (!buffer_primed_ && remaining_frames) { |
| 271 | read_cb_->Run(request_frames_, r0_); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 272 | buffer_primed_ = true; |
| 273 | } |
| 274 | |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 275 | // Step (2) -- Resample! const what we can outside of the loop for speed. It |
| 276 | // actually has an impact on ARM performance. See inner loop comment below. |
| 277 | const double current_io_ratio = io_sample_rate_ratio_; |
| 278 | const float* const kernel_ptr = kernel_storage_.get(); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 279 | while (remaining_frames) { |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 280 | // |i| may be negative if the last Resample() call ended on an iteration |
| 281 | // that put |virtual_source_idx_| over the limit. |
| 282 | // |
| 283 | // Note: The loop construct here can severely impact performance on ARM |
| 284 | // or when built with clang. See https://codereview.chromium.org/18566009/ |
bjornv@webrtc.org | 3cbd6c2 | 2014-09-04 13:21:44 +0000 | [diff] [blame] | 285 | for (int i = static_cast<int>( |
| 286 | ceil((block_size_ - virtual_source_idx_) / current_io_ratio)); |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 287 | i > 0; --i) { |
kwiberg | b890c95c | 2016-11-29 05:30:40 -0800 | [diff] [blame] | 288 | RTC_DCHECK_LT(virtual_source_idx_, block_size_); |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 289 | |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 290 | // |virtual_source_idx_| lies in between two kernel offsets so figure out |
| 291 | // what they are. |
bjornv@webrtc.org | 3cbd6c2 | 2014-09-04 13:21:44 +0000 | [diff] [blame] | 292 | const int source_idx = static_cast<int>(virtual_source_idx_); |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 293 | const double subsample_remainder = virtual_source_idx_ - source_idx; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 294 | |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 295 | const double virtual_offset_idx = |
| 296 | subsample_remainder * kKernelOffsetCount; |
bjornv@webrtc.org | 3cbd6c2 | 2014-09-04 13:21:44 +0000 | [diff] [blame] | 297 | const int offset_idx = static_cast<int>(virtual_offset_idx); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 298 | |
| 299 | // We'll compute "convolutions" for the two kernels which straddle |
| 300 | // |virtual_source_idx_|. |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 301 | const float* const k1 = kernel_ptr + offset_idx * kKernelSize; |
| 302 | const float* const k2 = k1 + kKernelSize; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 303 | |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 304 | // Ensure |k1|, |k2| are 16-byte aligned for SIMD usage. Should always be |
| 305 | // true so long as kKernelSize is a multiple of 16. |
kwiberg | b890c95c | 2016-11-29 05:30:40 -0800 | [diff] [blame] | 306 | RTC_DCHECK_EQ(0, reinterpret_cast<uintptr_t>(k1) % 16); |
| 307 | RTC_DCHECK_EQ(0, reinterpret_cast<uintptr_t>(k2) % 16); |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 308 | |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 309 | // Initialize input pointer based on quantized |virtual_source_idx_|. |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 310 | const float* const input_ptr = r1_ + source_idx; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 311 | |
| 312 | // Figure out how much to weight each kernel's "convolution". |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 313 | const double kernel_interpolation_factor = |
| 314 | virtual_offset_idx - offset_idx; |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 315 | *destination++ = CONVOLVE_FUNC( |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 316 | input_ptr, k1, k2, kernel_interpolation_factor); |
| 317 | |
| 318 | // Advance the virtual index. |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 319 | virtual_source_idx_ += current_io_ratio; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 320 | |
| 321 | if (!--remaining_frames) |
| 322 | return; |
| 323 | } |
| 324 | |
| 325 | // Wrap back around to the start. |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 326 | virtual_source_idx_ -= block_size_; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 327 | |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 328 | // Step (3) -- Copy r3_, r4_ to r1_, r2_. |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 329 | // This wraps the last input frames back to the start of the buffer. |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 330 | memcpy(r1_, r3_, sizeof(*input_buffer_.get()) * kKernelSize); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 331 | |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 332 | // Step (4) -- Reinitialize regions if necessary. |
| 333 | if (r0_ == r2_) |
| 334 | UpdateRegions(true); |
| 335 | |
| 336 | // Step (5) -- Refresh the buffer with more input. |
| 337 | read_cb_->Run(request_frames_, r0_); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 338 | } |
| 339 | } |
| 340 | |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 341 | #undef CONVOLVE_FUNC |
| 342 | |
Peter Kasting | dce40cf | 2015-08-24 14:52:23 -0700 | [diff] [blame] | 343 | size_t SincResampler::ChunkSize() const { |
| 344 | return static_cast<size_t>(block_size_ / io_sample_rate_ratio_); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 345 | } |
| 346 | |
| 347 | void SincResampler::Flush() { |
| 348 | virtual_source_idx_ = 0; |
| 349 | buffer_primed_ = false; |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 350 | memset(input_buffer_.get(), 0, |
| 351 | sizeof(*input_buffer_.get()) * input_buffer_size_); |
| 352 | UpdateRegions(false); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 353 | } |
| 354 | |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 355 | float SincResampler::Convolve_C(const float* input_ptr, const float* k1, |
| 356 | const float* k2, |
| 357 | double kernel_interpolation_factor) { |
| 358 | float sum1 = 0; |
| 359 | float sum2 = 0; |
| 360 | |
| 361 | // Generate a single output sample. Unrolling this loop hurt performance in |
| 362 | // local testing. |
Peter Kasting | dce40cf | 2015-08-24 14:52:23 -0700 | [diff] [blame] | 363 | size_t n = kKernelSize; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 364 | while (n--) { |
| 365 | sum1 += *input_ptr * *k1++; |
| 366 | sum2 += *input_ptr++ * *k2++; |
| 367 | } |
| 368 | |
| 369 | // Linearly interpolate the two "convolutions". |
bjornv@webrtc.org | 3cbd6c2 | 2014-09-04 13:21:44 +0000 | [diff] [blame] | 370 | return static_cast<float>((1.0 - kernel_interpolation_factor) * sum1 + |
| 371 | kernel_interpolation_factor * sum2); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 372 | } |
| 373 | |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 374 | } // namespace webrtc |