forked from klang-modular/synthapp
parent
5f2086e829
commit
6cd5e452f5
17 changed files with 329 additions and 214 deletions
@ -0,0 +1,22 @@ |
||||
{ |
||||
"configurations": [ |
||||
{ |
||||
"name": "Mac", |
||||
"includePath": [ |
||||
"${workspaceFolder}/**" |
||||
], |
||||
"defines": [ |
||||
"__LITTLE_ENDIAN__" |
||||
], |
||||
"macFrameworkPath": [ |
||||
"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks" |
||||
], |
||||
"compilerPath": "/usr/bin/clang", |
||||
"cStandard": "c17", |
||||
"cppStandard": "c++17", |
||||
"intelliSenseMode": "macos-clang-x64", |
||||
"configurationProvider": "ms-vscode.cmake-tools" |
||||
} |
||||
], |
||||
"version": 4 |
||||
} |
@ -0,0 +1,88 @@ |
||||
#ifndef __SVF_H__ |
||||
#define __SVF_H__ |
||||
|
||||
/* CEM3320/Oberheim/Phrophet-style filter as described here: https://arxiv.org/pdf/2111.05592.pdf */ |
||||
|
||||
#include <math.h> |
||||
|
||||
#include "util.h" |
||||
|
||||
class SVF12 { |
||||
public: |
||||
float frequency; |
||||
float resonance; |
||||
|
||||
typedef struct { |
||||
float lp; // low-pass
|
||||
float bp; // band-pass
|
||||
float hp; // high-pass
|
||||
} Output; |
||||
|
||||
protected: |
||||
float as1, as2; |
||||
|
||||
public: |
||||
SVF12() : as1(0), as2(0) {} |
||||
|
||||
Output tick(float as, float freq, float Q) { |
||||
Output out; |
||||
float kK = tan(M_PI_4 * clamp(freq, 0, 1)); |
||||
float kQ = fmaxf(0, Q); |
||||
|
||||
float kdiv = 1 + kK/kQ + kK*kK; |
||||
out.hp = (as - (1/kQ + kK) * as1 - as2) / kdiv; |
||||
float au = out.hp * kK; |
||||
out.bp = au + as1; |
||||
as1 = au + out.bp; |
||||
au = out.bp * kK; |
||||
out.lp = au + as2; |
||||
as2 = au + out.lp; |
||||
|
||||
return out; |
||||
} |
||||
}; |
||||
|
||||
// 12 and 24 dB/oct
|
||||
class Filter { |
||||
public: |
||||
enum Type { |
||||
TYPE_LP = 0, |
||||
TYPE_BP = 42, |
||||
TYPE_HP = 84 |
||||
}; |
||||
|
||||
enum Slope { |
||||
SLOPE_24 = 0, |
||||
SLOPE_12 = 64, |
||||
}; |
||||
|
||||
typedef struct { |
||||
float freq; |
||||
float Q; |
||||
Type type; |
||||
Slope slope; |
||||
} Settings; |
||||
|
||||
private: |
||||
const Settings* settings; |
||||
SVF12 first, second; |
||||
|
||||
public: |
||||
Filter() = delete; |
||||
Filter(const Settings *settings): settings(settings) {} |
||||
|
||||
inline float tick(float as, float freqMod) { |
||||
SVF12::Output in = first.tick(as, settings->freq + freqMod, settings->Q); |
||||
|
||||
switch(settings->type) { |
||||
case TYPE_LP: |
||||
return settings->slope == SLOPE_24 ? second.tick(in.lp, settings->freq + freqMod, settings->Q).lp : in.lp; |
||||
case TYPE_BP: |
||||
return settings->slope == SLOPE_24 ? second.tick(in.bp, settings->freq + freqMod, settings->Q).bp : in.bp;
|
||||
case TYPE_HP: |
||||
return settings->slope == SLOPE_24 ? second.tick(in.hp, settings->freq + freqMod, settings->Q).hp : in.hp; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
#endif |
@ -1,117 +0,0 @@ |
||||
#ifndef __SVF_H__ |
||||
#define __SVF_H__ |
||||
|
||||
/* CEM3320/Oberheim/Phrophet-style filter as described here: https://arxiv.org/pdf/2111.05592.pdf */ |
||||
|
||||
#include <math.h> |
||||
|
||||
#include "util.h" |
||||
|
||||
class SVF12 { |
||||
protected: |
||||
float as1, as2; |
||||
float kdiv; |
||||
float kK, kQ; |
||||
|
||||
inline void updateCoeffs() { |
||||
kdiv = 1 + kK/kQ + kK*kK; |
||||
} |
||||
|
||||
public: |
||||
typedef struct { |
||||
float hp; // high-pass
|
||||
float bp; // band-pass
|
||||
float lp; // low-pass
|
||||
} Output; |
||||
|
||||
SVF12() { |
||||
as1 = 0; |
||||
as2 = 0; |
||||
kK = tan(M_PI_4); |
||||
kQ = M_SQRT1_2; |
||||
updateCoeffs(); |
||||
} |
||||
|
||||
void setFrequency(float f) { |
||||
kK = tan(M_PI_4 * clamp(f, 0, 1)); |
||||
updateCoeffs(); |
||||
} |
||||
|
||||
void setQ(float q) { |
||||
kQ = q; |
||||
updateCoeffs(); |
||||
} |
||||
|
||||
Output tick(float as) { |
||||
Output out; |
||||
out.hp = (as - (1/kQ + kK) * as1 - as2) / kdiv; |
||||
float au = out.hp * kK; |
||||
out.bp = au + as1; |
||||
as1 = au + out.bp; |
||||
au = out.bp * kK; |
||||
out.lp = au + as2; |
||||
as2 = au + out.lp; |
||||
return out; |
||||
} |
||||
}; |
||||
|
||||
// 12 and 24 dB/oct
|
||||
class SVF { |
||||
public: |
||||
enum Mode {
|
||||
MODE_HP_12, |
||||
MODE_HP_24, |
||||
MODE_BP_12, |
||||
MODE_BP_24, |
||||
MODE_LP_12, |
||||
MODE_LP_24, |
||||
}; |
||||
|
||||
private: |
||||
Mode mode; |
||||
SVF12 first, second; |
||||
|
||||
public: |
||||
SVF() { |
||||
this->mode = MODE_LP_24; |
||||
} |
||||
|
||||
void setMode(Mode mode) { |
||||
this->mode = mode; |
||||
} |
||||
|
||||
void setFrequency(float f) { |
||||
first.setFrequency(f); |
||||
second.setFrequency(f); |
||||
} |
||||
|
||||
void setQ(float q) { |
||||
first.setQ(q); |
||||
second.setQ(q); |
||||
} |
||||
|
||||
float tick(float as) { |
||||
SVF12::Output in = first.tick(as); |
||||
switch(mode) { |
||||
case MODE_HP_12: |
||||
return in.hp; |
||||
|
||||
case MODE_HP_24: |
||||
return second.tick(in.hp).hp; |
||||
|
||||
case MODE_BP_12: |
||||
return in.bp; |
||||
|
||||
case MODE_BP_24: |
||||
return second.tick(in.bp).bp; |
||||
|
||||
case MODE_LP_12: |
||||
return in.lp; |
||||
|
||||
case MODE_LP_24: |
||||
return second.tick(in.lp).lp; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,6 @@ |
||||
#ifndef __GLOBALS_H__ |
||||
#define __GLOBALS_H__ |
||||
|
||||
#define SAMPLE_RATE 48000 |
||||
|
||||
#endif |
@ -0,0 +1,33 @@ |
||||
#ifndef __PART_H__ |
||||
#define __PART_H__ |
||||
|
||||
#include <map> |
||||
#include <unordered_set> |
||||
|
||||
#include "voicemanager.h" |
||||
#include "voice.h" |
||||
#include "preset.h" |
||||
|
||||
class Part { |
||||
public: |
||||
VoiceManager* voiceManager; |
||||
|
||||
Voice::Settings settings; |
||||
|
||||
float pitchBend; |
||||
float modulation; |
||||
|
||||
std::map<uint8_t, Voice*> notes; |
||||
|
||||
void loadPreset(Preset* preset); |
||||
|
||||
void noteOn(int note, int vel); |
||||
void noteOff(int note); |
||||
void control(int cc, int val); |
||||
|
||||
float tick() { |
||||
return 0; |
||||
} |
||||
}; |
||||
|
||||
#endif |
@ -1,13 +1,58 @@ |
||||
#ifndef __PRESET_H__ |
||||
#define __PRESET_H__ |
||||
|
||||
#include "dsp/oscillator.h" |
||||
#include "dsp/filter.h" |
||||
#include "dsp/adsr.h" |
||||
|
||||
struct Preset { |
||||
float cutoff; |
||||
float resonance; |
||||
typedef struct { |
||||
uint8_t attack; |
||||
uint8_t decay; |
||||
uint8_t sustain; |
||||
uint8_t release; |
||||
} Envelope; |
||||
|
||||
ADSR::Envelope ampEnv, fltEnv; |
||||
uint8_t osc1Mode; |
||||
uint8_t osc2Mode; |
||||
uint8_t oscMix; |
||||
|
||||
struct { |
||||
uint8_t type; |
||||
uint8_t slope; |
||||
uint8_t freq; |
||||
uint8_t Q; |
||||
} filter; |
||||
|
||||
Envelope ampEnv; |
||||
Envelope fltEnv; |
||||
}; |
||||
|
||||
static const Preset DEFAULT_PRESET = { |
||||
.osc1Mode = Oscillator::MODE_SAW, |
||||
.osc2Mode = Oscillator::MODE_SAW, |
||||
.oscMix = 0, |
||||
|
||||
.filter = { |
||||
.type = Filter::TYPE_LP, |
||||
.slope = Filter::SLOPE_24, |
||||
.freq = 127, |
||||
.Q = 0, |
||||
}, |
||||
|
||||
.ampEnv = { |
||||
.attack = 0, |
||||
.decay = 0, |
||||
.sustain = 127, |
||||
.release = 0 |
||||
}, |
||||
|
||||
.fltEnv = { |
||||
.attack = 0, |
||||
.decay = 0, |
||||
.sustain = 127, |
||||
.release = 0 |
||||
} |
||||
}; |
||||
|
||||
#endif |
@ -1,19 +1,23 @@ |
||||
#ifndef __SYNTH_H__ |
||||
#define __SYNTH_H__ |
||||
|
||||
#include "voice.h" |
||||
|
||||
const int SAMPLE_RATE = 48000; |
||||
const int NUM_VOICES = 72; |
||||
#include "voicemanager.h" |
||||
#include "part.h" |
||||
|
||||
class Synth { |
||||
public:
|
||||
void noteOn(int note, int velocity); |
||||
void noteOff(int note); |
||||
void control(int code, int value); |
||||
public: |
||||
Synth() { |
||||
for(int i = 0; i < 16; ++i) { |
||||
parts[i].voiceManager = &voiceManager; |
||||
} |
||||
} |
||||
void noteOn(int ch, int note, int vel); |
||||
void noteOff(int ch, int note); |
||||
void control(int ch, int cc, int val); |
||||
|
||||
private: |
||||
Voice voices[NUM_VOICES]; |
||||
VoiceManager voiceManager{}; |
||||
Part parts[16]; |
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,10 @@ |
||||
#ifndef __VOICEMANAGER_H__ |
||||
#define __VOICEMANAGER_H__ |
||||
|
||||
class VoiceManager { |
||||
public: |
||||
private: |
||||
|
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,44 @@ |
||||
#include "synth/globals.h" |
||||
|
||||
#include "synth/part.h" |
||||
|
||||
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); |
||||
} |
||||
|
||||
void Part::loadPreset(Preset* preset) { |
||||
settings.osc1Mode = Oscillator::Mode(preset->osc1Mode); |
||||
settings.osc2Mode = Oscillator::Mode(preset->osc2Mode); |
||||
settings.oscMix = preset->oscMix / 127.0; |
||||
|
||||
settings.filter.type = Filter::Type(preset->filter.type); |
||||
settings.filter.slope = Filter::Slope(preset->filter.slope); |
||||
settings.filter.freq = preset->filter.freq / 127.0; |
||||
settings.filter.Q = preset->filter.Q / 127.0; |
||||
|
||||
settings.ampEnv.attackStep = (1.0 / SAMPLE_RATE) / ccToA(preset->ampEnv.attack);; |
||||
settings.ampEnv.decayStep = (1.0 / SAMPLE_RATE) / ccToDR(preset->ampEnv.decay); |
||||
settings.ampEnv.sustain = preset->ampEnv.sustain / 127.0; |
||||
settings.ampEnv.releaseStep = (1.0 / SAMPLE_RATE) / ccToDR(preset->ampEnv.release); |
||||
|
||||
settings.fltEnv.attackStep = (1.0 / SAMPLE_RATE) / ccToA(preset->fltEnv.attack);; |
||||
settings.fltEnv.decayStep = (1.0 / SAMPLE_RATE) / ccToDR(preset->fltEnv.decay); |
||||
settings.fltEnv.sustain = preset->fltEnv.sustain / 127.0; |
||||
settings.fltEnv.releaseStep = (1.0 / SAMPLE_RATE) / ccToDR(preset->fltEnv.release); |
||||
} |
||||
|
||||
void Part::noteOn(int note, int velocity) { |
||||
|
||||
} |
||||
|
||||
void Part::noteOff(int note) { |
||||
|
||||
} |
||||
|
||||
void Part::control(int code, int value) { |
||||
|
||||
} |
@ -1,13 +1,13 @@ |
||||
#include "synth/synth.h" |
||||
|
||||
void Synth::noteOn(int note, int velocity) { |
||||
void Synth::noteOn(int ch, int note, int vel) { |
||||
|
||||
} |
||||
|
||||
void Synth::noteOff(int note) { |
||||
void Synth::noteOff(int ch, int note) { |
||||
|
||||
} |
||||
|
||||
void Synth::control(int code, int value) { |
||||
void Synth::control(int ch, int cc, int val) { |
||||
|
||||
} |
Loading…
Reference in new issue