Initial refactor

main
Thor 1 year ago
parent 6574268466
commit 5f2086e829
  1. 2
      .gitignore
  2. 3
      .gitmodules
  3. 3
      .vscode/settings.json
  4. 10
      CMakeLists.txt
  5. 3
      README.md
  6. 76
      include/synth/dsp/adsr.h
  7. 24
      include/synth/dsp/oscillator.h
  8. 63
      include/synth/dsp/svf.h
  9. 26
      include/synth/dsp/util.h
  10. 13
      include/synth/preset.h
  11. 19
      include/synth/synth.h
  12. 23
      include/synth/voice.h
  13. 25
      include/synthapp.h
  14. 53
      include/synthframe.h
  15. 1
      lib/wxWidgets
  16. 10
      src/oscillator.cpp
  17. 26
      src/synth/dsp/adsr.cpp
  18. 13
      src/synth/synth.cpp
  19. 121
      src/synthapp.cpp
  20. 121
      src/synthframe.cpp

2
.gitignore vendored

@ -4,4 +4,4 @@ CMakeCache.txt
CMakeFiles/
*.cmake
main
Makefile
Makefile

3
.gitmodules vendored

@ -4,3 +4,6 @@
[submodule "lib/portmidi"]
path = lib/portmidi
url = https://github.com/PortMidi/portmidi
[submodule "lib/wxWidgets"]
path = lib/wxWidgets
url = https://github.com/wxWidgets/wxWidgets

@ -0,0 +1,3 @@
{
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"
}

@ -7,8 +7,14 @@ set(BUILD_SHARED_LIBS OFF)
set(PA_USE_ASIO ON CACHE BOOL "Enable support for ASIO")
add_subdirectory(lib/portaudio)
add_subdirectory(lib/portmidi)
add_executable(main src/main.cpp src/oscillator.cpp)
add_subdirectory(lib/wxWidgets)
add_executable(main src/synthapp.cpp src/synthframe.cpp src/synth/synth.cpp src/synth/dsp/adsr.cpp)
target_include_directories(main PRIVATE include)
target_link_libraries(main PRIVATE PortAudio)
target_link_libraries(main PRIVATE portmidi)
target_include_directories(main PRIVATE lib/portmidi/porttime)
target_include_directories(main PRIVATE lib/portmidi/porttime)
target_link_libraries(main PRIVATE wxbase)
target_link_libraries(main PRIVATE wxcore)

@ -9,7 +9,6 @@ Build files are generated for various C++ compilers using CMake. For VS Code, yo
PortAudio and PortMidi are included as Git submodules that need to be fetched before building:
```
git submodule init
git submodule update
git submodule update --init --recursive
```

@ -0,0 +1,76 @@
#ifndef __ADSR_H__
#define __ADSR_H__
#include <cmath>
#include "util.h"
class ADSR {
public:
struct Envelope {
float attackStep;
float decayStep;
float sustain;
float releaseStep;
};
ADSR();
void setEnvelope(const Envelope* envelope);
void noteOn();
void noteOff();
float tick() {
float out = curve(gain);
switch(state) {
case IDLE:
break;
case ATTACK:
if(gain < 1) {
gain += envelope->attackStep;
} else {
state = DECAY;
gain = 1;
}
break;
case DECAY:
if(gain > envelope->sustain) {
gain -= envelope->decayStep;
} else {
state = SUSTAIN;
gain = envelope->sustain;
}
break;
case SUSTAIN:
break;
case RELEASE:
if(gain > 0) {
gain -= envelope->releaseStep;
} else {
state = IDLE;
gain = 0;
}
break;
}
return out;
}
private:
enum { IDLE, ATTACK, DECAY, SUSTAIN, RELEASE } state;
static const Envelope DEFAULT_ENVELOPE;
const Envelope* envelope;
float gain;
float curve(float gain) {
return 0.5 - 0.5 * cos(clamp(gain * M_PI, 0, M_PI));
}
};
#endif

