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.
278 lines
7.4 KiB
278 lines
7.4 KiB
/*
|
|
* 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.
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <cstdlib>
|
|
#include <math.h>
|
|
|
|
#include "synth.h"
|
|
#include "module.h"
|
|
#include "freqlut.h"
|
|
#include "wavout.h"
|
|
#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;
|
|
|
|
void benchmark_sin() {
|
|
int32_t x;
|
|
for (int j = 0; j < 1000; j++) {
|
|
for (int i = 0; i < 1000000; i++) {
|
|
x += Sin::lookup(i);
|
|
}
|
|
}
|
|
cout << x << endl; // to make sure it gets used
|
|
}
|
|
|
|
void benchmark_fm_op() {
|
|
int32_t buf[64];
|
|
int32_t fb_buf[2];
|
|
for (int i = 0; i < 15625000; i++)
|
|
FmOpKernel::compute_fb(buf, 0, 123456, 1 << 24, 1 << 24,
|
|
fb_buf, 1, false);
|
|
}
|
|
|
|
void test_sin_accuracy() {
|
|
double maxerr = 0;
|
|
for (int i = 0; i < 1000000; i++) {
|
|
int32_t phase = rand() & ((1 << 24) - 1);
|
|
int32_t y = Sin::compute(phase);
|
|
double yd = (1 << 24) * sin(phase * (M_PI / (1 << 23)));
|
|
double err = fabs(y - yd);
|
|
if (err > maxerr) maxerr = err;
|
|
}
|
|
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() {
|
|
int32_t worstfreq;
|
|
int32_t worstphase;
|
|
int32_t worsterr = 0;
|
|
double errsum = 0;
|
|
for (int i = 0; i < 1000000; i++) {
|
|
int32_t freq = rand() & 0x7fffff;
|
|
int32_t phase = rand() & 0xffffff;
|
|
int32_t gain = 1 << 24;
|
|
int32_t buf[64];
|
|
FmOpKernel::compute_pure(buf, phase, freq, gain, gain, false);
|
|
int32_t maxerr = 0;
|
|
for (int j = 0; j < 64; j++) {
|
|
double y = gain * sin((phase + j * freq) * (2.0 * M_PI / (1 << 24)));
|
|
int32_t accurate = (int32_t)floor(y + 0.5);
|
|
int32_t err = abs(buf[j] - accurate);
|
|
if (err > maxerr) maxerr = err;
|
|
}
|
|
errsum += maxerr;
|
|
if (maxerr > worsterr) {
|
|
worsterr = maxerr;
|
|
worstfreq = freq;
|
|
worstphase = phase;
|
|
}
|
|
if (i < 10)
|
|
cout << phase << " " << freq << " " << maxerr << endl;
|
|
}
|
|
cout << worstphase << " " << worstfreq << " " << worsterr << endl;
|
|
cout << "Mean: " << (errsum * 1e-6) << endl;
|
|
}
|
|
|
|
void mksaw(double sample_rate) {
|
|
const int n_samples = 400 * 1024;
|
|
WavOut w("/tmp/foo.wav", sample_rate, n_samples);
|
|
|
|
Sawtooth s;
|
|
int32_t control_last[1];
|
|
int32_t control[1];
|
|
|
|
ResoFilter rf;
|
|
int32_t fc_last[2];
|
|
int32_t fc[2];
|
|
fc[0] = 0; // TODO
|
|
fc[1] = 4.2 * (1 << 24);
|
|
fc_last[0] = fc[0];
|
|
fc_last[1] = fc[1];
|
|
|
|
double ramp = 1e-7;
|
|
double f = ramp * (64 + 1);
|
|
control[0] = (1 << 24) * log(f * sample_rate) / log(2);
|
|
|
|
int32_t buf[64];
|
|
int32_t buf2[64];
|
|
int32_t *bufs[1];
|
|
int32_t *bufs2[1];
|
|
bufs[0] = buf;
|
|
bufs2[0] = buf2;
|
|
int32_t phase = 0;
|
|
for (int i = 0; i < n_samples; i += 64) {
|
|
|
|
double f = ramp * (i + 64 + 1);
|
|
// f = 44.0 / sample_rate;
|
|
control_last[0] = control[0];
|
|
control[0] = (1 << 24) * log(f * sample_rate) / log(2);
|
|
fc_last[1] = fc[1];
|
|
fc[1] = 4.0 * i * (1 << 24) / n_samples;
|
|
s.process((const int32_t **)0, control, control_last, bufs);
|
|
rf.process((const int32_t **)bufs, fc, fc_last, bufs2);
|
|
for (int j = 0; j < 64; j++) {
|
|
buf2[j] = buf[j] >> 1;
|
|
//phase += 100000;
|
|
//buf2[j] = (Sin::compute(phase) - (int32_t)((1<< 24) * sin(phase * 2 * M_PI / (1 << 24)))) << 12;
|
|
}
|
|
w.write_data(buf2, 64);
|
|
}
|
|
w.close();
|
|
}
|
|
|
|
void mknote(double sample_rate) {
|
|
const int n_samples = 400 * 1024;
|
|
WavOut w("/tmp/foo.wav", sample_rate, n_samples);
|
|
|
|
int32_t freq = 150358;
|
|
int32_t phase = 0;
|
|
|
|
int rates[4] = {70, 50, 30, 80};
|
|
int levels[4] = {99, 90, 70, 0};
|
|
Env e;
|
|
e.init(rates, levels, 99, 0);
|
|
|
|
int rates2[4] = {70, 50, 30, 80};
|
|
int levels2[4] = {99, 90, 70, 0};
|
|
Env e2;
|
|
e2.init(rates, levels, 90, 0);
|
|
|
|
int32_t buf[64];
|
|
|
|
int32_t gain1, gain2;
|
|
gain2 = 0;
|
|
int32_t gain21, gain22;
|
|
gain22 = 0;
|
|
for (int i = 0; i < n_samples; i += N) {
|
|
gain1 = gain2;
|
|
gain21 = gain22;
|
|
if (i == n_samples / 2) {
|
|
e.keydown(false);
|
|
e2.keydown(false);
|
|
}
|
|
int32_t level = e.getsample();
|
|
gain2 = (1<<8) * pow(2, level * (1.0 / (1 << 24)));
|
|
FmOpKernel::compute_pure(buf, phase, freq, gain1, gain2, false);
|
|
level = e2.getsample();
|
|
gain22 = (1<<8) * pow(2, level * (1.0 / (1 << 24)));
|
|
FmOpKernel::compute(buf, buf, phase, freq, gain21, gain22, false);
|
|
phase += freq << LG_N;
|
|
w.write_data(buf, N);
|
|
}
|
|
w.close();
|
|
}
|
|
|
|
char epiano[] = {
|
|
95, 29, 20, 50, 99, 95, 0, 0, 41, 0, 19, 0, 115, 24, 79, 2, 0, 95, 20, 20,
|
|
50, 99, 95, 0, 0, 0, 0, 0, 0, 3, 0, 99, 2, 0, 95, 29, 20, 50, 99, 95, 0, 0,
|
|
0, 0, 0, 0, 59, 24, 89, 2, 0, 95, 20, 20, 50, 99, 95, 0, 0, 0, 0, 0, 0, 59,
|
|
8, 99, 2, 0, 95, 50, 35, 78, 99, 75, 0, 0, 0, 0, 0, 0, 59, 28, 58, 28, 0, 96,
|
|
25, 25, 67, 99, 75, 0, 0, 0, 0, 0, 0, 83, 8, 99, 2, 0, 94, 67, 95, 60, 50,
|
|
50, 50, 50, 4, 6, 34, 33, 0, 0, 56, 24, 69, 46, 80, 73, 65, 78, 79, 32, 49,
|
|
32
|
|
};
|
|
|
|
void mkdx7note(double sample_rate) {
|
|
const int n_samples = 400 * 1024;
|
|
WavOut w("/tmp/foo.wav", sample_rate, n_samples);
|
|
|
|
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) {
|
|
for (int j = 0; j < N; j++) {
|
|
buf[j] = 0;
|
|
}
|
|
if (i == n_samples / 2) {
|
|
note.keyup();
|
|
}
|
|
note.compute(buf, 0, 0, &controllers);
|
|
for (int j = 0; j < N; j++) {
|
|
buf[j] >>= 2;
|
|
}
|
|
w.write_data(buf, N);
|
|
}
|
|
w.close();
|
|
}
|
|
|
|
void test_ringbuffer();
|
|
|
|
void test_exp2() {
|
|
for (int32_t i = -16 << 24; i < 6 << 24; i += 123) {
|
|
int32_t result = Exp2::lookup(i);
|
|
int32_t accurate = floor((1<<24) * pow(2, i * 1.0 / (1<<24)) + .5);
|
|
int32_t error = accurate - result;
|
|
if (abs(error) > 1 && abs(error) > accurate / 10000000) {
|
|
std::cout << i << ": " << result << " " << accurate << std::endl;
|
|
// TODO (maybe): hook into automated test runner & report results
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
double sample_rate = 44100.0;
|
|
Freqlut::init(sample_rate);
|
|
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);
|
|
//test_ringbuffer();
|
|
test_exp2();
|
|
return 0;
|
|
}
|
|
|