You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
MicroDexed/fm_op_kernel.cpp

284 lines
8.9 KiB

6 years ago
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
6 years ago
#include "config.h"
6 years ago
#include <math.h>
#include <cstdlib>
#ifdef HAVE_NEON
#include <cpu-features.h>
#endif
#include "synth.h"
#include "sin.h"
#include "fm_op_kernel.h"
6 years ago
#ifdef HAVE_NEON
6 years ago
static bool hasNeon() {
return true;
return (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
}
extern "C"
void neon_fm_kernel(const int *in, const int *busin, int *out, int count,
int32_t phase0, int32_t freq, int32_t gain1, int32_t dgain);
6 years ago
6 years ago
const int32_t __attribute__ ((aligned(16))) zeros[_N_] = {0};
6 years ago
#else
static bool hasNeon() {
return false;
}
#endif
void FmOpKernel::compute(int32_t *output, const int32_t *input,
int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
6 years ago
int32_t gain = gain1;
int32_t phase = phase0;
if (hasNeon()) {
#ifdef HAVE_NEON
6 years ago
neon_fm_kernel(input, add ? output : zeros, output, _N_,
phase0, freq, gain, dgain);
6 years ago
#endif
} else {
if (add) {
for (int i = 0; i < _N_; i++) {
6 years ago
gain += dgain;
int32_t y = Sin::lookup(phase + input[i]);
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] += y1;
phase += freq;
}
} else {
for (int i = 0; i < _N_; i++) {
6 years ago
gain += dgain;
int32_t y = Sin::lookup(phase + input[i]);
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] = y1;
phase += freq;
}
}
}
}
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
6 years ago
int32_t gain = gain1;
int32_t phase = phase0;
if (hasNeon()) {
#ifdef HAVE_NEON
neon_fm_kernel(zeros, add ? output : zeros, output, _N_,
phase0, freq, gain, dgain);
6 years ago
#endif
} else {
if (add) {
for (int i = 0; i < _N_; i++) {
6 years ago
gain += dgain;
int32_t y = Sin::lookup(phase);
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] += y1;
phase += freq;
}
} else {
for (int i = 0; i < _N_; i++) {
6 years ago
gain += dgain;
int32_t y = Sin::lookup(phase);
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
6 years ago
output[i] = y1;
phase += freq;
}
}
}
}
#define noDOUBLE_ACCURACY
#define HIGH_ACCURACY
void FmOpKernel::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2,
int32_t *fb_buf, int fb_shift, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
6 years ago
int32_t gain = gain1;
int32_t phase = phase0;
int32_t y0 = fb_buf[0];
int32_t y = fb_buf[1];
if (add) {
for (int i = 0; i < _N_; i++) {
6 years ago
gain += dgain;
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = Sin::lookup(phase + scaled_fb);
y = ((int64_t)y * (int64_t)gain) >> 24;
output[i] += y;
phase += freq;
}
} else {
for (int i = 0; i < _N_; i++) {
6 years ago
gain += dgain;
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = Sin::lookup(phase + scaled_fb);
y = ((int64_t)y * (int64_t)gain) >> 24;
output[i] = y;
phase += freq;
}
}
fb_buf[0] = y0;
fb_buf[1] = y;
}
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// Experimental sine wave generators below
#if 0
// Results: accuracy 64.3 mean, 170 worst case
// high accuracy: 5.0 mean, 49 worst case
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
6 years ago
#ifdef HIGH_ACCURACY
int32_t u = Sin::compute10(phase << 6);
u = ((int64_t)u * gain) >> 30;
int32_t v = Sin::compute10((phase << 6) + (1 << 28)); // quarter cycle
v = ((int64_t)v * gain) >> 30;
int32_t s = Sin::compute10(freq << 6);
int32_t c = Sin::compute10((freq << 6) + (1 << 28));
6 years ago
#else
int32_t u = Sin::compute(phase);
u = ((int64_t)u * gain) >> 24;
int32_t v = Sin::compute(phase + (1 << 22)); // quarter cycle
v = ((int64_t)v * gain) >> 24;
int32_t s = Sin::compute(freq) << 6;
int32_t c = Sin::compute(freq + (1 << 22)) << 6;
6 years ago
#endif
for (int i = 0; i < _N_; i++) {
output[i] = u;
int32_t t = ((int64_t)v * (int64_t)c - (int64_t)u * (int64_t)s) >> 30;
u = ((int64_t)u * (int64_t)c + (int64_t)v * (int64_t)s) >> 30;
v = t;
}
6 years ago
}
#endif
#if 0
// Results: accuracy 392.3 mean, 15190 worst case (near freq = 0.5)
// for freq < 0.25, 275.2 mean, 716 worst
// high accuracy: 57.4 mean, 7559 worst
// freq < 0.25: 17.9 mean, 78 worst
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
6 years ago
#ifdef HIGH_ACCURACY
int32_t u = floor(gain * sin(phase * (M_PI / (1 << 23))) + 0.5);
int32_t v = floor(gain * cos((phase - freq * 0.5) * (M_PI / (1 << 23))) + 0.5);
int32_t a = floor((1 << 25) * sin(freq * (M_PI / (1 << 24))) + 0.5);
6 years ago
#else
int32_t u = Sin::compute(phase);
u = ((int64_t)u * gain) >> 24;
int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1));
v = ((int64_t)v * gain) >> 24;
int32_t a = Sin::compute(freq >> 1) << 1;
6 years ago
#endif
for (int i = 0; i < _N_; i++) {
output[i] = u;
v -= ((int64_t)a * (int64_t)u) >> 24;
u += ((int64_t)a * (int64_t)v) >> 24;
}
6 years ago
}
#endif
#if 0
// Results: accuracy 370.0 mean, 15480 worst case (near freq = 0.5)
// with FRAC_NUM accuracy initialization: mean 1.55, worst 58 (near freq = 0)
6 years ago
// with high accuracy: mean 4.2, worst 292 (near freq = 0.5)
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
6 years ago
#ifdef DOUBLE_ACCURACY
int32_t u = floor((1 << 30) * sin(phase * (M_PI / (1 << 23))) + 0.5);
FRAC_NUM a_d = sin(freq * (M_PI / (1 << 24)));
int32_t v = floor((1LL << 31) * a_d * cos((phase - freq * 0.5) *
(M_PI / (1 << 23))) + 0.5);
int32_t aa = floor((1LL << 31) * a_d * a_d + 0.5);
6 years ago
#else
#ifdef HIGH_ACCURACY
int32_t u = Sin::compute10(phase << 6);
int32_t v = Sin::compute10((phase << 6) + (1 << 28) - (freq << 5));
int32_t a = Sin::compute10(freq << 5);
v = ((int64_t)v * (int64_t)a) >> 29;
int32_t aa = ((int64_t)a * (int64_t)a) >> 29;
6 years ago
#else
int32_t u = Sin::compute(phase) << 6;
int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1));
int32_t a = Sin::compute(freq >> 1);
v = ((int64_t)v * (int64_t)a) >> 17;
int32_t aa = ((int64_t)a * (int64_t)a) >> 17;
6 years ago
#endif
#endif
if (aa < 0) aa = (1 << 31) - 1;
for (int i = 0; i < _N_; i++) {
gain += dgain;
output[i] = ((int64_t)u * (int64_t)gain) >> 30;
v -= ((int64_t)aa * (int64_t)u) >> 29;
u += v;
}
6 years ago
}
#endif
#if 0
// Results:: accuracy 112.3 mean, 4262 worst (near freq = 0.5)
// high accuracy 2.9 mean, 143 worst
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
6 years ago
#ifdef HIGH_ACCURACY
int32_t u = Sin::compute10(phase << 6);
int32_t lastu = Sin::compute10((phase - freq) << 6);
int32_t a = Sin::compute10((freq << 6) + (1 << 28)) << 1;
6 years ago
#else
int32_t u = Sin::compute(phase) << 6;
int32_t lastu = Sin::compute(phase - freq) << 6;
int32_t a = Sin::compute(freq + (1 << 22)) << 7;
6 years ago
#endif
if (a < 0 && freq < 256) a = (1 << 31) - 1;
if (a > 0 && freq > 0x7fff00) a = -(1 << 31);
for (int i = 0; i < _N_; i++) {
gain += dgain;
output[i] = ((int64_t)u * (int64_t)gain) >> 30;
//output[i] = u;
int32_t newu = (((int64_t)u * (int64_t)a) >> 30) - lastu;
lastu = u;
u = newu;
}
6 years ago
}
#endif