forked from klang-modular/synthapp
parent
6574268466
commit
5f2086e829
20 changed files with 507 additions and 128 deletions
@ -0,0 +1,3 @@ |
|||||||
|
{ |
||||||
|
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools" |
||||||
|
} |
@ -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 |
@ -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) { |
||||||
|
|
||||||
|
} |
@ -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…
Reference in new issue