@ -1,3 +1,6 @@
#ifndef __OSCILLATOR_H__
#define __OSCILLATOR_H__
/* PolyBLEP oscillator inspired by: https://www.metafunction.co.uk/post/all-about-digital-oscillators-part-2-blits-bleps
* Further info about BLEP at: https://pbat.ch/sndkit/blep/
*/
@ -17,10 +20,17 @@ public:
float driftAmount;
float driftValue;
Oscillator();
Oscillator() {
mode = MODE_SAW;
phase = 0;
phaseStep = (440 * 2 * M_PI) / 44100;
value = 0;
driftAmount = 0.001;
driftValue = 0;
}
float polyBlep(float t) {
double dt = phaseStep / PIx2;
float dt = phaseStep / PIx2;
// t-t^2/2 +1/2
// 0 < t <= 1
@ -48,7 +58,7 @@ public:
float t = phase / PIx2; // Define half phase
if (mode == MODE_SINE) {
value = sin(phase); // No harmonics in sine so no aliasing!! No Poly BLEPs needed!
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 -= polyBlep(t); // Layer output of Poly BLEP on top
@ -59,15 +69,17 @@ public:
value = -1.0; // Flop
}
value += polyBlep(t); // Layer output of Poly BLEP on top (flip)
value -= polyBlep(fmod(t + 0.5, 1.0)); // Layer output of Poly BLEP on top (flop)
value -= polyBlep(fmodf(t + 0.5, 1.0)); // Layer output of Poly BLEP on top (flop)
}
driftValue += 0.01 * ((float) rand() / RAND_MAX - 0.5) - 0.00001 * driftValue;
phase += phaseStep * (1.0 + driftAmount * driftValue);
if(phase >= PIx2) { // wrap if phase angle >=360º
while(phase >= PIx2) { // wrap if phase angle >=360º
phase -= PIx2;
}
return value;
}
};
};
#endif

