* Copyright (C) 2011 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:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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.
*/
#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "third_party/blink/renderer/platform/audio/sinc_resampler.h"
#include "base/memory/raw_ptr.h"
#include "build/build_config.h"
#include "media/base/audio_bus.h"
#include "third_party/blink/renderer/platform/audio/audio_bus.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
#include "third_party/fdlibm/ieee754.h"
#if defined(ARCH_CPU_X86_FAMILY)
#include <emmintrin.h>
#endif
namespace blink {
SincResampler::SincResampler(double scale_factor,
unsigned kernel_size,
unsigned number_of_kernel_offsets)
: scale_factor_(scale_factor),
kernel_size_(kernel_size),
number_of_kernel_offsets_(number_of_kernel_offsets),
kernel_storage_(kernel_size_ * (number_of_kernel_offsets_ + 1)),
virtual_source_index_(0),
block_size_(512),
input_buffer_(block_size_ + kernel_size_),
source_(nullptr),
source_frames_available_(0),
source_provider_(nullptr),
is_buffer_primed_(false) {
InitializeKernel();
}
void SincResampler::InitializeKernel() {
double alpha = 0.16;
double a0 = 0.5 * (1.0 - alpha);
double a1 = 0.5;
double a2 = 0.5 * alpha;
double sinc_scale_factor = scale_factor_ > 1.0 ? 1.0 / scale_factor_ : 1.0;
sinc_scale_factor *= 0.9;
int n = kernel_size_;
int half_size = n / 2;
for (unsigned offset_index = 0; offset_index <= number_of_kernel_offsets_;
++offset_index) {
double subsample_offset =
static_cast<double>(offset_index) / number_of_kernel_offsets_;
for (int i = 0; i < n; ++i) {
double s =
sinc_scale_factor * kPiDouble * (i - half_size - subsample_offset);
double sinc = !s ? 1.0 : fdlibm::sin(s) / s;
sinc *= sinc_scale_factor;
double x = (i - subsample_offset) / n;
double window = a0 - a1 * fdlibm::cos(kTwoPiDouble * x) +
a2 * fdlibm::cos(kTwoPiDouble * 2.0 * x);
kernel_storage_[i + offset_index * kernel_size_] = sinc * window;
}
}
}
void SincResampler::ConsumeSource(float* buffer,
unsigned number_of_source_frames) {
DCHECK(source_provider_);
scoped_refptr<AudioBus> bus =
AudioBus::Create(1, number_of_source_frames, false);
bus->SetChannelMemory(0, buffer, number_of_source_frames);
source_provider_->ProvideInput(
bus.get(), base::checked_cast<int>(number_of_source_frames));
}
namespace {
class BufferSourceProvider final : public AudioSourceProvider {
public:
BufferSourceProvider(const float* source, int number_of_source_frames)
: source_(source), source_frames_available_(number_of_source_frames) {}
void ProvideInput(AudioBus* bus, int frames_to_process) override {
DCHECK(source_);
DCHECK(bus);
if (!source_ || !bus) {
return;
}
float* buffer = bus->Channel(0)->MutableData();
int frames_to_copy = std::min(source_frames_available_, frames_to_process);
memcpy(buffer, source_, sizeof(float) * frames_to_copy);
if (frames_to_copy < frames_to_process) {
memset(buffer + frames_to_copy, 0,
sizeof(float) * (frames_to_process - frames_to_copy));
}
source_frames_available_ -= frames_to_copy;
source_ += frames_to_copy;
}
void SetClient(AudioSourceProviderClient*) override {}
private:
raw_ptr<const float, AllowPtrArithmetic> source_;
int source_frames_available_;
};
}
void SincResampler::Process(const float* source,
float* destination,
int number_of_source_frames) {
BufferSourceProvider source_provider(source, number_of_source_frames);
unsigned number_of_destination_frames =
static_cast<unsigned>(number_of_source_frames / scale_factor_);
unsigned remaining = number_of_destination_frames;
while (remaining) {
unsigned frames_this_time = std::min(remaining, block_size_);
Process(&source_provider, destination, frames_this_time);
destination += frames_this_time;
remaining -= frames_this_time;
}
}
void SincResampler::Process(AudioSourceProvider* source_provider,
float* destination,
uint32_t frames_to_process) {
DCHECK(source_provider);
DCHECK_GT(block_size_, kernel_size_);
DCHECK_GE(input_buffer_.size(), block_size_ + kernel_size_);
DCHECK_EQ(kernel_size_ % 2, 0u);
source_provider_ = source_provider;
unsigned number_of_destination_frames = frames_to_process;
float* r0 = input_buffer_.Data() + kernel_size_ / 2;
float* r1 = input_buffer_.Data();
float* r2 = r0;
float* r3 = r0 + block_size_ - kernel_size_ / 2;
float* r4 = r0 + block_size_;
float* r5 = r0 + kernel_size_ / 2;
if (!is_buffer_primed_) {
ConsumeSource(r0, block_size_ + kernel_size_ / 2);
is_buffer_primed_ = true;
}
while (number_of_destination_frames) {
while (virtual_source_index_ < block_size_) {
int source_index_i = static_cast<int>(virtual_source_index_);
double subsample_remainder = virtual_source_index_ - source_index_i;
double virtual_offset_index =
subsample_remainder * number_of_kernel_offsets_;
int offset_index = static_cast<int>(virtual_offset_index);
float* k1 = kernel_storage_.Data() + offset_index * kernel_size_;
float* k2 = k1 + kernel_size_;
float* input_p = r1 + source_index_i;
float sum1 = 0;
float sum2 = 0;
double kernel_interpolation_factor = virtual_offset_index - offset_index;
int n = kernel_size_;
#define CONVOLVE_ONE_SAMPLE() \
do { \
input = *input_p++; \
sum1 += input * *k1; \
sum2 += input * *k2; \
++k1; \
++k2; \
} while (0)
{
float input;
#if defined(ARCH_CPU_X86_FAMILY)
while ((reinterpret_cast<uintptr_t>(input_p) & 0x0F) && n) {
CONVOLVE_ONE_SAMPLE();
n--;
}
float* end_p = input_p + n - n % 4;
__m128 m_input;
__m128 m_k1;
__m128 m_k2;
__m128 mul1;
__m128 mul2;
__m128 sums1 = _mm_setzero_ps();
__m128 sums2 = _mm_setzero_ps();
bool k1_aligned = !(reinterpret_cast<uintptr_t>(k1) & 0x0F);
bool k2_aligned = !(reinterpret_cast<uintptr_t>(k2) & 0x0F);
#define LOAD_DATA(l1, l2) \
do { \
m_input = _mm_load_ps(input_p); \
m_k1 = _mm_##l1##_ps(k1); \
m_k2 = _mm_##l2##_ps(k2); \
} while (0)
#define CONVOLVE_4_SAMPLES() \
do { \
mul1 = _mm_mul_ps(m_input, m_k1); \
mul2 = _mm_mul_ps(m_input, m_k2); \
sums1 = _mm_add_ps(sums1, mul1); \
sums2 = _mm_add_ps(sums2, mul2); \
input_p += 4; \
k1 += 4; \
k2 += 4; \
} while (0)
if (k1_aligned && k2_aligned) {
while (input_p < end_p) {
LOAD_DATA(load, load);
CONVOLVE_4_SAMPLES();
}
} else if (!k1_aligned && k2_aligned) {
while (input_p < end_p) {
LOAD_DATA(loadu, load);
CONVOLVE_4_SAMPLES();
}
} else if (k1_aligned && !k2_aligned) {
while (input_p < end_p) {
LOAD_DATA(load, loadu);
CONVOLVE_4_SAMPLES();
}
} else {
while (input_p < end_p) {
LOAD_DATA(loadu, loadu);
CONVOLVE_4_SAMPLES();
}
}
float* group_sum_p = reinterpret_cast<float*>(&sums1);
sum1 +=
group_sum_p[0] + group_sum_p[1] + group_sum_p[2] + group_sum_p[3];
group_sum_p = reinterpret_cast<float*>(&sums2);
sum2 +=
group_sum_p[0] + group_sum_p[1] + group_sum_p[2] + group_sum_p[3];
n %= 4;
while (n) {
CONVOLVE_ONE_SAMPLE();
n--;
}
#else
if (n == 32) {
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
} else if (n == 64) {
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
CONVOLVE_ONE_SAMPLE();
} else {
while (n--) {
CONVOLVE_ONE_SAMPLE();
}
}
#endif
}
#undef CONVOLVE_ONE_SAMPLE
double result = (1.0 - kernel_interpolation_factor) * sum1 +
kernel_interpolation_factor * sum2;
*destination++ = result;
virtual_source_index_ += scale_factor_;
--number_of_destination_frames;
if (!number_of_destination_frames) {
return;
}
}
virtual_source_index_ -= block_size_;
memcpy(r1, r3, sizeof(float) * (kernel_size_ / 2));
memcpy(r2, r4, sizeof(float) * (kernel_size_ / 2));
ConsumeSource(r5, block_size_);
}
}
}