Add tanh function

Add a fast implementation of the tanh function. This will be useful
for distortion.
master
Raph Levien 12 years ago
parent d40a05871f
commit dedde725d2
  1. 29
      cpp/src/exp2.cc
  2. 34
      cpp/src/exp2.h
  3. 17
      cpp/src/main.cc
  4. 2
      cpp/src/pitchenv.cc

@ -19,6 +19,8 @@
#include "synth.h" #include "synth.h"
#include "exp2.h" #include "exp2.h"
#include <stdio.h>
int32_t exp2tab[EXP2_N_SAMPLES << 1]; int32_t exp2tab[EXP2_N_SAMPLES << 1];
void Exp2::init() { void Exp2::init() {
@ -34,3 +36,30 @@ void Exp2::init() {
exp2tab[(EXP2_N_SAMPLES << 1) - 2] = (1U << 31) - exp2tab[(EXP2_N_SAMPLES << 1) - 1]; exp2tab[(EXP2_N_SAMPLES << 1) - 2] = (1U << 31) - exp2tab[(EXP2_N_SAMPLES << 1) - 1];
} }
int32_t tanhtab[TANH_N_SAMPLES << 1];
static double dtanh(double y) {
return 1 - y * y;
}
void Tanh::init() {
double step = 4.0 / TANH_N_SAMPLES;
double y = 0;
for (int i = 0; i < TANH_N_SAMPLES; i++) {
tanhtab[(i << 1) + 1] = (1 << 24) * y + 0.5;
//printf("%d\n", tanhtab[(i << 1) + 1]);
// Use a basic 4th order Runge-Kutte to compute tanh from its
// differential equation.
double k1 = dtanh(y);
double k2 = dtanh(y + 0.5 * step * k1);
double k3 = dtanh(y + 0.5 * step * k2);
double k4 = dtanh(y + step * k3);
double dy = (step / 6) * (k1 + k4 + 2 * (k2 + k3));
y += dy;
}
for (int i = 0; i < TANH_N_SAMPLES - 1; i++) {
tanhtab[i << 1] = tanhtab[(i << 1) + 3] - tanhtab[(i << 1) + 1];
}
int32_t lasty = (1 << 24) * y + 0.5;
tanhtab[(TANH_N_SAMPLES << 1) - 2] = lasty - tanhtab[(TANH_N_SAMPLES << 1) - 1];
}

@ -44,3 +44,37 @@ int32_t Exp2::lookup(int32_t x) {
return y >> (6 - (x >> 24)); return y >> (6 - (x >> 24));
} }
#endif #endif
class Tanh {
public:
static void init();
// Q24 in, Q24 out
static int32_t lookup(int32_t x);
};
#define TANH_LG_N_SAMPLES 10
#define TANH_N_SAMPLES (1 << TANH_LG_N_SAMPLES)
extern int32_t tanhtab[TANH_N_SAMPLES << 1];
inline
int32_t Tanh::lookup(int32_t x) {
int32_t signum = x >> 31;
x ^= signum;
if (x >= (4 << 24)) {
if (x >= (17 << 23)) {
return signum ^ (1 << 24);
}
int32_t sx = ((int64_t)-48408812 * (int64_t)x) >> 24;
return signum ^ ((1 << 24) - 2 * Exp2::lookup(sx));
} else {
const int SHIFT = 26 - TANH_LG_N_SAMPLES;
int lowbits = x & ((1 << SHIFT) - 1);
int x_int = (x >> (SHIFT - 1)) & ((TANH_N_SAMPLES - 1) << 1);
int dy = tanhtab[x_int];
int y0 = tanhtab[x_int + 1];
int y = y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT);
return y ^ signum;
}
}

@ -81,6 +81,22 @@ void test_log_accuracy() {
cout << "Max error: " << maxerr << endl; cout << "Max error: " << maxerr << endl;
} }
void test_tanh_accuracy() {
Tanh::init();
double maxerr = 0;
for (int i = 0; i < 1000000; i++) {
int32_t x = (rand() & ((1 << 29) - 1)) - (1 << 28);
int32_t y = Tanh::lookup(x);
double yd = (1 << 24) * tanh(x * (1.0 / (1 << 24)));
double err = fabs(y - yd);
if (err > maxerr) {
cout << "x = " << x << ", y = " << y << ", yd = " << (int)yd << endl;
maxerr = err;
}
}
cout << "Max error: " << maxerr << endl;
}
void test_pure_accuracy() { void test_pure_accuracy() {
int32_t worstfreq; int32_t worstfreq;
int32_t worstphase; int32_t worstphase;
@ -264,6 +280,7 @@ int main(int argc, char **argv) {
//FmCore::dump(); //FmCore::dump();
//test_sin_accuracy(); //test_sin_accuracy();
test_log_accuracy(); test_log_accuracy();
test_tanh_accuracy();
//benchmark_fm_op(); //benchmark_fm_op();
//test_pure_accuracy(); //test_pure_accuracy();
//benchmark_sin(); //benchmark_sin();

@ -17,8 +17,6 @@
#include "synth.h" #include "synth.h"
#include "pitchenv.h" #include "pitchenv.h"
using namespace std;
int PitchEnv::unit_; int PitchEnv::unit_;
void PitchEnv::init(double sample_rate) { void PitchEnv::init(double sample_rate) {

Loading…
Cancel
Save