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 | |
henrike@webrtc.org | f2aafe4 | 2014-04-29 17:54:17 +0000 | [diff] [blame] | 90 | #include <assert.h> |
pbos@webrtc.org | 12dc1a3 | 2013-08-05 16:22:53 +0000 | [diff] [blame] | 91 | #include <math.h> |
| 92 | #include <string.h> |
| 93 | |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 94 | #include <limits> |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 95 | |
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 | |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 121 | // 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] | 122 | #if defined(WEBRTC_ARCH_X86_FAMILY) |
| 123 | #if defined(__SSE2__) |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 124 | #define CONVOLVE_FUNC Convolve_SSE |
| 125 | void SincResampler::InitializeCPUSpecificFeatures() {} |
| 126 | #else |
andrew@webrtc.org | 00073aa | 2014-02-27 04:12:34 +0000 | [diff] [blame] | 127 | // x86 CPU detection required. Function will be set by |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 128 | // InitializeCPUSpecificFeatures(). |
| 129 | // TODO(dalecurtis): Once Chrome moves to an SSE baseline this can be removed. |
| 130 | #define CONVOLVE_FUNC convolve_proc_ |
| 131 | |
| 132 | void SincResampler::InitializeCPUSpecificFeatures() { |
| 133 | convolve_proc_ = WebRtc_GetCPUInfo(kSSE2) ? Convolve_SSE : Convolve_C; |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 134 | } |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 135 | #endif |
Andrew MacDonald | cb7f8ce | 2015-05-19 22:20:17 -0700 | [diff] [blame] | 136 | #elif defined(WEBRTC_HAS_NEON) |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 137 | #define CONVOLVE_FUNC Convolve_NEON |
| 138 | void SincResampler::InitializeCPUSpecificFeatures() {} |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 139 | #else |
| 140 | // Unknown architecture. |
| 141 | #define CONVOLVE_FUNC Convolve_C |
| 142 | void SincResampler::InitializeCPUSpecificFeatures() {} |
| 143 | #endif |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 144 | |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 145 | SincResampler::SincResampler(double io_sample_rate_ratio, |
Peter Kasting | dce40cf | 2015-08-24 14:52:23 -0700 | [diff] [blame] | 146 | size_t request_frames, |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 147 | SincResamplerCallback* read_cb) |
| 148 | : io_sample_rate_ratio_(io_sample_rate_ratio), |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 149 | read_cb_(read_cb), |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 150 | request_frames_(request_frames), |
| 151 | input_buffer_size_(request_frames_ + kKernelSize), |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 152 | // Create input buffers with a 16-byte alignment for SSE optimizations. |
| 153 | kernel_storage_(static_cast<float*>( |
| 154 | AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))), |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 155 | kernel_pre_sinc_storage_(static_cast<float*>( |
| 156 | AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))), |
| 157 | kernel_window_storage_(static_cast<float*>( |
| 158 | AlignedMalloc(sizeof(float) * kKernelStorageSize, 16))), |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 159 | input_buffer_(static_cast<float*>( |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 160 | AlignedMalloc(sizeof(float) * input_buffer_size_, 16))), |
andrew@webrtc.org | 00073aa | 2014-02-27 04:12:34 +0000 | [diff] [blame] | 161 | #if defined(WEBRTC_CPU_DETECTION) |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 162 | convolve_proc_(NULL), |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 163 | #endif |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 164 | r1_(input_buffer_.get()), |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 165 | r2_(input_buffer_.get() + kKernelSize / 2) { |
andrew@webrtc.org | 00073aa | 2014-02-27 04:12:34 +0000 | [diff] [blame] | 166 | #if defined(WEBRTC_CPU_DETECTION) |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 167 | InitializeCPUSpecificFeatures(); |
| 168 | assert(convolve_proc_); |
| 169 | #endif |
| 170 | assert(request_frames_ > 0); |
| 171 | Flush(); |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 172 | assert(block_size_ > kKernelSize); |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 173 | |
| 174 | memset(kernel_storage_.get(), 0, |
| 175 | sizeof(*kernel_storage_.get()) * kKernelStorageSize); |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 176 | memset(kernel_pre_sinc_storage_.get(), 0, |
| 177 | sizeof(*kernel_pre_sinc_storage_.get()) * kKernelStorageSize); |
| 178 | memset(kernel_window_storage_.get(), 0, |
| 179 | sizeof(*kernel_window_storage_.get()) * kKernelStorageSize); |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 180 | |
| 181 | InitializeKernel(); |
| 182 | } |
| 183 | |
| 184 | SincResampler::~SincResampler() {} |
| 185 | |
| 186 | void SincResampler::UpdateRegions(bool second_load) { |
| 187 | // Setup various region pointers in the buffer (see diagram above). If we're |
| 188 | // on the second load we need to slide r0_ to the right by kKernelSize / 2. |
| 189 | r0_ = input_buffer_.get() + (second_load ? kKernelSize : kKernelSize / 2); |
| 190 | r3_ = r0_ + request_frames_ - kKernelSize; |
| 191 | r4_ = r0_ + request_frames_ - kKernelSize / 2; |
| 192 | block_size_ = r4_ - r2_; |
| 193 | |
| 194 | // r1_ at the beginning of the buffer. |
| 195 | assert(r1_ == input_buffer_.get()); |
| 196 | // r1_ left of r2_, r4_ left of r3_ and size correct. |
| 197 | assert(r2_ - r1_ == r4_ - r3_); |
| 198 | // r2_ left of r3. |
| 199 | assert(r2_ < r3_); |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 200 | } |
| 201 | |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 202 | void SincResampler::InitializeKernel() { |
| 203 | // Blackman window parameters. |
| 204 | static const double kAlpha = 0.16; |
| 205 | static const double kA0 = 0.5 * (1.0 - kAlpha); |
| 206 | static const double kA1 = 0.5; |
| 207 | static const double kA2 = 0.5 * kAlpha; |
| 208 | |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 209 | // Generates a set of windowed sinc() kernels. |
| 210 | // 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] | 211 | const double sinc_scale_factor = SincScaleFactor(io_sample_rate_ratio_); |
Peter Kasting | dce40cf | 2015-08-24 14:52:23 -0700 | [diff] [blame] | 212 | for (size_t offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) { |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 213 | const float subsample_offset = |
| 214 | static_cast<float>(offset_idx) / kKernelOffsetCount; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 215 | |
Peter Kasting | dce40cf | 2015-08-24 14:52:23 -0700 | [diff] [blame] | 216 | for (size_t i = 0; i < kKernelSize; ++i) { |
| 217 | const size_t idx = i + offset_idx * kKernelSize; |
| 218 | const float pre_sinc = static_cast<float>(M_PI * |
| 219 | (static_cast<int>(i) - static_cast<int>(kKernelSize / 2) - |
| 220 | subsample_offset)); |
andrew@webrtc.org | 2038920 | 2014-02-26 18:14:54 +0000 | [diff] [blame] | 221 | kernel_pre_sinc_storage_[idx] = pre_sinc; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 222 | |
| 223 | // Compute Blackman window, matching the offset of the sinc(). |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 224 | const float x = (i - subsample_offset) / kKernelSize; |
bjornv@webrtc.org | 3cbd6c2 | 2014-09-04 13:21:44 +0000 | [diff] [blame] | 225 | const float window = static_cast<float>(kA0 - kA1 * cos(2.0 * M_PI * x) + |
| 226 | kA2 * cos(4.0 * M_PI * x)); |
andrew@webrtc.org | 2038920 | 2014-02-26 18:14:54 +0000 | [diff] [blame] | 227 | kernel_window_storage_[idx] = window; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 228 | |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 229 | // Compute the sinc with offset, then window the sinc() function and store |
| 230 | // at the correct offset. |
bjornv@webrtc.org | 3cbd6c2 | 2014-09-04 13:21:44 +0000 | [diff] [blame] | 231 | kernel_storage_[idx] = static_cast<float>(window * |
| 232 | ((pre_sinc == 0) ? |
| 233 | sinc_scale_factor : |
| 234 | (sin(sinc_scale_factor * pre_sinc) / pre_sinc))); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 235 | } |
| 236 | } |
| 237 | } |
| 238 | |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 239 | void SincResampler::SetRatio(double io_sample_rate_ratio) { |
| 240 | if (fabs(io_sample_rate_ratio_ - io_sample_rate_ratio) < |
| 241 | std::numeric_limits<double>::epsilon()) { |
| 242 | return; |
| 243 | } |
| 244 | |
| 245 | io_sample_rate_ratio_ = io_sample_rate_ratio; |
| 246 | |
| 247 | // Optimize reinitialization by reusing values which are independent of |
| 248 | // |sinc_scale_factor|. Provides a 3x speedup. |
| 249 | const double sinc_scale_factor = SincScaleFactor(io_sample_rate_ratio_); |
Peter Kasting | dce40cf | 2015-08-24 14:52:23 -0700 | [diff] [blame] | 250 | for (size_t offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) { |
| 251 | for (size_t i = 0; i < kKernelSize; ++i) { |
| 252 | const size_t idx = i + offset_idx * kKernelSize; |
andrew@webrtc.org | 2038920 | 2014-02-26 18:14:54 +0000 | [diff] [blame] | 253 | const float window = kernel_window_storage_[idx]; |
| 254 | const float pre_sinc = kernel_pre_sinc_storage_[idx]; |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 255 | |
bjornv@webrtc.org | 3cbd6c2 | 2014-09-04 13:21:44 +0000 | [diff] [blame] | 256 | kernel_storage_[idx] = static_cast<float>(window * |
| 257 | ((pre_sinc == 0) ? |
| 258 | sinc_scale_factor : |
| 259 | (sin(sinc_scale_factor * pre_sinc) / pre_sinc))); |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 260 | } |
| 261 | } |
| 262 | } |
| 263 | |
Peter Kasting | dce40cf | 2015-08-24 14:52:23 -0700 | [diff] [blame] | 264 | void SincResampler::Resample(size_t frames, float* destination) { |
| 265 | size_t remaining_frames = frames; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 266 | |
| 267 | // 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] | 268 | if (!buffer_primed_ && remaining_frames) { |
| 269 | read_cb_->Run(request_frames_, r0_); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 270 | buffer_primed_ = true; |
| 271 | } |
| 272 | |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 273 | // Step (2) -- Resample! const what we can outside of the loop for speed. It |
| 274 | // actually has an impact on ARM performance. See inner loop comment below. |
| 275 | const double current_io_ratio = io_sample_rate_ratio_; |
| 276 | const float* const kernel_ptr = kernel_storage_.get(); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 277 | while (remaining_frames) { |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 278 | // |i| may be negative if the last Resample() call ended on an iteration |
| 279 | // that put |virtual_source_idx_| over the limit. |
| 280 | // |
| 281 | // Note: The loop construct here can severely impact performance on ARM |
| 282 | // or when built with clang. See https://codereview.chromium.org/18566009/ |
bjornv@webrtc.org | 3cbd6c2 | 2014-09-04 13:21:44 +0000 | [diff] [blame] | 283 | for (int i = static_cast<int>( |
| 284 | ceil((block_size_ - virtual_source_idx_) / current_io_ratio)); |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 285 | i > 0; --i) { |
| 286 | assert(virtual_source_idx_ < block_size_); |
| 287 | |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 288 | // |virtual_source_idx_| lies in between two kernel offsets so figure out |
| 289 | // what they are. |
bjornv@webrtc.org | 3cbd6c2 | 2014-09-04 13:21:44 +0000 | [diff] [blame] | 290 | const int source_idx = static_cast<int>(virtual_source_idx_); |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 291 | const double subsample_remainder = virtual_source_idx_ - source_idx; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 292 | |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 293 | const double virtual_offset_idx = |
| 294 | subsample_remainder * kKernelOffsetCount; |
bjornv@webrtc.org | 3cbd6c2 | 2014-09-04 13:21:44 +0000 | [diff] [blame] | 295 | const int offset_idx = static_cast<int>(virtual_offset_idx); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 296 | |
| 297 | // We'll compute "convolutions" for the two kernels which straddle |
| 298 | // |virtual_source_idx_|. |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 299 | const float* const k1 = kernel_ptr + offset_idx * kKernelSize; |
| 300 | const float* const k2 = k1 + kKernelSize; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 301 | |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 302 | // Ensure |k1|, |k2| are 16-byte aligned for SIMD usage. Should always be |
| 303 | // true so long as kKernelSize is a multiple of 16. |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 304 | assert(0u == (reinterpret_cast<uintptr_t>(k1) & 0x0F)); |
| 305 | assert(0u == (reinterpret_cast<uintptr_t>(k2) & 0x0F)); |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 306 | |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 307 | // Initialize input pointer based on quantized |virtual_source_idx_|. |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 308 | const float* const input_ptr = r1_ + source_idx; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 309 | |
| 310 | // Figure out how much to weight each kernel's "convolution". |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 311 | const double kernel_interpolation_factor = |
| 312 | virtual_offset_idx - offset_idx; |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 313 | *destination++ = CONVOLVE_FUNC( |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 314 | input_ptr, k1, k2, kernel_interpolation_factor); |
| 315 | |
| 316 | // Advance the virtual index. |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 317 | virtual_source_idx_ += current_io_ratio; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 318 | |
| 319 | if (!--remaining_frames) |
| 320 | return; |
| 321 | } |
| 322 | |
| 323 | // Wrap back around to the start. |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 324 | virtual_source_idx_ -= block_size_; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 325 | |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 326 | // Step (3) -- Copy r3_, r4_ to r1_, r2_. |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 327 | // 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] | 328 | memcpy(r1_, r3_, sizeof(*input_buffer_.get()) * kKernelSize); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 329 | |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 330 | // Step (4) -- Reinitialize regions if necessary. |
| 331 | if (r0_ == r2_) |
| 332 | UpdateRegions(true); |
| 333 | |
| 334 | // Step (5) -- Refresh the buffer with more input. |
| 335 | read_cb_->Run(request_frames_, r0_); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 336 | } |
| 337 | } |
| 338 | |
andrew@webrtc.org | c6a3755 | 2013-05-08 20:35:43 +0000 | [diff] [blame] | 339 | #undef CONVOLVE_FUNC |
| 340 | |
Peter Kasting | dce40cf | 2015-08-24 14:52:23 -0700 | [diff] [blame] | 341 | size_t SincResampler::ChunkSize() const { |
| 342 | return static_cast<size_t>(block_size_ / io_sample_rate_ratio_); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 343 | } |
| 344 | |
| 345 | void SincResampler::Flush() { |
| 346 | virtual_source_idx_ = 0; |
| 347 | buffer_primed_ = false; |
andrew@webrtc.org | b86fbaf | 2013-07-25 22:04:30 +0000 | [diff] [blame] | 348 | memset(input_buffer_.get(), 0, |
| 349 | sizeof(*input_buffer_.get()) * input_buffer_size_); |
| 350 | UpdateRegions(false); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 351 | } |
| 352 | |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 353 | float SincResampler::Convolve_C(const float* input_ptr, const float* k1, |
| 354 | const float* k2, |
| 355 | double kernel_interpolation_factor) { |
| 356 | float sum1 = 0; |
| 357 | float sum2 = 0; |
| 358 | |
| 359 | // Generate a single output sample. Unrolling this loop hurt performance in |
| 360 | // local testing. |
Peter Kasting | dce40cf | 2015-08-24 14:52:23 -0700 | [diff] [blame] | 361 | size_t n = kKernelSize; |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 362 | while (n--) { |
| 363 | sum1 += *input_ptr * *k1++; |
| 364 | sum2 += *input_ptr++ * *k2++; |
| 365 | } |
| 366 | |
| 367 | // Linearly interpolate the two "convolutions". |
bjornv@webrtc.org | 3cbd6c2 | 2014-09-04 13:21:44 +0000 | [diff] [blame] | 368 | return static_cast<float>((1.0 - kernel_interpolation_factor) * sum1 + |
| 369 | kernel_interpolation_factor * sum2); |
andrew@webrtc.org | a8ef811 | 2013-02-13 23:00:49 +0000 | [diff] [blame] | 370 | } |
| 371 | |
andrew@webrtc.org | 076fc12 | 2013-02-15 03:54:22 +0000 | [diff] [blame] | 372 | } // namespace webrtc |