@ -1,12 +1,13 @@
#ifndef __SVF_H__
#define __SVF_H__
/* CEM3320/Oberheim/Phrophet-style filter as described here: https://arxiv.org/pdf/2111.05592.pdf */
#include <math.h>
inline float clamp(float x, float a, float b) {
return fminf(b, fmaxf(a, x));
}
#include "util.h"
class SVF {
class SVF12 {
protected:
float as1, as2;
float kdiv;
@ -21,11 +22,9 @@ public:
float hp; // high-pass
float bp; // band-pass
float lp; // low-pass
float br; // band-reject
float ap; // all-pass
} Output;
SVF() {
SVF12() {
as1 = 0;
as2 = 0;
kK = tan(M_PI_4);
@ -34,7 +33,7 @@ public:
}
void setFrequency(float f) {
kK = tan(M_PI_2 * clamp(f, 0, 1));
kK = tan(M_PI_4 * clamp(f, 0, 1));
updateCoeffs();
}
@ -52,24 +51,29 @@ public:
au = out.bp * kK;
out.lp = au + as2;
as2 = au + out.lp;
float abr = out.hp + out.lp;
float aap = out.hp + out.lp + (1/kQ) * out.bp;
return out;
}
};
// same filter but two of them chained for 24 dB/oct
class SVFx2 {
// 12 and 24 dB/oct
class SVF {
public:
enum Mode { MODE_HP, MODE_BP, MODE_LP, MODE_BR, MODE_AP };
enum Mode {
MODE_HP_12,
MODE_HP_24,
MODE_BP_12,
MODE_BP_24,
MODE_LP_12,
MODE_LP_24,
};
private:
Mode mode;
SVF first, second;
SVF12 first, second;
public:
SVFx2() {
this->mode = MODE_LP;
SVF() {
this->mode = MODE_LP_24;
}
void setMode(Mode mode) {
@ -87,22 +91,27 @@ public:
}
float tick(float as) {
SVF::Output in = first.tick(as);
SVF12::Output in = first.tick(as);
switch(mode) {
case MODE_HP:
case MODE_HP_12:
return in.hp;
case MODE_HP_24:
return second.tick(in.hp).hp;
case MODE_BP:
case MODE_BP_12:
return in.bp;
case MODE_BP_24:
return second.tick(in.bp).bp;
case MODE_LP:
case MODE_LP_12:
return in.lp;
case MODE_LP_24:
return second.tick(in.lp).lp;
case MODE_BR:
return second.tick(in.br).br;
case MODE_AP:
return second.tick(in.ap).ap;
}
}
};
};
#endif

@ -0,0 +1,26 @@
#ifndef __UTIL_H__
#define __UTIL_H__
#include <cmath>
inline float clamp(float x, float a, float b) {
return fminf(b, fmaxf(a, x));
}
inline float dBToGain(float dB) {
return pow(10, dB / 20.0);
}
inline float noteToFreq(float note) {
return 440 * pow(2, (note - 69) / 12.0);
}
inline float triangle(float phase) {
if(phase < M_PI) {
return phase / M_PI_2 - 1;
} else {
return 1 - (phase - M_PI) / M_PI_2;
}
}
#endif

@ -0,0 +1,13 @@
#ifndef __PRESET_H__
#define __PRESET_H__
#include "dsp/adsr.h"
struct Preset {
float cutoff;
float resonance;
ADSR::Envelope ampEnv, fltEnv;
};
#endif

@ -0,0 +1,19 @@
#ifndef __SYNTH_H__
#define __SYNTH_H__
#include "voice.h"
const int SAMPLE_RATE = 48000;
const int NUM_VOICES = 72;
class Synth {
public:
void noteOn(int note, int velocity);
void noteOff(int note);
void control(int code, int value);
private:
Voice voices[NUM_VOICES];
};
#endif

@ -0,0 +1,23 @@
#ifndef __VOICE_H__
#define __VOICE_H__
#include "preset.h"
#include "dsp/oscillator.h"
#include "dsp/svf.h"
#include "dsp/adsr.h"
class Voice {
public:
static const int NUM_OSCS = 2;
void usePreset(Preset* preset);
private:
ADSR adsrAmp;
ADSR adsrFlt;
Oscillator oscs[NUM_OSCS];
SVF filter;
};
#endif

@ -0,0 +1,25 @@
#ifndef __SYNTHAPP_H__
#define __SYNTHAPP_H__
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <portaudio.h>
#include <portmidi.h>
#include "synth/synth.h"
class SynthApp : public wxApp {
public:
PaStream* paStream;
PmStream* pmStream;
Synth synth;
virtual bool OnInit();
virtual int OnExit();
};
#endif

@ -0,0 +1,53 @@
#ifndef __SYNTHFRAME_H__
#define __SYNTHFRAME_H__
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
class SynthFrame : public wxFrame {
public:
SynthFrame();
private:
enum {
AMP_ATTACK_SLIDER = wxID_TOP,
AMP_DECAY_SLIDER,
AMP_SUSTAIN_SLIDER,
AMP_RELEASE_SLIDER,
FLT_ATTACK_SLIDER,
FLT_DECAY_SLIDER,
FLT_SUSTAIN_SLIDER,
FLT_RELEASE_SLIDER
};
wxSlider* ampAttackSlider;
wxSlider* ampDecaySlider;
wxSlider* ampSustainSlider;
wxSlider* ampReleaseSlider;
wxSlider* fltAttackSlider;
wxSlider* fltDecaySlider;
wxSlider* fltSustainSlider;
wxSlider* fltReleaseSlider;
void OnExit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
void OnAmpAttackScroll(wxScrollEvent& event);
void OnAmpDecayScroll(wxScrollEvent& event);
void OnAmpSustainScroll(wxScrollEvent& event);
void OnAmpReleaseScroll(wxScrollEvent& event);
void OnFltAttackScroll(wxScrollEvent& event);
void OnFltDecayScroll(wxScrollEvent& event);
void OnFltSustainScroll(wxScrollEvent& event);
void OnFltReleaseScroll(wxScrollEvent& event);
wxDECLARE_EVENT_TABLE();
};
#endif

@ -0,0 +1 @@
Subproject commit a242283456ffd0920a2384f6fee45d56a7ecc3a5

@ -1,10 +0,0 @@
#include "oscillator.h"
Oscillator::Oscillator() {
mode = MODE_SAW;
phase = 0;
phaseStep = (440 * 2 * M_PI) / 44100;
value = 0;
driftAmount = 0.005;
driftValue = 0;
}

@ -0,0 +1,26 @@
#include "synth/dsp/adsr.h"
const ADSR::Envelope ADSR::DEFAULT_ENVELOPE = {
.attackStep = 1,
.decayStep = 1,
.sustain = 1,
.releaseStep = 1
};
ADSR::ADSR() {
this->envelope = &DEFAULT_ENVELOPE;
this->state = IDLE;
this->gain = 0;
}
void ADSR::setEnvelope(const Envelope* envelope) {
this->envelope = envelope;
}
void ADSR::noteOn() {
state = ATTACK;
}
void ADSR::noteOff() {
state = RELEASE;
}

@ -0,0 +1,13 @@
#include "synth/synth.h"
void Synth::noteOn(int note, int velocity) {
}
void Synth::noteOff(int note) {
}
void Synth::control(int code, int value) {
}

@ -2,29 +2,13 @@
#include <cmath>
#include <iostream>
#include <portaudio.h>
#include <portmidi.h>
#include <porttime.h>
#include "oscillator.h"
#include "svf.h"
#include "synthapp.h"
#include "synthframe.h"
using namespace std;
#define SAMPLE_RATE 48000
#define NUM_OSCS 7
float noteToFreq(float note) {
return 440 * pow(2, (note - 69) / 12.0);
}
typedef struct {
Oscillator oscs[NUM_OSCS];
SVFx2 filter;
float time;
PmStream* pmStream;
} State;
// PortAudio callback for audio I/O
static int paCallback(
const void *inputBuffer,
@ -34,75 +18,55 @@ static int paCallback(
PaStreamCallbackFlags statusFlags,
void *userData ) {
State *state = (State*) userData;
Synth *state = (Synth*) userData;
float *out = (float*)outputBuffer;
for(int i = 0; i < framesPerBuffer; i++) {
float value = 0;
for(int j = 0; j < NUM_OSCS; ++j) {
value += state->oscs[j].tick();
}
value /= NUM_OSCS;
value = state->filter.tick(value);
value = 0.25 * clamp(value, -1, 1);
*out++ = value;
*out++ = value;
state->time += 0.5 / SAMPLE_RATE;
}
return 0;
}
// PortTimer callback for MIDI I/O
static void ptCallback(PtTimestamp timestamp, void* userData) {
State* state = (State*) userData;
SynthApp* app = (SynthApp*) userData;
PmEvent events[8];
int numEvents = Pm_Read(state->pmStream, events, 8);
int numEvents = Pm_Read(app->pmStream, events, 8);
for(int i = 0; i < numEvents; ++i) {
PmMessage msg = events[i].message;
int cmd = (msg >> 4) & 0xF;
int chan = msg & 0xF;
int type = (msg >> 4) & 0xF;
int ch = msg & 0xF;
if(cmd == 0x9) {
int vel = (msg >> 16) & 0x7F;
if(vel > 0) {
int note = (msg >> 8) & 0x7F;
float centreFreq = noteToFreq(note);
float spreadCoeff = 0.04;
for(int osc = 0, pos = -3; pos <= 3; ++osc, ++pos) {
float freq = noteToFreq(note + spreadCoeff * pos);
state->oscs[osc].phaseStep = (freq * 2 * M_PI) / SAMPLE_RATE;
}
state->time = 0;
}
if(type == 0x8 || (type == 0x9 && Pm_MessageData2(msg) == 0)) { // Note Off
} else if(type == 0x9) { // Note On
int note = Pm_MessageData1(msg);
int vel = Pm_MessageData2(msg);
} else if(cmd == 0xB) {
int ctl = (msg >> 8) & 0x7F;
} else if(type == 0xB) { // Controller Message
int cc = Pm_MessageData1(msg);
int val = Pm_MessageData2(msg);
if(ctl == 0x01) {
int val = (msg >> 16) & 0x7F;
state->filter.setFrequency(val / 256.0);
if(cc == 1 || cc == 74) { // Sound Controller 5: Brightness
/*
float flutterSpread = 0.001 * val / 127.0;
for(int osc = 0; i < NUM_OSCS; ++i) {
state->oscs[i].flutterSpread = flutterSpread;
}
*/
}
}
cout << "MIDI:";
for(int j = 0; j < 24; j += 8) {
cout << " " << hex << ((msg >> j) & 0xFF);
}
cout << endl;
}
}
void handlePMError(PmError err) {
Pt_Stop();
Pa_Terminate();
Pm_Terminate();
cerr << "PortMidi error" << endl;
@ -112,6 +76,7 @@ void handlePMError(PmError err) {
}
void handlePAError(PaError err) {
Pt_Stop();
Pa_Terminate();
Pm_Terminate();
cerr << "PortAudio error" << endl;
@ -120,16 +85,7 @@ void handlePAError(PaError err) {
exit(err);
}
int main(void) {
static State state;
for(int i = 0; i < 7; ++i) {
state.oscs[i].phaseStep = (440 * 2 * M_PI) / SAMPLE_RATE;
state.oscs[i].phase = PIx2 * (float) rand() / RAND_MAX;
}
state.filter.setMode(SVFx2::MODE_LP);
state.filter.setQ(1);
state.time = 0;
bool SynthApp::OnInit() {
// Initialise PortMIDI and PortAudio libraries
PmError pmErr = Pm_Initialize();
if(pmErr != pmNoError) {
@ -189,9 +145,9 @@ int main(void) {
}
// Start MIDI timer and open MIDI input
Pt_Start(1, ptCallback, &state);
Pt_Start(1, ptCallback, this);
PmError pmError = Pm_OpenInput(
&state.pmStream,
&pmStream,
pmDeviceIndex,
NULL,
8,
@ -202,22 +158,21 @@ int main(void) {
}
// Configure/start audio output stream
PaStream *paStream;
PaStreamParameters paStreamParams;
paStreamParams.device = paDeviceIndex;
paStreamParams.channelCount = 2;
paStreamParams.sampleFormat = paFloat32;
paStreamParams.suggestedLatency = 0;
paStreamParams.suggestedLatency = 512.0 / SAMPLE_RATE;
paStreamParams.hostApiSpecificStreamInfo = NULL;
paErr = Pa_OpenStream(
&paStream,
NULL, /* no input channels */
&paStreamParams, /* stereo output */
NULL, // no input channels
&paStreamParams, // stereo output
SAMPLE_RATE,
0, /* frames per buffer */
0, // frames per buffer
paNoFlag,
paCallback,
&state);
&synth);
if(paErr != paNoError) {
handlePAError(paErr);
}
@ -229,11 +184,15 @@ int main(void) {
const PaStreamInfo *paStreamInfo = Pa_GetStreamInfo(paStream);
cout << "Latency: " << round(1000 * paStreamInfo->outputLatency) << " ms" << endl;
// Wait for Return key
cout << "Running - press Enter to exit" << endl;
getchar();
wxFrame *frame = new SynthFrame();
frame->Show(true);
return true;
}
int SynthApp::OnExit() {
Pt_Stop();
paErr = Pa_StopStream(paStream);
PaError paErr = Pa_StopStream(paStream);
if(paErr != paNoError) {
handlePAError(paErr);
}
@ -241,12 +200,14 @@ int main(void) {
if(paErr != paNoError) {
handlePAError(paErr);
}
pmErr = Pm_Close(state.pmStream);
PmError pmErr = Pm_Close(pmStream);
if(pmErr != pmNoError) {
handlePMError(pmErr);
}
Pa_Terminate();
cout << "Terminating" << endl;
return 0;
}
}
wxIMPLEMENT_APP(SynthApp);

@ -0,0 +1,121 @@
#include <iostream>
#include "synthframe.h"
#include "synthapp.h"
wxDECLARE_APP(SynthApp);
float ccToA(int x) {
return 5.0 * exp(9.2 * (x / 127.0) - 9.2);
}
float ccToDR(int x) {
return 5.0 * exp(7.0 * (x / 127.0) - 7.0);
}
SynthFrame::SynthFrame() : wxFrame(NULL, wxID_ANY, "Hello World") {
wxMenu *menuFile = new wxMenu;
menuFile->Append(wxID_EXIT);
wxMenu *menuHelp = new wxMenu;
menuHelp->Append(wxID_ABOUT);
wxMenuBar *menuBar = new wxMenuBar;
menuBar->Append(menuFile, "&File");
menuBar->Append(menuHelp, "&Help");
SetMenuBar(menuBar);
CreateStatusBar();
SetStatusText("Welcome to wxWidgets!");
wxPanel* panel = new wxPanel(this);
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
panel->SetSizer(sizer);
sizer->Add(new wxSlider(panel, AMP_ATTACK_SLIDER, 63, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE));
sizer->Add(new wxSlider(panel, AMP_DECAY_SLIDER, 63, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE));
sizer->Add(new wxSlider(panel, AMP_SUSTAIN_SLIDER, 63, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE));
sizer->Add(new wxSlider(panel, AMP_RELEASE_SLIDER, 63, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE));
sizer->Add(new wxSlider(panel, FLT_ATTACK_SLIDER, 63, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE));
sizer->Add(new wxSlider(panel, FLT_DECAY_SLIDER, 63, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE));
sizer->Add(new wxSlider(panel, FLT_SUSTAIN_SLIDER, 63, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE));
sizer->Add(new wxSlider(panel, FLT_RELEASE_SLIDER, 63, 0, 127, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_INVERSE));
Bind(wxEVT_MENU, &SynthFrame::OnAbout, this, wxID_ABOUT);
Bind(wxEVT_MENU, &SynthFrame::OnExit, this, wxID_EXIT);
}
void SynthFrame::OnExit(wxCommandEvent& event) {
Close(true);
}
void SynthFrame::OnAbout(wxCommandEvent& event) {
wxMessageBox("This is a wxWidgets Hello World example",
"About Hello World", wxOK | wxICON_INFORMATION);
}
void SynthFrame::OnAmpAttackScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
float seconds = ccToA(event.GetPosition());
//app.state.adsrAmp.attackStep = (1.0 / 48000.0) / seconds;
}
void SynthFrame::OnAmpDecayScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
float seconds = ccToDR(event.GetPosition());
//app.state.adsrAmp.decayStep = (1.0 / 48000.0) / seconds;
}
void SynthFrame::OnAmpSustainScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
float gain = event.GetPosition() / 127.0;
std::cout << "Gain: " << gain << std::endl;
//app.state.adsrAmp.sustain = gain;
}
void SynthFrame::OnAmpReleaseScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
float seconds = ccToDR(event.GetPosition());
//app.state.adsrAmp.releaseStep = (1.0 / 48000.0) / seconds;
}
void SynthFrame::OnFltAttackScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
float seconds = ccToA(event.GetPosition());
//app.state.adsrFlt.attackStep = (1.0 / 48000.0) / seconds;
}
void SynthFrame::OnFltDecayScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
float seconds = ccToDR(event.GetPosition());
//app.state.adsrFlt.decayStep = (1.0 / 48000.0) / seconds;
}
void SynthFrame::OnFltSustainScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
float gain = event.GetPosition() / 127.0;
//app.state.adsrFlt.sustain = gain;
}
void SynthFrame::OnFltReleaseScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
float seconds = ccToDR(event.GetPosition());
//app.state.adsrFlt.releaseStep = (1.0 / 48000.0) / seconds;
}
wxBEGIN_EVENT_TABLE(SynthFrame, wxFrame)
EVT_COMMAND_SCROLL(SynthFrame::AMP_ATTACK_SLIDER, SynthFrame::OnAmpAttackScroll)
EVT_COMMAND_SCROLL(SynthFrame::AMP_DECAY_SLIDER, SynthFrame::OnAmpDecayScroll)
EVT_COMMAND_SCROLL(SynthFrame::AMP_SUSTAIN_SLIDER, SynthFrame::OnAmpSustainScroll)
EVT_COMMAND_SCROLL(SynthFrame::AMP_RELEASE_SLIDER, SynthFrame::OnAmpReleaseScroll)
EVT_COMMAND_SCROLL(SynthFrame::FLT_ATTACK_SLIDER, SynthFrame::OnFltAttackScroll)
EVT_COMMAND_SCROLL(SynthFrame::FLT_DECAY_SLIDER, SynthFrame::OnFltDecayScroll)
EVT_COMMAND_SCROLL(SynthFrame::FLT_SUSTAIN_SLIDER, SynthFrame::OnFltSustainScroll)
EVT_COMMAND_SCROLL(SynthFrame::FLT_RELEASE_SLIDER, SynthFrame::OnFltReleaseScroll)
wxEND_EVENT_TABLE()
Loading…
Cancel
Save