Various changes

main
Thor 1 year ago
parent d7b13aad99
commit 9e07cb5103
  1. 7
      CMakeLists.txt
  2. 4
      include/synth/channel.h
  3. 2
      include/synth/dsp/adsr.h
  4. 69
      include/synth/dsp/filter.h
  5. 23
      include/synth/dsp/oscillator.h
  6. 22
      include/synth/dsp/util.h
  7. 2
      include/synth/globals.h
  8. 4
      include/synth/synth.h
  9. 8
      include/synth/voice.h
  10. 2
      include/synth/voicemanager.h
  11. 12
      src/genluts.cpp
  12. 13
      src/synth/channel.cpp
  13. 6
      src/synth/preset.cpp
  14. 3
      src/synth/voice.cpp
  15. 11
      src/synth/voicemanager.cpp
  16. 2
      src/synthapp.cpp

@ -7,8 +7,8 @@ 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")
set(CMAKE_CXX_FLAGS_DEBUG "-Wall -Wextra -Wdouble-promotion -g -ffast-math")
set(CMAKE_CXX_FLAGS_RELEASE "-Wall -Wextra -Wdouble-promotion -Ofast -DNDEBUG")
endif()
if(WIN32)
@ -53,3 +53,6 @@ target_include_directories(main PRIVATE lib/portmidi/porttime)
target_link_libraries(main PRIVATE wxbase)
target_link_libraries(main PRIVATE wxcore)
add_custom_target(synth)
add_dependencies(synth main genluts)

