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.
 
 
 
 
 
 
music-synthesizer-for-android/cpp/src/main.cc

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;
}