Various changes

main
Thor 1 year ago
parent b933f936f8
commit d7b13aad99
  1. 15
      CMakeLists.txt
  2. 1
      include/synth/channel.h
  3. 35
      include/synth/dsp/filter.h
  4. 33
      include/synth/dsp/util.h
  5. 41
      include/synth/frame.h
  6. 40
      include/synth/globals.h
  7. 1
      include/synth/synth.h
  8. 2
      include/synth/voice.h
  9. 52
      src/genluts.cpp
  10. 1
      src/synth/channel.cpp
  11. 18
      src/synth/dsp/centlut.cpp
  12. 14
      src/synth/dsp/filterlut.cpp
  13. 18
      src/synth/dsp/notelut.cpp
  14. 8
      src/synth/synth.cpp
  15. 9
      src/synth/voicemanager.cpp
  16. 63
      src/synthapp.cpp
  17. 2
      xctrace-command.txt

@ -3,6 +3,13 @@ project(synth)
set(CMAKE_CXX_STANDARD 20)
set(BUILD_SHARED_LIBS OFF)
if(MSVC)
set(CMAKE_CXX_FLAGS_DEBUG "/W4 /DEBUG:FASTLINK /fp:fast")
set(CMAKE_CXX_FLAGS_RELEASE "/W4 /O2 /fp:fast /DNDEBUG")
else()
set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g -ffast-math")
set(CMAKE_CXX_FLAGS_RELEASE "-Wall -Ofast -DNDEBUG")
endif()
if(WIN32)
option(PA_USE_ASIO "" ON)
@ -26,6 +33,8 @@ add_executable(main
src/synth/channel.cpp
src/synth/preset.cpp
src/synth/voice.cpp
src/synth/dsp/notelut.cpp
src/synth/dsp/centlut.cpp
src/synth/dsp/oscillator.cpp
src/synth/dsp/filter.cpp
src/synth/dsp/filterlut.cpp
@ -35,12 +44,6 @@ target_include_directories(main PRIVATE include)
add_executable(genluts src/genluts.cpp)
target_include_directories(genluts PRIVATE include)
if(MSVC)
target_compile_options(main PRIVATE /fp:fast)
else()
target_compile_options(main PRIVATE -ffast-math)
endif()
#target_compile_options(main PRIVATE -fsanitize=thread)
#target_link_options(main PRIVATE -fsanitize=thread)

@ -8,6 +8,7 @@
#include "voicemanager.h"
#include "voice.h"
#include "preset.h"
#include "frame.h"
class Channel {
public:

@ -5,27 +5,34 @@
#include <cmath>
#include <cfloat>
#include <iostream>
#include "../globals.h"
#include "util.h"
#define FILTER_K_BASE 6.214608098422192
#define KLUT_SIZE 64
extern float klut[KLUT_SIZE];
inline float freqToK(float x) {
float intPart;
float fracPart = modf(x * (KLUT_SIZE - 1), &intPart);
int index = (int) intPart;
if(fracPart < FLT_EPSILON) {
return klut[index];
} else {
return (1 - fracPart) * klut[index] + fracPart * klut[index + 1];
#ifdef USE_LUTS
#define K_LUT_SIZE 64
extern float kLUT[K_LUT_SIZE];
inline float freqToK(float x) {
float intPart, fracPart = modf(x * (K_LUT_SIZE - 1), &intPart);
int index = (int) intPart;
return fracPart < FLT_EPSILON ? kLUT[index] : (1 - fracPart) * kLUT[index] + fracPart * kLUT[index + 1];
}
return klut[(int) roundf((KLUT_SIZE - 1) * x)];
}
inline float noteToK(float note) {
#else
inline float freqToK(float x) {
return tan(M_PI_4 * 0.002 * exp(x * FILTER_K_BASE));
}
#endif
inline float noteToFltFreq(float note) {
return ((note - 69) / 12.0) / (FILTER_K_BASE * M_LOG2E);
}

@ -2,19 +2,36 @@
#define __UTIL_H__
#include <cmath>
#include <cfloat>
#include "../globals.h"
#define PIx2 (2 * M_PI)
inline float clamp(float x, float a, float b) {
return fminf(b, fmaxf(a, x));
}
#ifdef USE_LUTS
inline float dBToGain(float dB) {
return pow(10, dB / 20.0);
}
#define NOTE_LUT_SIZE 128
#define CENT_LUT_SIZE 128
extern float noteLUT[NOTE_LUT_SIZE];
extern float centLUT[CENT_LUT_SIZE];
inline float noteToFreq(float note) {
float intPart, fracPart = modf(note, &intPart);
int index = (int) intPart;
return fracPart < FLT_EPSILON ? noteLUT[index] : noteLUT[index] * centLUT[(int) (fracPart * 127.0)];
}
#else
inline float noteToFreq(float note) {
return 440 * pow(2, (note - 69) / 12.0);
}
inline float noteToFreq(float note) {
return 440 * pow(2, (note - 69) / 12.0);
#endif
inline float clamp(float x, float a, float b) {
return fminf(b, fmaxf(a, x));
}
inline float triangle(float phase) {

@ -0,0 +1,41 @@
#ifndef __FRAME_H__
#define __FRAME_H__
struct frame {
float left;
float right;
inline frame operator+(const frame& rhs) {
return {
.left = left + rhs.left,
.right = right + rhs.right
};
}
inline frame operator+=(const frame& rhs) {
this->left += rhs.left;
this->right += rhs.right;
return *this;
}
inline frame operator*(float rhs) {
return {
.left = left * rhs,
.right = right * rhs
};
}
inline frame operator+=(float rhs) {
this->left += rhs;
this->right += rhs;
return *this;
}
inline frame operator*=(float rhs) {
this->left *= rhs;
this->right *= rhs;
return *this;
}
};
#endif

@ -1,44 +1,8 @@
#ifndef __GLOBALS_H__
#define __GLOBALS_H__
#define SAMPLE_RATE 48000
#define SAMPLE_RATE 44100
struct frame {
float left;
float right;
inline frame operator+(const frame& rhs) {
return {
.left = left + rhs.left,
.right = right + rhs.right
};
}
inline frame operator+=(const frame& rhs) {
this->left += rhs.left;
this->right += rhs.right;
return *this;
}
inline frame operator*(float rhs) {
return {
.left = left * rhs,
.right = right * rhs
};
}
inline frame operator+=(float rhs) {
this->left += rhs;
this->right += rhs;
return *this;
}
inline frame operator*=(float rhs) {
this->left *= rhs;
this->right *= rhs;
return *this;
}
};
//#define USE_LUTS
#endif

@ -3,6 +3,7 @@
#include "voicemanager.h"
#include "channel.h"
#include "frame.h"
class Synth {
public:

@ -53,7 +53,7 @@ public:
float out = 0;
out += (1 - settings->oscMix) * osc1.tick(osc1PhaseStep);
out += settings->oscMix * osc2.tick(osc2PhaseStep);
const float keyTrackVal = settings->keyTrack * noteToK(note);
const float keyTrackVal = settings->keyTrack * noteToFltFreq(note);
out = filter.tick(out, settings->modEnvFltGain * adsrMod.tick() + keyTrackVal + fltFreqAdd);
out *= adsrAmp.tick();
return out;

@ -1,30 +1,46 @@
#include <stdio.h>
#include <math.h>
#include <cstdio>
#include <cmath>
#include "synth/dsp/filter.h"
int main(int argc, char** argv) {
FILE* f;
#define USE_LUTS
float klut[KLUT_SIZE];
for(int i = 0; i < KLUT_SIZE; ++i) {
klut[i] = tan(M_PI_4 * 0.002 * exp(i / (KLUT_SIZE - 1.0) * FILTER_K_BASE));
}
#include "synth/dsp/filter.h"
#include "synth/dsp/util.h"
f = fopen("src/synth/dsp/filterlut.cpp", "w");
fputs("float klut[] = {\n", f);
for(int i = 0; i < KLUT_SIZE; ++i) {
if(i % 16 == 0) {
void dumpLUT(const char * filename, const char * identifier, float *lut, size_t size) {
FILE * f = fopen(filename, "w");
fprintf(f, "float %s[] = {\n", identifier);
for(int i = 0; i < size; ++i) {
if(i % 8 == 0) {
fputs(" ", f);
}
fprintf(f, "%.7f", klut[i]);
if(i < KLUT_SIZE - 1) {
fprintf(f, "%13.7f", lut[i]);
if(i < size - 1) {
fputs(", ", f);
}
if(i % 16 == 15) {
if(i % 8 == 7) {
putc('\n', f);
}
}
fputs("};\n", f);
fclose(f);
fclose(f);
}
int main(int argc, char** argv) {
float kLUT[K_LUT_SIZE];
for(int i = 0; i < K_LUT_SIZE; ++i) {
kLUT[i] = tan(M_PI_4 * 0.002 * exp(i / (K_LUT_SIZE - 1.0) * FILTER_K_BASE));
}
dumpLUT("src/synth/dsp/filterlut.cpp", "kLUT", kLUT, K_LUT_SIZE);
float noteLUT[NOTE_LUT_SIZE];
for(int i = 0; i < NOTE_LUT_SIZE; ++i) {
noteLUT[i] = 440 * pow(2, (i - 69) / 12.0);
}
dumpLUT("src/synth/dsp/notelut.cpp", "noteLUT", noteLUT, NOTE_LUT_SIZE);
float centLUT[CENT_LUT_SIZE];
for(int i = 0; i < CENT_LUT_SIZE; ++i) {
centLUT[i] = pow(2, (float) i / ((CENT_LUT_SIZE - 1) * 12.0));
}
dumpLUT("src/synth/dsp/centlut.cpp", "centLUT", centLUT, CENT_LUT_SIZE);
}

@ -93,7 +93,6 @@ void Channel::control(int code, int value) {
case CC_FLT_Q: // Timbre / Harmonic Content (Standard MIDI)
settings.filter.res = value / 31.75;
std::cout << "Q=" << settings.filter.res << std::endl;
break;
case CC_AMP_REL: // Release Time (Standard MIDI)

@ -0,0 +1,18 @@
float centLUT[] = {
1.0000000, 1.0004549, 1.0009100, 1.0013654, 1.0018209, 1.0022767, 1.0027326, 1.0031888,
1.0036452, 1.0041018, 1.0045586, 1.0050156, 1.0054728, 1.0059302, 1.0063878, 1.0068456,
1.0073037, 1.0077620, 1.0082204, 1.0086790, 1.0091379, 1.0095969, 1.0100563, 1.0105158,
1.0109755, 1.0114354, 1.0118955, 1.0123559, 1.0128164, 1.0132772, 1.0137382, 1.0141994,
1.0146607, 1.0151223, 1.0155841, 1.0160462, 1.0165083, 1.0169708, 1.0174334, 1.0178963,
1.0183593, 1.0188227, 1.0192862, 1.0197498, 1.0202137, 1.0206778, 1.0211421, 1.0216067,
1.0220715, 1.0225364, 1.0230016, 1.0234669, 1.0239326, 1.0243984, 1.0248644, 1.0253307,
1.0257971, 1.0262637, 1.0267307, 1.0271977, 1.0276650, 1.0281326, 1.0286002, 1.0290682,
1.0295364, 1.0300047, 1.0304732, 1.0309421, 1.0314111, 1.0318803, 1.0323497, 1.0328194,
1.0332892, 1.0337592, 1.0342295, 1.0347000, 1.0351708, 1.0356417, 1.0361128, 1.0365841,
1.0370557, 1.0375276, 1.0379995, 1.0384717, 1.0389441, 1.0394168, 1.0398897, 1.0403627,
1.0408360, 1.0413095, 1.0417832, 1.0422572, 1.0427313, 1.0432056, 1.0436803, 1.0441550,
1.0446301, 1.0451053, 1.0455807, 1.0460564, 1.0465323, 1.0470084, 1.0474846, 1.0479612,
1.0484380, 1.0489149, 1.0493921, 1.0498694, 1.0503471, 1.0508249, 1.0513029, 1.0517812,
1.0522597, 1.0527384, 1.0532173, 1.0536964, 1.0541759, 1.0546554, 1.0551351, 1.0556152,
1.0560954, 1.0565759, 1.0570565, 1.0575374, 1.0580184, 1.0584998, 1.0589813, 1.0594631
};

@ -1,6 +1,10 @@
float klut[] = {
0.0015708, 0.0017336, 0.0019134, 0.0021118, 0.0023307, 0.0025723, 0.0028390, 0.0031333, 0.0034582, 0.0038167, 0.0042124, 0.0046491, 0.0051311, 0.0056631, 0.0062502, 0.0068982,
0.0076134, 0.0084028, 0.0092740, 0.0102355, 0.0112968, 0.0124681, 0.0137608, 0.0151877, 0.0167625, 0.0185007, 0.0204193, 0.0225369, 0.0248743, 0.0274544, 0.0303024, 0.0334462,
0.0369167, 0.0407480, 0.0449779, 0.0496483, 0.0548053, 0.0605004, 0.0667905, 0.0737388, 0.0814158, 0.0898997, 0.0992782, 0.1096492, 0.1211226, 0.1338222, 0.1478880, 0.1634790,
0.1807770, 0.1999912, 0.2213640, 0.2451794, 0.2717728, 0.3015462, 0.3349871, 0.3726965, 0.4154281, 0.4641461, 0.5201120, 0.5850177, 0.6611994, 0.7519926, 0.8623521, 1.0000000
float kLUT[] = {
0.0015708, 0.0017336, 0.0019134, 0.0021118, 0.0023307, 0.0025723, 0.0028390, 0.0031333,
0.0034582, 0.0038167, 0.0042124, 0.0046491, 0.0051311, 0.0056631, 0.0062502, 0.0068982,
0.0076134, 0.0084028, 0.0092740, 0.0102355, 0.0112968, 0.0124681, 0.0137608, 0.0151877,
0.0167625, 0.0185007, 0.0204193, 0.0225369, 0.0248743, 0.0274544, 0.0303024, 0.0334462,
0.0369167, 0.0407480, 0.0449779, 0.0496483, 0.0548053, 0.0605004, 0.0667905, 0.0737388,
0.0814158, 0.0898997, 0.0992782, 0.1096492, 0.1211226, 0.1338222, 0.1478880, 0.1634790,
0.1807770, 0.1999912, 0.2213640, 0.2451794, 0.2717728, 0.3015462, 0.3349871, 0.3726965,
0.4154281, 0.4641461, 0.5201120, 0.5850177, 0.6611994, 0.7519926, 0.8623521, 1.0000000
};

@ -0,0 +1,18 @@
float noteLUT[] = {
8.1757994, 8.6619568, 9.1770239, 9.7227182, 10.3008614, 10.9133825, 11.5623255, 12.2498569,
12.9782715, 13.7500000, 14.5676174, 15.4338531, 16.3515987, 17.3239136, 18.3540478, 19.4454365,
20.6017227, 21.8267651, 23.1246510, 24.4997139, 25.9565430, 27.5000000, 29.1352348, 30.8677063,
32.7031975, 34.6478271, 36.7080956, 38.8908730, 41.2034454, 43.6535301, 46.2493019, 48.9994278,
51.9130859, 55.0000000, 58.2704697, 61.7354126, 65.4063950, 69.2956543, 73.4161911, 77.7817459,
82.4068909, 87.3070602, 92.4986038, 97.9988556, 103.8261719, 110.0000000, 116.5409393, 123.4708252,
130.8127899, 138.5913086, 146.8323822, 155.5634918, 164.8137817, 174.6141205, 184.9972076, 195.9977112,
207.6523438, 220.0000000, 233.0818787, 246.9416504, 261.6255798, 277.1826172, 293.6647644, 311.1269836,
329.6275635, 349.2282410, 369.9944153, 391.9954224, 415.3046875, 440.0000000, 466.1637573, 493.8833008,
523.2511597, 554.3652344, 587.3295288, 622.2539673, 659.2551270, 698.4564819, 739.9888306, 783.9908447,
830.6093750, 880.0000000, 932.3275146, 987.7666016, 1046.5023193, 1108.7304688, 1174.6590576, 1244.5079346,
1318.5102539, 1396.9129639, 1479.9776611, 1567.9816895, 1661.2187500, 1760.0000000, 1864.6550293, 1975.5332031,
2093.0046387, 2217.4609375, 2349.3181152, 2489.0158691, 2637.0205078, 2793.8259277, 2959.9553223, 3135.9633789,
3322.4375000, 3520.0000000, 3729.3100586, 3951.0664062, 4186.0092773, 4434.9218750, 4698.6362305, 4978.0317383,
5274.0410156, 5587.6518555, 5919.9106445, 6271.9267578, 6644.8750000, 7040.0000000, 7458.6201172, 7902.1328125,
8372.0185547, 8869.8437500, 9397.2724609, 9956.0634766, 10548.0820312, 11175.3037109, 11839.8212891, 12543.8535156
};

@ -1,18 +1,18 @@
#include <iostream>
#include <cstdio>
#include "synth/synth.h"
void Synth::noteOn(int ch, int note, int vel) {
channels[ch].noteOn(note, vel);
std::cout << "Note On: ch=" << ch << " note=" << note << " vel=" << vel << std::endl;
printf("Note On: ch=%d note=%d vel=%d\n", ch, note, vel);
}
void Synth::noteOff(int ch, int note) {
channels[ch].noteOff(note);
std::cout << "Note Off: ch=" << ch << " note=" << note << std::endl;
printf("Note Off: ch=%d note=%d\n", ch, note);
}
void Synth::control(int ch, int cc, int val) {
channels[ch].control(cc, val);
std::cout << "Controller: ch=" << ch << " cc=" << cc << " val=" << val << std::endl;
printf("Controller: ch=%d cc=%d val=%d\n", ch, cc, val);
}

@ -1,4 +1,4 @@
#include <iostream>
#include <cstdio>
#include "synth/voicemanager.h"
@ -20,10 +20,11 @@ Voice * VoiceManager::get(channel_t channel) {
voiceData.serial = serial++;
voiceData.voice.reset();
putchar('\n');
for(VoiceData& vd : voices) {
std::cout << "channel=" << vd.channel << " serial=" << vd.serial << " idle=" << vd.voice.isIdle() << " note=" << vd.voice.note << std::endl;
printf("channel=%d serial=%d idle=%d note=%d\n", vd.channel, vd.serial, vd.voice.isIdle(), vd.voice.note);
}
std::cout << std::endl;
putchar('\n');
return &voiceData.voice;
}
@ -38,4 +39,4 @@ Voice** VoiceManager::getChannelVoices(channel_t channel) {
}
*voice = NULL;
return result;
}
}

@ -1,6 +1,6 @@
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <iostream>
#include "synth/globals.h"
@ -10,6 +10,7 @@
using namespace std;
// PortAudio callback for audio I/O
bool firstPACallback = true;
static int paCallback(
const void *inputBuffer,
void *outputBuffer,
@ -20,6 +21,11 @@ static int paCallback(
SynthApp *app = (SynthApp*) userData;
if(firstPACallback) {
printf("Buffer size is %ld frames\n", framesPerBuffer);
firstPACallback = false;
}
PmEvent events[16];
int numEvents = Pm_Read(app->pmStream, events, 16);
for(int i = 0; i < numEvents; ++i) {
@ -40,13 +46,6 @@ static int paCallback(
int val = Pm_MessageData2(msg);
app->synth.control(ch, cc, val);
}
/*
cout << "MIDI:";
for(int j = 0; j < 24; j += 8) {
cout << " " << hex << ((msg >> j) & 0xFF);
}
cout << endl;
*/
}
float *out = (float*) outputBuffer;
@ -90,19 +89,22 @@ bool SynthApp::OnInit() {
}
int pmDeviceCount = Pm_CountDevices();
cout << "Select MIDI input device:" << endl;
putchar('\n');
printf("Select MIDI input device:\n\n");
for(int i = 0; i < pmDeviceCount; ++i) {
const PmDeviceInfo* deviceInfo = Pm_GetDeviceInfo(i);
if(!deviceInfo->input) {
continue;
}
cout << i << ". " << deviceInfo->name << endl;
printf("%d. %s\n", i, deviceInfo->name);
}
putchar('\n');
int pmDeviceIndex;
for(;;) { // loop for input until valid choice is made
string input;
getline(cin, input);
//string input = "1";
printf("Number: ");
char input[4];
fgets(input, 4, stdin);
//const char* input = "1";
try {
pmDeviceIndex = stoi(input);
if( pmDeviceIndex >= 0 &&
@ -111,23 +113,27 @@ bool SynthApp::OnInit() {
break;
}
} catch(...) { } // stoi() throws an exeption for invalid integers
cout << "Invalid selection" << endl;
printf("Invalid selection\n");
}
putchar('\n');
PaHostApiIndex paDeviceCount = Pa_GetDeviceCount();
cout << "Select audio output device:" << endl;
printf("Select audio output device:\n\n");
for(int i = 0; i < paDeviceCount; ++i) {
const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo(i);
if(deviceInfo->maxOutputChannels < 2) {
continue;
}
cout << i << ". " << deviceInfo->name << " (" << Pa_GetHostApiInfo(deviceInfo->hostApi)->name << ")" << endl;
printf("%d. %s (%s)\n", i, deviceInfo->name, Pa_GetHostApiInfo(deviceInfo->hostApi)->name);
}
putchar('\n');
PaHostApiIndex paDeviceIndex;
for(;;) { // loop for input until valid choice is made
string input;
getline(cin, input);
//string input = "4";
printf("Number: ");
char input[4];
fgets(input, 4, stdin);
//const char* input = "5";
try {
paDeviceIndex = stoi(input);
if( paDeviceIndex >= 0 &&
@ -136,11 +142,11 @@ bool SynthApp::OnInit() {
break;
}
} catch(...) { } // stoi() throws an exeption for invalid integers
cout << "Invalid selection" << endl;
printf("Invalid selection\n");
}
// Open MIDI input
PmError pmError = Pm_OpenInput(
pmErr = Pm_OpenInput(
&pmStream,
pmDeviceIndex,
NULL,
@ -151,18 +157,25 @@ bool SynthApp::OnInit() {
handlePMError(pmErr);
}
const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo(paDeviceIndex);
putchar('\n');
printf("Default sample rate is %.0f Hz\n", deviceInfo->defaultSampleRate);
printf("Default low input latency is %.0f samples\n", deviceInfo->defaultSampleRate * deviceInfo->defaultLowOutputLatency);
putchar('\n');
// Configure/start audio output stream
PaStreamParameters paStreamParams;
paStreamParams.device = paDeviceIndex;
paStreamParams.channelCount = 2;
paStreamParams.sampleFormat = paFloat32;
paStreamParams.suggestedLatency = 512.0 / SAMPLE_RATE;
paStreamParams.suggestedLatency = deviceInfo->defaultLowOutputLatency;
paStreamParams.hostApiSpecificStreamInfo = NULL;
paErr = Pa_OpenStream(
&paStream,
NULL, // no input channels
&paStreamParams, // stereo output
SAMPLE_RATE,
deviceInfo->defaultSampleRate,
0, // frames per buffer
paNoFlag,
paCallback,
@ -176,7 +189,7 @@ bool SynthApp::OnInit() {
}
const PaStreamInfo *paStreamInfo = Pa_GetStreamInfo(paStream);
cout << "Latency: " << round(1000 * paStreamInfo->outputLatency) << " ms" << endl;
printf("Reported latency is %d frames\n", (int) round(SAMPLE_RATE * paStreamInfo->outputLatency));
wxFrame *frame = new SynthFrame();
frame->Show(true);
@ -197,7 +210,7 @@ int SynthApp::OnExit() {
handlePMError(pmErr);
}
Pa_Terminate();
cout << "Terminating" << endl;
printf("\nTerminating\n");
return 0;
}

@ -1 +1 @@
xctrace record --template 'Time Profiler' --target-stdin - --target-stdout - --launch ./build/main
xctrace record --template 'Time Profiler' --launch ./build/main
Loading…
Cancel
Save