This source file includes following definitions.
- SincScaleFactor
- InitializeCPUSpecificFeatures
- InitializeCPUSpecificFeatures
- InitializeCPUSpecificFeatures
- InitializeCPUSpecificFeatures
- not_currently_resampling_
- UpdateRegions
- InitializeKernel
- SetRatio
- ChunkSize
- Flush
- Convolve_C
- Convolve_NEON
#define _USE_MATH_DEFINES
#include "media/base/sinc_resampler.h"
#include <cmath>
#include <limits>
#include "base/cpu.h"
#include "base/logging.h"
#if defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON)
#include <arm_neon.h>
#endif
namespace media {
static double SincScaleFactor(double io_ratio) {
double sinc_scale_factor = io_ratio > 1.0 ? 1.0 / io_ratio : 1.0;
sinc_scale_factor *= 0.9;
return sinc_scale_factor;
}
#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_NACL)
#if defined(__SSE__)
#define CONVOLVE_FUNC Convolve_SSE
void SincResampler::InitializeCPUSpecificFeatures() {}
#else
#define CONVOLVE_FUNC g_convolve_proc_
typedef float (*ConvolveProc)(const float*, const float*, const float*, double);
static ConvolveProc g_convolve_proc_ = NULL;
void SincResampler::InitializeCPUSpecificFeatures() {
CHECK(!g_convolve_proc_);
g_convolve_proc_ = base::CPU().has_sse() ? Convolve_SSE : Convolve_C;
}
#endif
#elif defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON)
#define CONVOLVE_FUNC Convolve_NEON
void SincResampler::InitializeCPUSpecificFeatures() {}
#else
#define CONVOLVE_FUNC Convolve_C
void SincResampler::InitializeCPUSpecificFeatures() {}
#endif
SincResampler::SincResampler(double io_sample_rate_ratio,
int request_frames,
const ReadCB& read_cb)
: io_sample_rate_ratio_(io_sample_rate_ratio),
read_cb_(read_cb),
request_frames_(request_frames),
input_buffer_size_(request_frames_ + kKernelSize),
kernel_storage_(static_cast<float*>(
base::AlignedAlloc(sizeof(float) * kKernelStorageSize, 16))),
kernel_pre_sinc_storage_(static_cast<float*>(
base::AlignedAlloc(sizeof(float) * kKernelStorageSize, 16))),
kernel_window_storage_(static_cast<float*>(
base::AlignedAlloc(sizeof(float) * kKernelStorageSize, 16))),
input_buffer_(static_cast<float*>(
base::AlignedAlloc(sizeof(float) * input_buffer_size_, 16))),
r1_(input_buffer_.get()),
r2_(input_buffer_.get() + kKernelSize / 2),
not_currently_resampling_(1) {
CHECK_GT(request_frames_, 0);
Flush();
CHECK_GT(block_size_, kKernelSize)
<< "block_size must be greater than kKernelSize!";
memset(kernel_storage_.get(), 0,
sizeof(*kernel_storage_.get()) * kKernelStorageSize);
memset(kernel_pre_sinc_storage_.get(), 0,
sizeof(*kernel_pre_sinc_storage_.get()) * kKernelStorageSize);
memset(kernel_window_storage_.get(), 0,
sizeof(*kernel_window_storage_.get()) * kKernelStorageSize);
InitializeKernel();
}
SincResampler::~SincResampler() {
CHECK(!base::AtomicRefCountDec(¬_currently_resampling_));
}
void SincResampler::UpdateRegions(bool second_load) {
r0_ = input_buffer_.get() + (second_load ? kKernelSize : kKernelSize / 2);
r3_ = r0_ + request_frames_ - kKernelSize;
r4_ = r0_ + request_frames_ - kKernelSize / 2;
block_size_ = r4_ - r2_;
CHECK_EQ(r1_, input_buffer_.get());
CHECK_EQ(r2_ - r1_, r4_ - r3_);
CHECK_LT(r2_, r3_);
}
void SincResampler::InitializeKernel() {
static const double kAlpha = 0.16;
static const double kA0 = 0.5 * (1.0 - kAlpha);
static const double kA1 = 0.5;
static const double kA2 = 0.5 * kAlpha;
const double sinc_scale_factor = SincScaleFactor(io_sample_rate_ratio_);
for (int offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) {
const float subsample_offset =
static_cast<float>(offset_idx) / kKernelOffsetCount;
for (int i = 0; i < kKernelSize; ++i) {
const int idx = i + offset_idx * kKernelSize;
const float pre_sinc = M_PI * (i - kKernelSize / 2 - subsample_offset);
kernel_pre_sinc_storage_[idx] = pre_sinc;
const float x = (i - subsample_offset) / kKernelSize;
const float window =
kA0 - kA1 * cos(2.0 * M_PI * x) + kA2 * cos(4.0 * M_PI * x);
kernel_window_storage_[idx] = window;
if (pre_sinc == 0) {
kernel_storage_[idx] = sinc_scale_factor * window;
} else {
kernel_storage_[idx] =
window * sin(sinc_scale_factor * pre_sinc) / pre_sinc;
}
}
}
}
void SincResampler::SetRatio(double io_sample_rate_ratio) {
if (fabs(io_sample_rate_ratio_ - io_sample_rate_ratio) <
std::numeric_limits<double>::epsilon()) {
return;
}
io_sample_rate_ratio_ = io_sample_rate_ratio;
const double sinc_scale_factor = SincScaleFactor(io_sample_rate_ratio_);
for (int offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) {
for (int i = 0; i < kKernelSize; ++i) {
const int idx = i + offset_idx * kKernelSize;
const float window = kernel_window_storage_[idx];
const float pre_sinc = kernel_pre_sinc_storage_[idx];
if (pre_sinc == 0) {
kernel_storage_[idx] = sinc_scale_factor * window;
} else {
kernel_storage_[idx] =
window * sin(sinc_scale_factor * pre_sinc) / pre_sinc;
}
}
}
}
void SincResampler::Resample(int frames, float* destination) {
CHECK(!base::AtomicRefCountDec(¬_currently_resampling_));
int remaining_frames = frames;
if (!buffer_primed_ && remaining_frames) {
read_cb_.Run(request_frames_, r0_);
buffer_primed_ = true;
}
const double current_io_ratio = io_sample_rate_ratio_;
const float* const kernel_ptr = kernel_storage_.get();
while (remaining_frames) {
int source_idx = virtual_source_idx_;
while (source_idx < block_size_) {
const double subsample_remainder = virtual_source_idx_ - source_idx;
const double virtual_offset_idx =
subsample_remainder * kKernelOffsetCount;
const int offset_idx = virtual_offset_idx;
const float* const k1 = kernel_ptr + offset_idx * kKernelSize;
const float* const k2 = k1 + kKernelSize;
DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(k1) & 0x0F);
DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(k2) & 0x0F);
const float* const input_ptr = r1_ + source_idx;
const double kernel_interpolation_factor =
virtual_offset_idx - offset_idx;
*destination++ = CONVOLVE_FUNC(
input_ptr, k1, k2, kernel_interpolation_factor);
virtual_source_idx_ += current_io_ratio;
source_idx = virtual_source_idx_;
if (!--remaining_frames) {
base::AtomicRefCountInc(¬_currently_resampling_);
return;
}
}
DCHECK_GE(virtual_source_idx_, block_size_);
virtual_source_idx_ -= block_size_;
memcpy(r1_, r3_, sizeof(*input_buffer_.get()) * kKernelSize);
if (r0_ == r2_)
UpdateRegions(true);
read_cb_.Run(request_frames_, r0_);
}
base::AtomicRefCountInc(¬_currently_resampling_);
}
#undef CONVOLVE_FUNC
int SincResampler::ChunkSize() const {
return block_size_ / io_sample_rate_ratio_;
}
void SincResampler::Flush() {
CHECK(base::AtomicRefCountIsOne(¬_currently_resampling_));
virtual_source_idx_ = 0;
buffer_primed_ = false;
memset(input_buffer_.get(), 0,
sizeof(*input_buffer_.get()) * input_buffer_size_);
UpdateRegions(false);
}
float SincResampler::Convolve_C(const float* input_ptr, const float* k1,
const float* k2,
double kernel_interpolation_factor) {
float sum1 = 0;
float sum2 = 0;
int n = kKernelSize;
while (n--) {
sum1 += *input_ptr * *k1++;
sum2 += *input_ptr++ * *k2++;
}
return (1.0 - kernel_interpolation_factor) * sum1
+ kernel_interpolation_factor * sum2;
}
#if defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON)
float SincResampler::Convolve_NEON(const float* input_ptr, const float* k1,
const float* k2,
double kernel_interpolation_factor) {
float32x4_t m_input;
float32x4_t m_sums1 = vmovq_n_f32(0);
float32x4_t m_sums2 = vmovq_n_f32(0);
const float* upper = input_ptr + kKernelSize;
for (; input_ptr < upper; ) {
m_input = vld1q_f32(input_ptr);
input_ptr += 4;
m_sums1 = vmlaq_f32(m_sums1, m_input, vld1q_f32(k1));
k1 += 4;
m_sums2 = vmlaq_f32(m_sums2, m_input, vld1q_f32(k2));
k2 += 4;
}
m_sums1 = vmlaq_f32(
vmulq_f32(m_sums1, vmovq_n_f32(1.0 - kernel_interpolation_factor)),
m_sums2, vmovq_n_f32(kernel_interpolation_factor));
float32x2_t m_half = vadd_f32(vget_high_f32(m_sums1), vget_low_f32(m_sums1));
return vget_lane_f32(vpadd_f32(m_half, m_half), 0);
}
#endif
}