diff --git a/CMakeLists.txt b/CMakeLists.txt index bf17a19..a8527af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ endif() add_executable(main src/synthapp.cpp src/synthframe.cpp + src/ccslider.cpp src/synth/synth.cpp src/synth/voicemanager.cpp src/synth/channel.cpp @@ -27,9 +28,13 @@ add_executable(main src/synth/voice.cpp src/synth/dsp/oscillator.cpp src/synth/dsp/filter.cpp + src/synth/dsp/filterlut.cpp src/synth/dsp/adsr.cpp) 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() diff --git a/include/ccslider.h b/include/ccslider.h new file mode 100644 index 0000000..d403caa --- /dev/null +++ b/include/ccslider.h @@ -0,0 +1,10 @@ +#include + +#ifndef WX_PRECOMP + #include +#endif + +class CCSlider : public wxPanel { +public: + CCSlider(wxWindow* parent, wxWindowID id, int value, const wxString& label); +}; \ No newline at end of file diff --git a/include/synth/dsp/filter.h b/include/synth/dsp/filter.h index 8c47058..21de284 100644 --- a/include/synth/dsp/filter.h +++ b/include/synth/dsp/filter.h @@ -3,12 +3,28 @@ /* CEM3320/Oberheim/Phrophet-style filter as described here: https://arxiv.org/pdf/2111.05592.pdf */ -#include +#include +#include #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]; + } + return klut[(int) roundf((KLUT_SIZE - 1) * x)]; +} + inline float noteToK(float note) { return ((note - 69) / 12.0) / (FILTER_K_BASE * M_LOG2E); } @@ -71,7 +87,8 @@ public: 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 kK = tan(M_PI_4 * clamp(0.002 * exp(freq * FILTER_K_BASE), 0, 1)); + float kK = freqToK(freq); float kQ = M_SQRT1_2 + settings->res; SVF12::Output outA = fltA.tick(in, kK, kQ); diff --git a/include/synthframe.h b/include/synthframe.h index ebe67c1..b23194b 100644 --- a/include/synthframe.h +++ b/include/synthframe.h @@ -38,6 +38,8 @@ private: LFO_FILTER_SLIDER }; + wxSlider* addCCSlider(wxWindow* parent, wxSizer* sizer, wxWindowID id, const wxString& label, uint8_t value); + void OnExit(wxCommandEvent& event); void OnAbout(wxCommandEvent& event); diff --git a/src/ccslider.cpp b/src/ccslider.cpp new file mode 100644 index 0000000..a544612 --- /dev/null +++ b/src/ccslider.cpp @@ -0,0 +1,8 @@ +#include "ccslider.h" + +CCSlider::CCSlider(wxWindow* parent, wxWindowID id, int value, const wxString& label) : wxPanel(parent) { + wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); + SetSizer(sizer); + sizer->Add(new wxSlider(this, id, value, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE), 1, wxEXPAND); + sizer->Add(new wxStaticText(this, wxID_ANY, label)); +} \ No newline at end of file diff --git a/src/genluts.cpp b/src/genluts.cpp new file mode 100644 index 0000000..9fe4e4f --- /dev/null +++ b/src/genluts.cpp @@ -0,0 +1,30 @@ +#include +#include + +#include "synth/dsp/filter.h" + +int main(int argc, char** argv) { + FILE* f; + + 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)); + } + + 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) { + fputs(" ", f); + } + fprintf(f, "%.7f", klut[i]); + if(i < KLUT_SIZE - 1) { + fputs(", ", f); + } + if(i % 16 == 15) { + putc('\n', f); + } + } + fputs("};\n", f); + fclose(f); +} \ No newline at end of file diff --git a/src/synth/dsp/filterlut.cpp b/src/synth/dsp/filterlut.cpp new file mode 100644 index 0000000..4ae39d8 --- /dev/null +++ b/src/synth/dsp/filterlut.cpp @@ -0,0 +1,6 @@ +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 +}; diff --git a/src/synth/preset.cpp b/src/synth/preset.cpp index a09b29b..4893b6b 100644 --- a/src/synth/preset.cpp +++ b/src/synth/preset.cpp @@ -10,7 +10,7 @@ const Preset DEFAULT_PRESET = { .filter = { .type = Filter::TYPE_LP, .slope = Filter::SLOPE_24, - .freq = 45, + .freq = 64, .Q = 50, }, @@ -28,9 +28,9 @@ const Preset DEFAULT_PRESET = { .release = 100 }, - .modEnvFltGain = 40, + .modEnvFltGain = 56, .keyTrack = 127, .lfoFreq = 64, - .lfoPitchMod = 0, + .lfoPitchMod = 16, .lfoFltMod = 64 }; \ No newline at end of file diff --git a/src/synthframe.cpp b/src/synthframe.cpp index 143044f..6d76161 100644 --- a/src/synthframe.cpp +++ b/src/synthframe.cpp @@ -6,6 +6,8 @@ #include "synthframe.h" #include "synthapp.h" +#include "ccslider.h" + wxDECLARE_APP(SynthApp); SynthFrame::SynthFrame() : wxFrame(NULL, wxID_ANY, "Hello World") { @@ -28,30 +30,33 @@ SynthFrame::SynthFrame() : wxFrame(NULL, wxID_ANY, "Hello World") { wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); panel->SetSizer(sizer); - - sizer->Add(new wxSlider(panel, OSC_DET_SLIDER, DEFAULT_PRESET.oscDetune, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); - sizer->Add(new wxSlider(panel, OSC2_PIT_SLIDER, DEFAULT_PRESET.osc2Pitch, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); - sizer->AddSpacer(20); - sizer->Add(new wxSlider(panel, FLT_FREQ_SLIDER, DEFAULT_PRESET.filter.freq, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); - sizer->Add(new wxSlider(panel, FLT_Q_SLIDER, DEFAULT_PRESET.filter.Q, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); - sizer->AddSpacer(20); - sizer->Add(new wxSlider(panel, KEY_TRACK_SLIDER, DEFAULT_PRESET.keyTrack, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); - sizer->AddSpacer(20); - sizer->Add(new wxSlider(panel, AMP_ATTACK_SLIDER, DEFAULT_PRESET.ampEnv.attack, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); - sizer->Add(new wxSlider(panel, AMP_DECAY_SLIDER, DEFAULT_PRESET.ampEnv.decay, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); - sizer->Add(new wxSlider(panel, AMP_SUSTAIN_SLIDER, DEFAULT_PRESET.ampEnv.sustain, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); - sizer->Add(new wxSlider(panel, AMP_RELEASE_SLIDER, DEFAULT_PRESET.ampEnv.release, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); - sizer->AddSpacer(20); - sizer->Add(new wxSlider(panel, MOD_ATTACK_SLIDER, DEFAULT_PRESET.modEnv.attack, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); - sizer->Add(new wxSlider(panel, MOD_DECAY_SLIDER, DEFAULT_PRESET.modEnv.decay, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); - sizer->Add(new wxSlider(panel, MOD_SUSTAIN_SLIDER, DEFAULT_PRESET.modEnv.sustain, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); - sizer->Add(new wxSlider(panel, MOD_RELEASE_SLIDER, DEFAULT_PRESET.modEnv.release, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); - sizer->AddSpacer(20); - sizer->Add(new wxSlider(panel, MOD_ENV_FLT_SLIDER, DEFAULT_PRESET.modEnvFltGain, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); - sizer->AddSpacer(20); - sizer->Add(new wxSlider(panel, LFO_FREQ_SLIDER, DEFAULT_PRESET.lfoFreq, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); - sizer->Add(new wxSlider(panel, LFO_PITCH_SLIDER, DEFAULT_PRESET.lfoPitchMod, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); - sizer->Add(new wxSlider(panel, LFO_FILTER_SLIDER, DEFAULT_PRESET.lfoFltMod, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE)); + sizer->AddStretchSpacer(); + sizer->Add(new CCSlider(panel, OSC_DET_SLIDER, DEFAULT_PRESET.oscDetune, "Det"), 1, wxEXPAND); + sizer->Add(new CCSlider(panel, OSC2_PIT_SLIDER, DEFAULT_PRESET.osc2Pitch, "2nd"), 1, wxEXPAND); + sizer->AddStretchSpacer(); + sizer->Add(new CCSlider(panel, FLT_FREQ_SLIDER, DEFAULT_PRESET.filter.freq, "Frq"), 1, wxEXPAND); + sizer->Add(new CCSlider(panel, FLT_Q_SLIDER, DEFAULT_PRESET.filter.Q, "Res"), 1, wxEXPAND); + sizer->AddStretchSpacer(); + sizer->Add(new CCSlider(panel, KEY_TRACK_SLIDER, DEFAULT_PRESET.keyTrack, "Key"), 1, wxEXPAND); + sizer->AddStretchSpacer(); + sizer->Add(new CCSlider(panel, AMP_ATTACK_SLIDER, DEFAULT_PRESET.ampEnv.attack, "aA"), 1, wxEXPAND); + sizer->Add(new CCSlider(panel, AMP_DECAY_SLIDER, DEFAULT_PRESET.ampEnv.decay, "aD"), 1, wxEXPAND); + sizer->Add(new CCSlider(panel, AMP_SUSTAIN_SLIDER, DEFAULT_PRESET.ampEnv.sustain, "aS"), 1, wxEXPAND); + sizer->Add(new CCSlider(panel, AMP_RELEASE_SLIDER, DEFAULT_PRESET.ampEnv.release, "aR"), 1, wxEXPAND); + sizer->AddStretchSpacer(); + sizer->Add(new CCSlider(panel, MOD_ATTACK_SLIDER, DEFAULT_PRESET.modEnv.attack, "mA"), 1, wxEXPAND); + sizer->Add(new CCSlider(panel, MOD_DECAY_SLIDER, DEFAULT_PRESET.modEnv.decay, "mD"), 1, wxEXPAND); + sizer->Add(new CCSlider(panel, MOD_SUSTAIN_SLIDER, DEFAULT_PRESET.modEnv.sustain, "mS"), 1, wxEXPAND); + sizer->Add(new CCSlider(panel, MOD_RELEASE_SLIDER, DEFAULT_PRESET.modEnv.release, "mR"), 1, wxEXPAND); + sizer->AddStretchSpacer(); + sizer->Add(new CCSlider(panel, MOD_ENV_FLT_SLIDER, DEFAULT_PRESET.modEnvFltGain, "Env"), 1, wxEXPAND); + sizer->AddStretchSpacer(); + sizer->Add(new CCSlider(panel, LFO_FREQ_SLIDER, DEFAULT_PRESET.lfoFreq, "LFO"), 1, wxEXPAND); + sizer->Add(new CCSlider(panel, LFO_PITCH_SLIDER, DEFAULT_PRESET.lfoPitchMod, "Pit"), 1, wxEXPAND); + sizer->Add(new CCSlider(panel, LFO_FILTER_SLIDER, DEFAULT_PRESET.lfoFltMod, "Flt"), 1, wxEXPAND); + sizer->AddStretchSpacer(); + + this->FitInside(); Bind(wxEVT_MENU, &SynthFrame::OnAbout, this, wxID_ABOUT); Bind(wxEVT_MENU, &SynthFrame::OnExit, this, wxID_EXIT);