@ -25,7 +25,7 @@ public:
void control(int cc, int val);
frame tick() {
frame out{0};
frame out{0, 0};
float lfo = sin(lfoPhase);
Voice** voices = voiceManager->getChannelVoices(number);
for(Voice** voice = voices; *voice != NULL; ++voice) {
@ -34,7 +34,7 @@ public:
out *= volume;
lfoPhase += settings.lfoStep;
while(lfoPhase >= PIx2) {
while(lfoPhase >= (float) PIx2) {
lfoPhase -= PIx2;
}

@ -77,7 +77,7 @@ private:
float last;
float curve(float gain) {
return 0.5 - 0.5 * cos(clamp(gain * M_PI, 0, M_PI));
return 0.5f - 0.5f * cosf(clamp(gain * (float) M_PI, 0.f, M_PI));
}
};

@ -10,12 +10,16 @@
#include "../globals.h"
#include "util.h"
#define FILTER_K_BASE 6.214608098422192
#define FILTER_OVERSAMPLE 4
#define FILTER_K_MIN_FREQ 20.0f
#define FILTER_K_MAX_FREQ 15000.0f
#define FILTER_K_BASE logf(FILTER_K_MAX_FREQ / FILTER_K_MIN_FREQ)
#define FILTER_K_SCALE (2.f * FILTER_K_MIN_FREQ / (FILTER_OVERSAMPLE * SAMPLE_RATE))
#define K_LUT_SIZE 64
#ifdef USE_LUTS
#define K_LUT_SIZE 64
extern float kLUT[K_LUT_SIZE];
inline float freqToK(float x) {
@ -27,13 +31,13 @@
#else
inline float freqToK(float x) {
return tan(M_PI_4 * 0.002 * exp(x * FILTER_K_BASE));
return tanf((float) M_PI_2 * std::min(0.5f, FILTER_K_SCALE * expf(x * FILTER_K_BASE)));
}
#endif
inline float noteToFltFreq(float note) {
return ((note - 69) / 12.0) / (FILTER_K_BASE * M_LOG2E);
return ((note - 69) / 12.0f) / (FILTER_K_BASE * (float) M_LOG2E);
}
class SVF12 {
@ -64,6 +68,28 @@ public:
}
};
class Oversampler {
private:
int times;
float kK, kQ = (float) M_SQRT1_2;
SVF12 aa1, aa2;
public:
Oversampler(int times) : times(times), kK(tanf((float) M_PI_2 * 1.0f / times)) {}
inline float tick(float in, std::function<float(float)> process) {
if(times < 2) {
return process(in);
} else {
float out = aa2.tick(aa1.tick(times * process(in), kK, kQ).lp, kK, kQ).lp;
for(int i = 1; i < times; ++i) {
aa2.tick(aa1.tick(process(0), kK, kQ).lp, kK, kQ);
}
return out;
}
}
};
// 12 and 24 dB/oct
class Filter {
public:
@ -88,27 +114,32 @@ public:
private:
Settings const * settings;
SVF12 fltA, fltB;
Oversampler os{FILTER_OVERSAMPLE};
public:
void assign(Settings const * settings);
inline float tick(float in, float freqAdd) {
float freq = clamp(settings->freq + freqAdd, 0, 1);
//float kK = tan(M_PI_4 * clamp(0.002 * exp(freq * FILTER_K_BASE), 0, 1));
float freq = clamp(settings->freq + freqAdd, 0.f, 1.f);
float kK = freqToK(freq);
float kQ = M_SQRT1_2 + settings->res;
SVF12::Output outA = fltA.tick(in, kK, kQ);
switch(settings->type) {
case TYPE_LP:
return settings->slope == SLOPE_24 ? fltB.tick(outA.lp, kK, kQ).lp : outA.lp;
case TYPE_BP:
return settings->slope == SLOPE_24 ? fltB.tick(outA.bp, kK, kQ).bp : outA.bp;
case TYPE_HP:
return settings->slope == SLOPE_24 ? fltB.tick(outA.hp, kK, kQ).hp : outA.hp;
}
float kQ = (float) M_SQRT1_2 + settings->res;
return os.tick(in, [this, kK, kQ](float oin) {
SVF12::Output outA = fltA.tick(oin, kK, kQ);
switch(settings->type) {
case TYPE_LP:
return settings->slope == SLOPE_24 ? fltB.tick(outA.lp, kK, kQ).lp : outA.lp;
case TYPE_BP:
return settings->slope == SLOPE_24 ? fltB.tick(outA.bp, kK, kQ).bp : outA.bp;
case TYPE_HP:
return settings->slope == SLOPE_24 ? fltB.tick(outA.hp, kK, kQ).hp : outA.hp;
default:
return oin;
}
});
}
};
#endif

@ -20,7 +20,7 @@ public:
float driftAmount;
float driftValue;
Oscillator() : phase(randPhase()), value(0), driftAmount(0.001), driftValue(0) {}
Oscillator() : phase(randPhase()), value(0), driftAmount(0.001f), driftValue(0) {}
void assign(Mode const * mode);
@ -32,21 +32,22 @@ public:
if (*mode == MODE_SINE) {
value = sinf(phase); // No harmonics in sine so no aliasing!! No Poly BLEPs needed!
} else if (*mode == MODE_SAW) {
value = (2.0 * phase / PIx2) - 1.0; // Render naive waveshape
value = (2.0f * phase / PIx2) - 1.0f; // Render naive waveshape
value -= polyBlep(t, dt); // Layer output of Poly BLEP on top
} else if (*mode == MODE_SQUARE) {
if (phase < M_PI) {
if (phase < (float) M_PI) {
value = 1.0; // Flip
} else {
value = -1.0; // Flop
}
value += polyBlep(t, dt); // Layer output of Poly BLEP on top (flip)
value -= polyBlep(fmodf(t + 0.5, 1.0), dt); // Layer output of Poly BLEP on top (flop)
value -= polyBlep(fmodf(t + 0.5f, 1.0f), dt); // Layer output of Poly BLEP on top (flop)
}
driftValue += 0.01 * (randFrac() - 0.5) - 0.00001 * driftValue;
phase += phaseStep * (1.0 + driftAmount * driftValue);
while(phase >= PIx2) { // wrap if phase angle >=360º
//driftValue += 0.01f * (randFrac() - 0.5f) - 0.00001f * driftValue;
//phase += phaseStep * (1.0f + driftAmount * driftValue);
phase += phaseStep;
while(phase >= (float) PIx2) { // wrap if phase angle >=360º
phase -= PIx2;
}
@ -59,15 +60,15 @@ public:
// discontinuities between 0 & 1
if (t < dt) {
t /= dt;
return t + t - t * t - 1.0;
return t + t - t * t - 1.0f;
}
// t^2/2 +t +1/2
// -1 <= t <= 0
// discontinuities between -1 & 0
else if (t > 1.0 - dt) {
t = (t - 1.0) / dt;
return t * t + t + t + 1.0;
else if (t > 1.0f - dt) {
t = (t - 1.0f) / dt;
return t * t + t + t + 1.0f;
}
// no discontinuities

@ -6,12 +6,12 @@
#include "../globals.h"
#define PIx2 (2 * M_PI)
#define PIx2 ((float )(2 * M_PI))
#ifdef USE_LUTS
#define NOTE_LUT_SIZE 128
#define CENT_LUT_SIZE 128
//#define USE_NOTE_LUTS
#define NOTE_LUT_SIZE 128
#define CENT_LUT_SIZE 128
#ifdef USE_NOTE_LUTS
extern float noteLUT[NOTE_LUT_SIZE];
extern float centLUT[CENT_LUT_SIZE];
@ -25,7 +25,7 @@
#else
inline float noteToFreq(float note) {
return 440 * pow(2, (note - 69) / 12.0);
return 440 * powf(2.f, (note - 69) / 12.0f);
}
#endif
@ -35,19 +35,19 @@ inline float clamp(float x, float a, float b) {
}
inline float triangle(float phase) {
if(phase < M_PI) {
return phase / M_PI_2 - 1;
if(phase < (float) M_PI) {
return phase / (float) M_PI_2 - 1;
} else {
return 1 - (phase - M_PI) / M_PI_2;
return 1 - (phase - (float) M_PI) / (float) M_PI_2;
}
}
inline float randFrac() {
return (float) rand() / RAND_MAX;
return rand() / (float) RAND_MAX;
}
inline float randPhase() {
return PIx2 * randFrac();
return (float) PIx2 * randFrac();
}
#endif

@ -3,6 +3,4 @@
#define SAMPLE_RATE 44100
//#define USE_LUTS
#endif

@ -8,7 +8,7 @@
class Synth {
public:
Synth() {
for(int i = 0; i < 16; ++i) {
for(channel_t i = 0; i < 16; ++i) {
channels[i].number = i;
channels[i].voiceManager = &voiceManager;
channels[i].loadPreset(&DEFAULT_PRESET);
@ -24,7 +24,7 @@ public:
for(auto& channel : channels) {
out += channel.tick();
}
out *= 0.100;
out *= 0.100f;
return out;
}

@ -40,16 +40,18 @@ private:
public:
int note;
float unisonPosition;
void reset();
void assign(Settings const * settings);
void noteOn(int note);
void noteOn(int note, float unisonPosition = 0);
void noteOff();
inline bool isIdle() { return adsrAmp.isIdle(); }
inline float tick(float pitchAdd, float fltFreqAdd) {
const float osc1PhaseStep = noteToFreq(note + pitchAdd - settings->oscDetune) * PIx2 / SAMPLE_RATE;
const float osc2PhaseStep = noteToFreq(note + pitchAdd + settings->oscDetune + settings->osc2Pitch) * PIx2 / SAMPLE_RATE;
const float basePitch = note + pitchAdd + unisonPosition * 4 * settings->oscDetune;
const float osc1PhaseStep = noteToFreq(basePitch - settings->oscDetune) * PIx2 / SAMPLE_RATE;
const float osc2PhaseStep = noteToFreq(basePitch + settings->oscDetune + settings->osc2Pitch) * PIx2 / SAMPLE_RATE;
float out = 0;
out += (1 - settings->oscMix) * osc1.tick(osc1PhaseStep);
out += settings->oscMix * osc2.tick(osc2PhaseStep);

@ -5,7 +5,7 @@
#include "synth/voice.h"
#define NUM_VOICES 16
#define NUM_VOICES 32
typedef unsigned int channel_t;
typedef unsigned int serial_t;

@ -1,19 +1,17 @@
#include <cstdio>
#include <cmath>
#define USE_LUTS
#include "synth/dsp/filter.h"
#include "synth/dsp/util.h"
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) {
for(size_t i = 0; i < size; ++i) {
if(i % 8 == 0) {
fputs(" ", f);
}
fprintf(f, "%13.7f", lut[i]);
fprintf(f, "%13.7ff", (double) lut[i]);
if(i < size - 1) {
fputs(", ", f);
}
@ -28,19 +26,19 @@ void dumpLUT(const char * filename, const char * identifier, float *lut, size_t
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));
kLUT[i] = tanf((float) M_PI_4 * FILTER_K_SCALE * expf(i / (K_LUT_SIZE - 1.0f) * 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);
noteLUT[i] = 440 * powf(2.f, (i - 69.f) / 12.0f);
}
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));
centLUT[i] = pow(2.f, i / ((CENT_LUT_SIZE - 1) * 12.0f));
}
dumpLUT("src/synth/dsp/centlut.cpp", "centLUT", centLUT, CENT_LUT_SIZE);
}

@ -6,7 +6,7 @@
#include "synth/channel.h"
float timeToStep(float t) {
return (1.0 / SAMPLE_RATE) / t;
return (1.0f / SAMPLE_RATE) / t;
}
float ccToA(int x) {
@ -18,7 +18,7 @@ float ccToDR(int x) {
}
float ccToLFOStep(int x) {
return PIx2 * 0.1 * exp((1 + x) * 6.907755278982137 / 128.0) / SAMPLE_RATE;
return PIx2 * 0.1f * expf((1 + x) * 6.907755278982137f / 128.0f) / SAMPLE_RATE;
}
float ccToLFOPitchMod(int x) {
@ -55,9 +55,11 @@ void Channel::loadPreset(const Preset * preset) {
}
void Channel::noteOn(int note, int velocity) {
Voice* const voice = voiceManager->get(number);
voice->assign(&settings);
voice->noteOn(note);
for(int i = -1; i <= 1; ++i) {
Voice* const voice = voiceManager->get(number);
voice->assign(&settings);
voice->noteOn(note, i);
}
}
void Channel::noteOff(int note) {
@ -93,6 +95,7 @@ void Channel::control(int code, int value) {
case CC_FLT_Q: // Timbre / Harmonic Content (Standard MIDI)
settings.filter.res = value / 31.75;
printf("Q=%f\n", M_SQRT1_2 + (double) settings.filter.res);
break;
case CC_AMP_REL: // Release Time (Standard MIDI)

@ -22,10 +22,10 @@ const Preset DEFAULT_PRESET = {
},
.modEnv = {
.attack = 48,
.decay = 100,
.attack = 0,
.decay = 106,
.sustain = 0,
.release = 100
.release = 106
},
.modEnvFltGain = 56,

@ -14,8 +14,9 @@ void Voice::assign(Settings const * settings) {
filter.assign(&settings->filter);
}
void Voice::noteOn(int note) {
void Voice::noteOn(int note, float unisonPosition) {
this->note = note;
this->unisonPosition = unisonPosition;
adsrAmp.noteOn();
adsrMod.noteOn();
}

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

@ -50,7 +50,7 @@ static int paCallback(
float *out = (float*) outputBuffer;
for(int i = 0; i < framesPerBuffer; i++) {
for(unsigned long i = 0; i < framesPerBuffer; i++) {
frame f = app->synth.tick();
*out++ = f.left;
*out++ = f.right;

Loading…
Cancel
Save