| /* |
| * Copyright (C) 2013 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "third_party/blink/renderer/platform/audio/up_sampler.h" |
| |
| #include <memory> |
| |
| #include "third_party/blink/renderer/platform/wtf/math_extras.h" |
| #include "third_party/fdlibm/ieee754.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| // Computes ideal band-limited filter coefficients to sample in between each |
| // source sample-frame. This filter will be used to compute the odd |
| // sample-frames of the output. |
| std::unique_ptr<AudioFloatArray> MakeKernel(size_t size) { |
| std::unique_ptr<AudioFloatArray> kernel = |
| std::make_unique<AudioFloatArray>(size); |
| |
| // Blackman window parameters. |
| double alpha = 0.16; |
| double a0 = 0.5 * (1.0 - alpha); |
| double a1 = 0.5; |
| double a2 = 0.5 * alpha; |
| |
| int n = kernel->size(); |
| int half_size = n / 2; |
| double subsample_offset = -0.5; |
| |
| for (int i = 0; i < n; ++i) { |
| // Compute the sinc() with offset. |
| double s = kPiDouble * (i - half_size - subsample_offset); |
| double sinc = !s ? 1.0 : fdlibm::sin(s) / s; |
| |
| // Compute Blackman window, matching the offset of the sinc(). |
| double x = (i - subsample_offset) / n; |
| double window = a0 - a1 * fdlibm::cos(kTwoPiDouble * x) + |
| a2 * fdlibm::cos(kTwoPiDouble * 2.0 * x); |
| |
| // Window the sinc() function. |
| (*kernel)[i] = sinc * window; |
| } |
| |
| return kernel; |
| } |
| |
| } // namespace |
| |
| UpSampler::UpSampler(size_t input_block_size) |
| : input_block_size_(input_block_size), |
| temp_buffer_(input_block_size), |
| input_buffer_(input_block_size * 2) { |
| std::unique_ptr<AudioFloatArray> convolution_kernel = |
| MakeKernel(kDefaultKernelSize); |
| if (input_block_size_ <= 128) { |
| // If the input block size is small enough, use direct convolution because |
| // it is faster than FFT convolution for such input block sizes. |
| direct_convolver_ = std::make_unique<DirectConvolver>( |
| input_block_size_, std::move(convolution_kernel)); |
| } else { |
| // Otherwise, use FFT convolution because it is faster than direct |
| // convolution for large input block sizes. |
| simple_fft_convolver_ = std::make_unique<SimpleFFTConvolver>( |
| input_block_size_, std::move(convolution_kernel)); |
| } |
| } |
| |
| void UpSampler::Process(const float* source_p, |
| float* dest_p, |
| size_t source_frames_to_process) { |
| const size_t convolution_kernel_size = |
| direct_convolver_ ? direct_convolver_->ConvolutionKernelSize() |
| : simple_fft_convolver_->ConvolutionKernelSize(); |
| |
| DCHECK_EQ(source_frames_to_process, input_block_size_); |
| |
| DCHECK_EQ(source_frames_to_process, temp_buffer_.size()); |
| |
| size_t half_size = convolution_kernel_size / 2; |
| |
| DCHECK_EQ(input_buffer_.size(), source_frames_to_process * 2); |
| DCHECK_LE(half_size, source_frames_to_process); |
| |
| // Copy source samples to 2nd half of input buffer. |
| float* input_p = input_buffer_.Data() + source_frames_to_process; |
| memcpy(input_p, source_p, sizeof(float) * source_frames_to_process); |
| |
| // Copy even sample-frames 0,2,4,6... (delayed by the linear phase delay) |
| // directly into destP. |
| for (unsigned i = 0; i < source_frames_to_process; ++i) |
| dest_p[i * 2] = *((input_p - half_size) + i); |
| |
| // Compute odd sample-frames 1,3,5,7... |
| float* odd_samples_p = temp_buffer_.Data(); |
| if (direct_convolver_) { |
| direct_convolver_->Process(source_p, odd_samples_p, |
| source_frames_to_process); |
| } else { |
| simple_fft_convolver_->Process(source_p, odd_samples_p, |
| source_frames_to_process); |
| } |
| |
| for (unsigned i = 0; i < source_frames_to_process; ++i) |
| dest_p[i * 2 + 1] = odd_samples_p[i]; |
| |
| // Copy 2nd half of input buffer to 1st half. |
| memcpy(input_buffer_.Data(), input_p, |
| sizeof(float) * source_frames_to_process); |
| } |
| |
| void UpSampler::Reset() { |
| direct_convolver_.reset(); |
| simple_fft_convolver_.reset(); |
| input_buffer_.Zero(); |
| } |
| |
| size_t UpSampler::LatencyFrames() const { |
| const size_t convolution_kernel_size = |
| direct_convolver_ ? direct_convolver_->ConvolutionKernelSize() |
| : simple_fft_convolver_->ConvolutionKernelSize(); |
| // Divide by two since this is a linear phase kernel and the delay is at the |
| // center of the kernel. |
| return convolution_kernel_size / 2; |
| } |
| |
| } // namespace blink |