From 6feebefea959a6cffc834f8b10f20b0a168d0707 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Thu, 6 Jun 2013 22:17:33 -0700 Subject: [PATCH] Implement log2 function This patch implements log2, which is necessary for amplitude modulation (including amplitude LFO). Also fixes up command line main.cc to track Dx7 init changes. (The accuracy test is in that file). --- cpp/src/core.gyp | 1 + cpp/src/log2.cc | 35 ++++++++++++++++++++++++++++++ cpp/src/log2.h | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ cpp/src/main.cc | 35 +++++++++++++++++++++++++----- 4 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 cpp/src/log2.cc create mode 100644 cpp/src/log2.h diff --git a/cpp/src/core.gyp b/cpp/src/core.gyp index 7c6570d..780b3f3 100644 --- a/cpp/src/core.gyp +++ b/cpp/src/core.gyp @@ -11,6 +11,7 @@ 'fm_op_kernel.cc', 'freqlut.cc', 'lfo.cc', + 'log2.cc', 'patch.cc', 'pitchenv.cc', 'resofilter.cc', diff --git a/cpp/src/log2.cc b/cpp/src/log2.cc new file mode 100644 index 0000000..7639613 --- /dev/null +++ b/cpp/src/log2.cc @@ -0,0 +1,35 @@ +/* + * Copyright 2013 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. + */ + +#include + +#include "synth.h" +#include "log2.h" + +int32_t log2tab[LOG2_N_SAMPLES << 1]; + +void Log2::init() { + const double mul = 1 / log(2); + for (int i = 0; i < LOG2_N_SAMPLES; i++) { + double y = mul * log(i + (LOG2_N_SAMPLES)) + (7 - LOG2_LG_N_SAMPLES); + log2tab[(i << 1) + 1] = (int32_t)floor(y * (1 << 24) + 0.5); + } + for (int i = 0; i < LOG2_N_SAMPLES - 1; i++) { + log2tab[i << 1] = log2tab[(i << 1) + 3] - log2tab[(i << 1) + 1]; + } + log2tab[(LOG2_N_SAMPLES << 1) - 2] = (8 << 24) - log2tab[(LOG2_N_SAMPLES << 1) - 1]; +} + diff --git a/cpp/src/log2.h b/cpp/src/log2.h new file mode 100644 index 0000000..998e0d5 --- /dev/null +++ b/cpp/src/log2.h @@ -0,0 +1,56 @@ +/* + * Copyright 2013 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. + */ + +class Log2 { + public: + Log2(); + + static void init(); + + // Q24 in, Q24 out + static int32_t lookup(uint32_t x); +}; + +#define LOG2_LG_N_SAMPLES 9 +#define LOG2_N_SAMPLES (1 << LOG2_LG_N_SAMPLES) + +#define LOG2_INLINE + +extern int32_t log2tab[LOG2_N_SAMPLES << 1]; + +#ifdef __GNUC__ +static int clz(unsigned int x) { + return __builtin_clz(x); +} +// TODO: other implementations (including ANSI C) +#endif + +#ifdef LOG2_INLINE +inline +int32_t Log2::lookup(uint32_t x) { + int exp = clz(x | 1); + unsigned int y = x << exp; + + const int SHIFT = 31 - LOG2_LG_N_SAMPLES; + int lowbits = y & ((1 << SHIFT) - 1); + int y_int = (y >> (SHIFT - 1)) & ((LOG2_N_SAMPLES - 1) << 1); + int dz = log2tab[y_int]; + int z0 = log2tab[y_int + 1]; + + int z = z0 + (((int64_t)dz * (int64_t)lowbits) >> SHIFT); + return z - (exp << 24); +} +#endif diff --git a/cpp/src/main.cc b/cpp/src/main.cc index f3be631..e20ca8a 100644 --- a/cpp/src/main.cc +++ b/cpp/src/main.cc @@ -25,10 +25,13 @@ #include "sawtooth.h" #include "sin.h" #include "exp2.h" +#include "log2.h" #include "resofilter.h" #include "fm_core.h" #include "fm_op_kernel.h" #include "env.h" +#include "patch.h" +#include "controllers.h" #include "dx7note.h" using namespace std; @@ -60,7 +63,22 @@ void test_sin_accuracy() { double err = fabs(y - yd); if (err > maxerr) maxerr = err; } - cout << "Max error: " << maxerr; + cout << "Max error: " << maxerr << endl; +} + +void test_log_accuracy() { + double maxerr = 0; + for (int i = 0; i < 1000000; i++) { + uint32_t x = rand(); + int32_t y = Log2::lookup(x); + double yd = (1 << 24) * log(x * (1.0 / (1 << 24))) / log(2); + double err = fabs(y - yd); + if (err > maxerr) { + maxerr = err; + cout << "x = " << x << ", y = " << y << ", yd = " << (int)yd << endl; + } + } + cout << "Max error: " << maxerr << endl; } void test_pure_accuracy() { @@ -197,7 +215,12 @@ void mkdx7note(double sample_rate) { const int n_samples = 400 * 1024; WavOut w("/tmp/foo.wav", sample_rate, n_samples); - Dx7Note note(epiano, 57, 64); + Dx7Note note; + char unpacked_patch[156]; + UnpackPatch(epiano, unpacked_patch); + note.init(unpacked_patch, 57, 64); + Controllers controllers; + controllers.values_[kControllerPitch] = 0x2000; int32_t buf[N]; for (int i = 0; i < n_samples; i += N) { @@ -207,7 +230,7 @@ void mkdx7note(double sample_rate) { if (i == n_samples / 2) { note.keyup(); } - note.compute(buf); + note.compute(buf, 0, 0, &controllers); for (int j = 0; j < N; j++) { buf[j] >>= 2; } @@ -236,17 +259,19 @@ int main(int argc, char **argv) { Sawtooth::init(sample_rate); Sin::init(); Exp2::init(); + Log2::init(); //FmCore::dump(); //test_sin_accuracy(); + test_log_accuracy(); //benchmark_fm_op(); //test_pure_accuracy(); //benchmark_sin(); //int32_t freq = atoi(argv[1]); //cout << "Logfreq(" << freq << ") = " << Freqlut::lookup(freq) << endl; - //mkdx7note(sample_rate); - mksaw(sample_rate); + mkdx7note(sample_rate); + //mksaw(sample_rate); //test_ringbuffer(); test_exp2(); return 0;