Backing up changes

main
Thor 12 months ago
parent f860ebfef1
commit 09df1b535f
  1. 54
      include/synth/channel.h
  2. 128
      include/synth/dsp/filter.h
  3. 82
      include/synth/dsp/oscillator.h
  4. 7
      include/synth/globals.h
  5. 45
      include/synth/synth.h
  6. 74
      include/synth/voice.h
  7. 5
      include/synthapp.h
  8. 4
      lib/synth/CMakeLists.txt
  9. 0
      lib/synth/include/cc.h
  10. 75
      lib/synth/include/channel.h
  11. 9
      lib/synth/include/dsp/adsr.h
  12. 74
      lib/synth/include/dsp/delayline.h
  13. 124
      lib/synth/include/dsp/filter.h
  14. 107
      lib/synth/include/dsp/fm.h
  15. 0
      lib/synth/include/dsp/frame.h
  16. 101
      lib/synth/include/dsp/oscillator.h
  17. 34
      lib/synth/include/dsp/phys.h
  18. 8
      lib/synth/include/dsp/reverb.h
  19. 19
      lib/synth/include/dsp/stereodelayline.h
  20. 8
      lib/synth/include/dsp/stereosap.h
  21. 9
      lib/synth/include/globals.h
  22. 26
      lib/synth/include/luts.h
  23. 0
      lib/synth/include/preset.h
  24. 65
      lib/synth/include/synth.h
  25. 48
      lib/synth/include/util.h
  26. 127
      lib/synth/include/voice.h
  27. 2
      lib/synth/include/voicemanager.h
  28. 34
      lib/synth/src/channel.cpp
  29. 2
      lib/synth/src/dsp/adsr.cpp
  30. 4
      lib/synth/src/dsp/filter.cpp
  31. 2
      lib/synth/src/dsp/oscillator.cpp
  32. 2
      lib/synth/src/dsp/reverb.cpp
  33. 19
      lib/synth/src/luts.cpp
  34. 12
      lib/synth/src/preset.cpp
  35. 8
      lib/synth/src/synth.cpp
  36. 15
      lib/synth/src/voice.cpp
  37. 2
      lib/synth/src/voicemanager.cpp
  38. 18
      src/synth/dsp/centlut.cpp
  39. 10
      src/synth/dsp/filterlut.cpp
  40. 18
      src/synth/dsp/notelut.cpp
  41. 16
      src/synthapp.cpp
  42. 54
      src/synthframe.cpp

@ -1,54 +0,0 @@
#ifndef __PART_H__
#define __PART_H__
#include <unordered_map>
#include <set>
#include "globals.h"
#include "voicemanager.h"
#include "voice.h"
#include "preset.h"
#include "dsp/frame.h"
class Channel {
public:
VoiceManager::channel_t number;
VoiceManager* voiceManager;
Voice::Settings settings;
Channel() : volume(1), pitchBend(0), modulation(0), lfoPhase(randPhase()) {}
void loadPreset(const Preset* preset);
void noteOn(int note, int vel);
void noteOff(int note);
void control(int cc, int val);
frame tick() {
frame out{0, 0};
float lfo = sin(lfoPhase);
Voice** voices = voiceManager->getChannelVoices(number);
for(Voice** voice = voices; *voice != NULL; ++voice) {
float voiceOut = (*voice)->tick(pitchBend + settings.lfoPitchMod * lfo, settings.lfoFltMod * lfo);
out.l += sinf((1 - (*voice)->pan) * (float) M_PI_2) * voiceOut;
out.r += sinf((*voice)->pan * (float) M_PI_2) * voiceOut;
}
out *= volume;
lfoPhase += settings.lfoStep;
if(lfoPhase >= (float) PIx2) {
lfoPhase -= PIx2;
}
return out;
}
private:
float volume;
float pitchBend;
float modulation;
float lfoPhase;
};
#endif

@ -1,128 +0,0 @@
#ifndef __SVF_H__
#define __SVF_H__
/* CEM3320/Oberheim/Phrophet-style filter as described here: https://arxiv.org/pdf/2111.05592.pdf */
#include <cmath>
#include <cfloat>
#include <iostream>
#include <algorithm>
#include <functional>
#include "../globals.h"
#include "util.h"
#define FILTER_OVERSAMPLE 4
#define FILTER_K_SCALE (2 * CV_FREQ_MIN / (FILTER_OVERSAMPLE * SAMPLE_RATE))
#define FILTER_CV_MAX 9.25 // MIDI note "135" (19912.126958213178287 Hz)
inline float cvToK(float cv) {
return tanf((float) M_PI_2 * std::min(0.5f, std::min(1.f / FILTER_OVERSAMPLE, (float) FILTER_K_SCALE * exp2f(cv))));
}
class SVF12 {
public:
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) {}
inline Output tick(float in, float kK, float kQ) {
Output out;
float kdiv = 1 + kK/kQ + kK*kK;
out.hp = (in - (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;
}
};
class Oversampler {
private:
float kK, kQ = (float) M_SQRT1_2;
SVF12 aa1, aa2;
public:
Oversampler() : kK(tanf((float) M_PI_2 * 1.0f / FILTER_OVERSAMPLE)) {}
inline float tick(float in, std::function<float(float)> process) {
if(FILTER_OVERSAMPLE < 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);
// }
float out = process(aa2.tick(aa1.tick(FILTER_OVERSAMPLE * in, kK, kQ).lp, kK, kQ).lp);
for(int i = 1; i < FILTER_OVERSAMPLE; ++i) {
process(aa2.tick(aa1.tick(0, kK, kQ).lp, kK, kQ).lp);
}
return out;
}
}
};
// 12 and 24 dB/oct
class Filter {
public:
enum Type {
TYPE_LP = 0,
TYPE_BP,
TYPE_HP
};
enum Slope {
SLOPE_24 = 0,
SLOPE_12,
};
typedef struct {
float freq;
float res;
Type type;
Slope slope;
} Settings;
private:
Settings const * settings;
SVF12 fltA, fltB;
Oversampler os;
public:
void assign(Settings const * settings);
inline float tick(float in, float freqMod) {
float kK = cvToK(settings->freq + freqMod);
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

@ -1,82 +0,0 @@
#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/
*/
#include <cstdlib>
#include <math.h>
#include "util.h"
class Oscillator {
public:
enum Mode { MODE_SINE = 0, MODE_SAW, MODE_SQUARE };
Mode const * mode;
float phase; // current waveform phase angle (radians)
float value; // current amplitude value
float driftAmount;
float driftValue;
Oscillator() : phase(randPhase()), value(0), driftAmount(0.001f), driftValue(0) {}
void assign(Mode const * mode);
// Generate next output sample and advance the phase angle
inline float tick(float cv) {
const float phaseStep = (float) (2 * CV_FREQ_MIN) * exp2f(cv) * (float) SAMPLE_RATE_INV;
const float t = phase * (float) PIx2_INV; // Define half phase
const float dt = phaseStep * (float) PIx2_INV;
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.0f * t) - 1.0f; // Render naive waveshape
value -= polyBlep(t, dt); // Layer output of Poly BLEP on top
} else if (*mode == MODE_SQUARE) {
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.5f, 1.0f), dt); // Layer output of Poly BLEP on top (flop)
}
driftValue += 0.005f * whiteNoise() - 0.00001f * driftValue;
phase += phaseStep * (1.0f + driftAmount * driftValue);
if(phase >= (float) PIx2) { // wrap if phase angle >=360º
phase -= PIx2;
}
return value;
}
inline float polyBlep(float t, float dt) {
// t-t^2/2 +1/2
// 0 < t <= 1
// discontinuities between 0 & 1
if (t < dt) {
t /= dt;
return t + t - t * t - 1.0f;
}
// t^2/2 +t +1/2
// -1 <= t <= 0
// discontinuities between -1 & 0
else if (t > 1.0f - dt) {
t = (t - 1.0f) / dt;
return t * t + t + t + 1.0f;
}
// no discontinuities
// 0 otherwise
else return 0.0;
}
};
#endif

@ -1,7 +0,0 @@
#ifndef __GLOBALS_H__
#define __GLOBALS_H__
#define SAMPLE_RATE 44100
#define SAMPLE_RATE_INV (1.0 / SAMPLE_RATE)
#endif

@ -1,45 +0,0 @@
#ifndef __SYNTH_H__
#define __SYNTH_H__
#include "voicemanager.h"
#include "channel.h"
#include "dsp/frame.h"
#include "dsp/reverb.h"
class Synth {
public:
Synth() {
for(VoiceManager::channel_t i = 0; i < 16; ++i) {
channels[i].number = i;
channels[i].voiceManager = &voiceManager;
channels[i].loadPreset(&DEFAULT_PRESET);
}
}
void noteOn(int ch, int note, int vel);
void noteOff(int ch, int note);
void control(int ch, int cc, int val);
inline frame tick() {
frame out{};
frame reverbBus{};
for(auto& channel : channels) {
frame channelOut = channel.tick();
out += channelOut;
reverbBus += channel.settings.reverb * channelOut;
}
out += reverb.tick(reverbBus);
out *= 0.125f;
return out;
}
private:
VoiceManager voiceManager{};
Channel channels[16];
Reverb reverb;
};
#endif

@ -1,74 +0,0 @@
#ifndef __VOICE_H__
#define __VOICE_H__
#include "globals.h"
#include "preset.h"
#include "dsp/oscillator.h"
#include "dsp/filter.h"
#include "dsp/adsr.h"
class Voice {
public:
typedef struct {
float unison;
Oscillator::Mode osc1Mode;
Oscillator::Mode osc2Mode;
float oscMix;
float oscDetune;
float osc2Pitch;
float noiseMix;
Filter::Settings filter;
ADSR::Envelope ampEnv;
ADSR::Envelope modEnv;
float modEnvFltGain;
float keyTrack;
float lfoStep;
float lfoPitchMod;
float lfoFltMod;
float reverb;
} Settings;
private:
Settings const * settings;
ADSR adsrAmp;
ADSR adsrMod;
Oscillator osc1;
Oscillator osc2;
Filter filter;
public:
int note;
float gain;
float pan;
float detune;
void reset();
void assign(Settings const * settings);
void noteOn(int note, float detune = 0, float gain = 1.0, float pan = 0.5);
void noteOff();
inline bool isIdle() { return adsrAmp.isIdle(); }
inline float tick(float pitchMod, float fltMod) {
const float basePitch = note + pitchMod + detune;
const float osc1CV = noteToCV(basePitch - settings->oscDetune);
const float osc2CV = noteToCV(basePitch + settings->oscDetune + settings->osc2Pitch);
float out = 0;
out += (1 - settings->oscMix) * osc1.tick(osc1CV);
out += settings->oscMix * osc2.tick(osc2CV);
out += settings->noiseMix * ((1.f / 3.f) * (whiteNoise() + whiteNoise() + whiteNoise()) - out);
const float keyTrackVal = settings->keyTrack * (note - 72) / 12.f;
out = filter.tick(out, settings->modEnvFltGain * adsrMod.tick() + keyTrackVal + fltMod);
out *= gain * adsrAmp.tick();
return out;
}
};
#endif

@ -1,6 +1,8 @@
#ifndef __SYNTHAPP_H__
#define __SYNTHAPP_H__
#include <mutex>
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
@ -10,13 +12,14 @@
#include <portaudio.h>
#include <portmidi.h>
#include "synth/synth.h"
#include "synth.h"
class SynthApp : public wxApp {
public:
PaStream* paStream;
PmStream* pmStream;
Synth synth;
std::mutex synthMutex;
virtual bool OnInit();
virtual int OnExit();

@ -17,16 +17,14 @@ if(MSVC)
endif()
add_library(synth
src/luts.cpp
src/synth.cpp
src/voicemanager.cpp
src/channel.cpp
src/preset.cpp
src/voice.cpp
src/dsp/notelut.cpp
src/dsp/centlut.cpp
src/dsp/oscillator.cpp
src/dsp/filter.cpp
src/dsp/filterlut.cpp
src/dsp/adsr.cpp
src/dsp/reverb.cpp)
target_include_directories(synth PUBLIC include)

@ -0,0 +1,75 @@
#ifndef __PART_H__
#define __PART_H__
#include <unordered_map>
#include <set>
#include "globals.h"
#include "voicemanager.h"
#include "voice.h"
#include "preset.h"
#include "dsp/frame.h"
#include "luts.h"
class Channel {
public:
VoiceManager::channel_t number;
VoiceManager* voiceManager;
Voice::Settings settings;
Channel() : volume(1), pitchBend(0), modulation(0), lfoPhase(randFrac()) {}
void loadPreset(const Preset* preset);
void noteOn(int note, int vel);
void noteOff(int note);
void control(int cc, int val);
bool tick(float *out, const size_t bufferSize) {
Voice** voices = voiceManager->getChannelVoices(number);
if(*voices == NULL) {
return false;
}
const size_t numFrames = bufferSize / 2;
float lfo[numFrames];
for(size_t i = 0; i < numFrames; ++i) {
lfo[i] = fastSin(lfoPhase);
lfoPhase += settings.lfoStep;
lfoPhase -= floorf(lfoPhase);
}
float pitchMod[numFrames];
for(size_t i = 0; i < numFrames; ++i) {
pitchMod[i] = settings.lfoPitchMod * lfo[i];
}
float fltMod[numFrames];
for(size_t i = 0; i < numFrames; ++i) {
fltMod[i] = settings.lfoFltMod * lfo[i];
}
std::fill(out, out + bufferSize, 0.f);
for(Voice** voice = voices; *voice != NULL; ++voice) {
float voiceOut[numFrames];
(*voice)->tick(voiceOut, numFrames, pitchMod, fltMod);
for(size_t i = 0; i < bufferSize; ++i) {
out[i] += voiceOut[i];
}
}
return true;
}
private:
float volume;
float pitchBend;
float modulation;
float lfoPhase;
};
#endif

@ -3,7 +3,7 @@
#include <cmath>
#include "util.h"
#include "../util.h"
class ADSR {
public:
@ -33,7 +33,7 @@ public:
case ATTACK:
if(t < 1) {
t += env->attackStep;
last = curve(t);
last = t*t;
} else {
state = DECAY;
t = 1;
@ -44,7 +44,8 @@ public:
case DECAY:
if(t > 0) {
t -= env->decayStep;
last = env->sustain + (1 - env->sustain) * curve(t);
//last = env->sustain + (1 - env->sustain) * curve(t);
last = env->sustain + (1 - env->sustain) * t*t;
} else {
state = SUSTAIN;
t = 1;
@ -59,7 +60,7 @@ public:
case RELEASE:
if(t > 0) {
t -= env->releaseStep;
return last * curve(t);
return last * t*t;
} else {
state = IDLE;
t = 0;

@ -0,0 +1,74 @@
#ifndef __DELAYLINE_H__
#define __DELAYLINE_H__
#include <cstddef>
template <size_t CAPACITY>
class DelayLine {
private:
float samples[CAPACITY] = {0};
size_t cursor;
public:
size_t size;
DelayLine(size_t size = CAPACITY) : size(size) {}
inline float get() {
while(cursor >= size) {
cursor -= size;
}
return samples[cursor];
}
inline float tick() {
++cursor;
while(cursor >= size) {
cursor -= size;
}
return samples[cursor];
}
inline float tick(float in) {
while(cursor >= size) {
cursor -= size;
}
samples[cursor++] = in;
if(cursor == size) {
cursor = 0;
}
return samples[cursor];
}
inline void inject(float in, size_t pos) {
size_t index = cursor + pos;
while(index >= size) {
index -= size;
}
samples[index] = in;
}
inline void inject(float* in, size_t num, size_t pos = 0) {
size_t index = cursor + pos;
while(index >= size) {
index -= size;
}
for(size_t i = 0; i < num; ++i) {
samples[index++] = *(in++);
if(index == size) {
index = 0;
}
}
}
inline float tap(size_t n) {
size_t index = cursor + n;
if(index < size) {
return samples[index];
} else {
return samples[index - size];
}
}
};
#endif

@ -0,0 +1,124 @@
#ifndef __SVF_H__
#define __SVF_H__
/* CEM3320/Oberheim/Phrophet-style filter as described here: https://arxiv.org/pdf/2111.05592.pdf */
#include <cmath>
#include <cfloat>
#include <iostream>
#include <algorithm>
#include <functional>
#include "../globals.h"
#include "../util.h"
#include "../luts.h"
#define FILTER_K_SCALE (2 * CV_FREQ_MIN / SAMPLE_RATE)
inline float cvToK(float cv) {
return tanf((float) M_PI_2 * std::min(0.5f, (float) FILTER_K_SCALE * exp2f(cv)));
}
class SVF12 {
protected:
float hp, bp, lp;
float as1, as2;
public:
SVF12() : hp(0), bp(0), lp(0), as1(0), as2(0) {}
inline void tick(float *in, float *hpo, float *bpo, float *lpo, size_t bufferSize, float *kK, float *kQ) {
float kQ_1[bufferSize];
for(size_t i = 0; i < bufferSize; ++i) {
kQ_1[i] = 1.f / kQ[i];
}
float kMul[bufferSize];
for(size_t i = 0; i < bufferSize; ++i) {
kMul[i] = 1.f / (1.f + kK[i]*kQ_1[i] + kK[i]*kK[i]);
}
for(size_t i = 0; i < bufferSize; ++i) {
hp = (in[i] - (kQ_1[i] + kK[i]) * as1 - as2) * kMul[i];
float au = hp * kK[i];
bp = au + as1;
as1 = au + bp;
au = bp * kK[i];
lp = au + as2;
as2 = au + lp;
hpo && (hpo[i] = hp);
bpo && (bpo[i] = bp);
lpo && (lpo[i] = lp);
}
}
};
// 12 and 24 dB/oct
class Filter {
public:
enum Type {
TYPE_LP = 0,
TYPE_BP,
TYPE_HP
};
enum Slope {
SLOPE_12 = 0,
SLOPE_24,
};
typedef struct {
float freq;
float res;
Type type;
Slope slope;
} Settings;
private:
Settings const * settings;
SVF12 fltA, fltB;
float freq, res;
public:
void assign(Settings const * settings);
inline void tick(float *in, float *out, size_t bufferSize, float *freqMod) {
float dt = 1.f / (float) bufferSize;
float kK[bufferSize];
float dFreq = dt * (settings->freq - freq);
for(size_t i = 0; i < bufferSize; i++) {
kK[i] = fastCVtoK(freq + freqMod[i]);
freq += dFreq;
}
float kQ[bufferSize];
float dRes = dt * (settings->res - res);
for(size_t i = 0; i < bufferSize; i++) {
kQ[i] = (float) M_SQRT1_2 + res;
res += dRes;
}
switch(settings->type) {
case TYPE_LP:
fltA.tick(in, NULL, NULL, out, bufferSize, kK, kQ);
if(settings->slope == SLOPE_24) {
fltB.tick(out, NULL, NULL, out, bufferSize, kK, kQ);
}
break;
case TYPE_BP:
fltA.tick(in, NULL, out, NULL, bufferSize, kK, kQ);
if(settings->slope == SLOPE_24) {
fltB.tick(out, NULL, out, NULL, bufferSize, kK, kQ);
}
break;
case TYPE_HP:
fltA.tick(in, out, NULL, NULL, bufferSize, kK, kQ);
if(settings->slope == SLOPE_24) {
fltB.tick(out, out, NULL, NULL, bufferSize, kK, kQ);
}
break;
}
}
};
#endif

@ -0,0 +1,107 @@
#ifndef __FM_H__
#define __FM_H__
#include <array>
#include "../util.h"
class FM {
private:
float phase1 = randPhase(), phase2 = randPhase(), phase3 = randPhase(), phase4 = randPhase(), phase5 = randPhase(), phase6 = randPhase();
float fb1 = 0.f, fb2 = 0.f;
public:
struct Settings {
struct {
float pitch;
float level;
} ops[6];
};
Settings settings;
float tick(float pitch, float fmEnv) {
float step3 = cvToStep(pitch);
float step2 = cvToStep(pitch + 3.807354922057604f); // Frequency Coarse = 14
float step1 = cvToStep(pitch + 0.007195501404204f); // Detune = +7
float step6 = cvToStep(pitch);
float step5 = cvToStep(pitch - 0.007231569231076f); // Detune = -7
float step4 = cvToStep(pitch);
float osc3 = fmEnv * 0.333f * sinf(phase3);
float osc2 = fmEnv * 0.333f * sinf(phase2 + (float) M_PI * osc3);
float osc1 = sinf(phase1 + (float) M_PI * osc2);
float osc6 = fmEnv * 1.0f * sinf(phase6 + 0.0f * (float) M_PI * (fb1 + fb2));
float osc5 = fmEnv * 1.0f * sinf(phase5 + (float) M_PI * osc6);
float osc4 = sinf(phase4 + (float) M_PI * osc5);
fb2 = fb1;
fb1 = osc4;
phase1 += step1;
if(phase1 >= (float) PIx2) {
phase1 -= PIx2;
}
phase2 += step2;
if(phase2 >= (float) PIx2) {
phase2 -= PIx2;
}
phase3 += step3;
if(phase3 >= (float) PIx2) {
phase3 -= PIx2;
}
phase4 += step4;
if(phase4 >= (float) PIx2) {
phase4 -= PIx2;
}
phase5 += step5;
if(phase5 >= (float) PIx2) {
phase5 -= PIx2;
}
phase6 += step6;
if(phase6 >= (float) PIx2) {
phase6 -= PIx2;
}
return osc1 + osc4;
}
};
/*
op6.detune 14
op6.output_level 79
op6.frequency_coarse 1
op5.detune 0
op5.output_level 99
op5.frequency_coarse 1
op4.detune 7
op4.output_level 89
op4.frequency_coarse 1
op3.detune 7
op3.output_level 99
op3.frequency_coarse 1
op2.detune 7
op2.output_level 58
op2.frequency_coarse 14
op1.detune 10
op1.output_level 99
op1.frequency_coarse 1
algorithm 4
feedback 6 [0..7]
RANGES
opx.output_level [0, 99]
opX.frequency_coarse [0, 31] 0.50 - 31.00 X
op6.frequency_fine [0, 99] 1.00 - 1.99 X
opX.detune [0, 14] [-7, +7]
feedback [0, 7]
*/
#endif

@ -0,0 +1,101 @@
#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/
*/
#include <cstdlib>
#include <math.h>
#include "../util.h"
#include "../luts.h"
class Oscillator {
public:
enum Mode { MODE_SINE = 0, MODE_SAW, MODE_SQUARE };
Mode const * mode;
float phase; // current waveform phase angle (radians)
float value; // current amplitude value
float driftAmount;
float driftValue;
Oscillator() : phase(randFrac()), value(0), driftAmount(0.001f), driftValue(0) {}
void assign(Mode const * mode);
// Generate next output sample and advance the phase angle
inline void tick(float* out, size_t bufferSize, float *pitch, float* gain) {
size_t frameCount = bufferSize / 2;
float driftBuf[frameCount];
for(size_t i = 0; i < frameCount; ++i) {
driftValue += 0.005f * whiteNoise() - 0.00001f * driftValue;
driftBuf[i] = 1.0f + driftAmount * driftValue;
}
float phaseStepBuf[frameCount];
for(size_t i = 0; i < frameCount; ++i) {
phaseStepBuf[i] = cvToStep(pitch[i]) * driftBuf[i];
}
float phaseBuf[frameCount];
for(size_t i = 0; i < frameCount; ++i) {
phaseBuf[i] = phase;
phase += phaseStepBuf[i];
phase -= floorf(phase);
}
if(*mode == MODE_SINE) {
for(size_t i = 0, j = 0; i < frameCount; ++i) {
float value = fastSin(phaseBuf[i]);
out[j++] += gain[j & 1] * value;
out[j++] += gain[j & 1] * value;
}
} else if(*mode == MODE_SAW) {
for(size_t i = 0, j = 0; i < frameCount; ++i) {
float value = (2.0f * phaseBuf[i]) - 1.0f; // Render naive waveshape
value -= polyBlep(phaseBuf[i], phaseStepBuf[i]); // Layer output of Poly BLEP on top
out[j++] += gain[j & 1] * value;
out[j++] += gain[j & 1] * value;
}
} else if (*mode == MODE_SQUARE) {
for(size_t i = 0, j = 0; i < frameCount; ++i) {
float value = phaseBuf[i] < 0.5f ? 1.f : -1.f; // Render naive waveshape
value += polyBlep(phaseBuf[i], phaseStepBuf[i]); // Layer output of Poly BLEP on top (flip)
value -= polyBlep(fmodf(phaseBuf[i] + 0.5f, 1.0f), phaseStepBuf[i]); // Layer output of Poly BLEP on top (flop)
out[j++] = gain[j & 1] * value;
out[j++] = gain[j & 1] * value;
}
}
}
inline float polyBlep(float t, float dt) {
// t-t^2/2 +1/2
// 0 < t <= 1
// discontinuities between 0 & 1
if(t < dt) {
t /= dt;
return t + t - t * t - 1.0f;
}
// t^2/2 +t +1/2
// -1 <= t <= 0
// discontinuities between -1 & 0
else if(t > 1.0f - dt) {
t = (t - 1.0f) / dt;
return t * t + t + t + 1.0f;
}
// no discontinuities
// 0 otherwise
else {
return 0.0;
}
}
};
#endif

@ -0,0 +1,34 @@
#ifndef __PHYS_H__
#define __PHYS_H__
#include "../util.h"
#include "delayline.h"
class Physical {
private:
DelayLine<MAX_SAMPLE_PERIOD> dl;
float lp = 0, lp2 = 0;
public:
inline float tick(float pitch) {
int period = roundf(cvToPeriod(pitch));
dl.size = period;
float out = dl.get();
lp *= 0.99f;
lp += 0.90f * (out - lp);
dl.inject(lp, 0);
dl.tick();
lp2 += 0.01f * (out - lp2);
return lp2;
}
inline void pluck() {
float tmp[512];
for(int i = 0; i < 512; ++i) {
tmp[i] = 8.f * (whiteNoise() + whiteNoise() + whiteNoise() + whiteNoise());
}
dl.inject(tmp, 512);
}
};
#endif

@ -5,11 +5,11 @@
#include <algorithm>
#include "globals.h"
#include "frame.h"
#include "delayline.h"
#include "sap.h"
#include "stereodelayline.h"
#define REVERB_FRAMES 131072
#define REVERB_FRAMES (3 * SAMPLE_RATE)
#define REVERB_TAPS 256
class Reverb {
@ -19,7 +19,7 @@ private:
float lg, rg;
};
DelayLine<REVERB_FRAMES> dl;
StereoDelayLine<REVERB_FRAMES> dl;
Tap taps[REVERB_TAPS];
float lpfL = 0.f, lpfR = 0.f;
float hpfL = 0.f, hpfR = 0.f;

@ -1,19 +1,28 @@
#ifndef __DELAYLINE_H__
#define __DELAYLINE_H__
#ifndef __STEREODELAYLINE_H__
#define __STEREODELAYLINE_H__
#include <cstddef>
#include "frame.h"
template <size_t CAPACITY>
class DelayLine {
class StereoDelayLine {
private:
const size_t size;
frame samples[CAPACITY] = {{0, 0}};
size_t cursor;
public:
DelayLine(size_t size = CAPACITY) : size(size) {}
size_t size;
StereoDelayLine(size_t size = CAPACITY) : size(size) {}
inline frame tick() {
++cursor;
if(cursor == size) {
cursor = 0;
}
return samples[cursor];
}
inline frame tick(frame in) {
samples[cursor++] = in;

@ -5,18 +5,18 @@
#include <cstddef>
#include "delayline.h"
#include "stereodelayline.h"
template <size_t CAPACITY>
class SAP {
class StereoSAP {
private:
const size_t size;
DelayLine<CAPACITY> dl;
StereoDelayLine<CAPACITY> dl;
public:
float gain = 0.7;
SAP(size_t size = CAPACITY) : size(size) {}
StereoSAP(size_t size = CAPACITY) : size(size) {}
inline frame tick(frame in) {
frame fb = in + dl.tap(0) * gain;

@ -0,0 +1,9 @@
#ifndef __GLOBALS_H__
#define __GLOBALS_H__
#define SAMPLE_RATE 96000
#define SAMPLE_RATE_INV (1.0 / SAMPLE_RATE)
#define FILTER_CV_MAX 9.25 // MIDI note "135" (19912.126958213178287 Hz)
#endif

@ -0,0 +1,26 @@
#ifndef __LUTS_H__
#define __LUTS_H__
#include <algorithm>
#include "globals.h"
#include "util.h"
#define SIN_LUT_SIZE 256
#define K_LUT_SIZE 256
extern float sinLUT[SIN_LUT_SIZE];
extern float kLUT[K_LUT_SIZE];
void genLUTs();
inline float fastSin(float t) {
t += randFrac() * (1.f / SIN_LUT_SIZE);
return sinLUT[(size_t) (t * SIN_LUT_SIZE) & (SIN_LUT_SIZE - 1)];
}
inline float fastCVtoK(float cv) {
return kLUT[std::min(K_LUT_SIZE - (size_t) 1, (size_t) (cv * (K_LUT_SIZE - 1) * (1.f / (float) FILTER_CV_MAX)))];
}
#endif

@ -0,0 +1,65 @@
#ifndef __SYNTH_H__
#define __SYNTH_H__
#include "voicemanager.h"
#include "channel.h"
#include "dsp/frame.h"
#include "dsp/reverb.h"
#include "luts.h"
class Synth {
public:
Synth() {
genLUTs();
for(VoiceManager::channel_t i = 0; i < 16; ++i) {
channels[i].number = i;
channels[i].voiceManager = &voiceManager;
channels[i].loadPreset(&DEFAULT_PRESET);
}
}
void noteOn(int ch, int note, int vel);
void noteOff(int ch, int note);
void control(int ch, int cc, int val);
inline void tick(float* out, const size_t bufferSize) {
std::fill(out, out + bufferSize, 0.f);
float reverbBus[bufferSize];
std::fill(reverbBus, reverbBus + bufferSize, 0.f);
for(auto& channel : channels) {
float chOut[bufferSize];
if(!channel.tick(chOut, bufferSize)) {
continue;
}
for(size_t i = 0; i < bufferSize; ++i) {
out[i] += chOut[i];
}
for(size_t i = 0; i < bufferSize; ++i) {
reverbBus[i] += channel.settings.reverb * chOut[i];
}
}
for(size_t i = 0; i < bufferSize; i += 2) {
frame f = {reverbBus[i], reverbBus[i + 1]};
f = reverb.tick(f);
out[i] += f.l;
out[i + 1] += f.r;
}
for(size_t i = 0; i < bufferSize; ++i) {
out[i] *= 0.125f;
}
}
private:
VoiceManager voiceManager{};
Channel channels[16];
Reverb reverb;
};
#endif

@ -1,32 +1,35 @@
#ifndef __UTIL_H__
#define __UTIL_H__
#include <cstdio>
#include <cmath>
#include <cfloat>
#include <chrono>
#include "../globals.h"
#include "globals.h"
#define unlikely(cond) __builtin_expect((cond), 0)
#define likely(cond) __builtin_expect((cond), 1)
#define PIx2 (2 * M_PI)
#define PIx2_INV (1.0 / PIx2)
#define CV_FREQ_MIN 32.703195662574829 // MIDI note 24 (C1)
#define MAX_SAMPLE_PERIOD ((int) (SAMPLE_RATE / CV_FREQ_MIN + 0.5))
inline float noteToCV(float note) {
return (note - 24) / 12.f;
inline float fakeSin(float x) {
x = 4.f * (x - 0.5f);
return x * (2.f - fabsf(x));
}
inline float clamp(float x, float a, float b) {
return fminf(b, fmaxf(a, x));
}
inline float triangle(float phase) {
if(phase < (float) M_PI) {
return phase / (float) M_PI_2 - 1;
} else {
return 1 - (phase - (float) M_PI) / (float) M_PI_2;
}
// Get timestamp in nanoseconds
inline uint64_t nanos() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
}
inline float whiteNoise() {
@ -45,21 +48,30 @@ inline float randFrac() {
static uint32_t x2 = 0xE1E9F0A7;
x1 ^= x2;
float value = x2 / (float) 0x100000000;
float value = x2 * (1.f / 0x100000000);
x2 += x1;
return value;
}
inline float randPhase() {
return (float) PIx2 * randFrac();
inline float triangle(float phase) {
if(phase < 0.5f) {
return 2.f * phase - 1;
} else {
return 1 - 2.f * (phase - 0.5f);
}
}
// Get time stamp in nanoseconds.
inline uint64_t nanos() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
inline float cvToStep(float cv) {
return (float) CV_FREQ_MIN * exp2f(cv) * (float) SAMPLE_RATE_INV;
}
inline float noteToCV(float note) {
return note / 12.f;
}
inline float cvToPeriod(float cv) {
return (float) SAMPLE_RATE / ((float) CV_FREQ_MIN * exp2f(cv));
}
#endif

@ -0,0 +1,127 @@
#ifndef __VOICE_H__
#define __VOICE_H__
#include "globals.h"
#include "preset.h"
#include "dsp/oscillator.h"
#include "dsp/filter.h"
#include "dsp/adsr.h"
#define MAX_OSCS 8
class Voice {
public:
typedef struct {
size_t unison;
Oscillator::Mode osc1Mode;
Oscillator::Mode osc2Mode;
float oscMix;
float oscDetune;
float osc2Pitch;
float noiseMix;
Filter::Settings filter;
ADSR::Envelope ampEnv;
ADSR::Envelope modEnv;
float modEnvFltGain;
float keyTrack;
float lfoStep;
float lfoPitchMod;
float lfoFltMod;
float reverb;
} Settings;
private:
Settings const * settings;
ADSR adsrAmp;
ADSR adsrMod;
Oscillator osc[MAX_OSCS];
Filter filter;
public:
int note;
float velocity;
float gain;
void reset();
void assign(Settings const * settings);
void noteOn(int note, float velocity = 1, float gain = 1.0f);
void noteOff();
inline bool isIdle() { return adsrAmp.isIdle(); }
inline void tick(float *out, const size_t bufferSize, float *pitchMod, float *fltMod) {
const size_t frameCount = bufferSize / 2;
float basePitchBuf[frameCount];
for(size_t i = 0; i < frameCount; ++i) {
basePitchBuf[i] = note + pitchMod[i];
}
float oscCVBuf[MAX_OSCS][frameCount];
float detune = -settings->oscDetune * 0.5f * (settings->unison - 1);
for(size_t i = 0; i < settings->unison; ++i) {
float transpose = (i & 1) ? settings->osc2Pitch : 0;
for(size_t j = 0; j < frameCount; ++j) {
oscCVBuf[i][j] = noteToCV(basePitchBuf[i] + detune + transpose);
}
detune += settings->oscDetune;
}
float pan, panStep;
if(settings->unison > 1) {
pan = 0.f;
panStep = 1.f / (settings->unison - 1);
} else {
pan = 0.5f;
panStep = 0.f;
}
float oscBuf[bufferSize];
std::fill(oscBuf, oscBuf + bufferSize, 0.f);
for(size_t i = 0; i < settings->unison; ++i) {
float baseGain = (i & 1) ? settings->oscMix : (1 - settings->oscMix);
float gain[2];
gain[0] = sinf((1.f - pan) * (float) M_PI_2);
gain[1] = sinf(pan * (float) M_PI_2);
pan += panStep;
osc[i].tick(oscBuf, bufferSize, oscCVBuf[i], gain);
}
float noiseBuf[bufferSize];
if(settings->unison > 1) { // Stereo noise
for(size_t i = 0; i < bufferSize; ++i) {
noiseBuf[i] = (1.f / 3.f) * (whiteNoise() + whiteNoise() + whiteNoise());
}
} else { // Mono noise
for(size_t i = 0; i < bufferSize; ) {
float noise = (1.f / 3.f) * (whiteNoise() + whiteNoise() + whiteNoise());
noiseBuf[i++] = noise;
noiseBuf[i++] = noise;
}
}
for(size_t i = 0; i < bufferSize; ++i) {
oscBuf[i] += settings->noiseMix * (noiseBuf[i] - oscBuf[i]);
}
const float keyTrackVal = settings->keyTrack * (note - 24) / 12.f;
float fltModBuf[frameCount];
for(size_t i = 0; i < frameCount; ++i) {
fltModBuf[i] = settings->modEnvFltGain * adsrMod.tick() + keyTrackVal + fltMod[i];
}
filter.tick(oscBuf, out, bufferSize, fltModBuf);
for(size_t i = 0; i < bufferSize; ++i) {
out[i] *= gain * adsrAmp.tick();
}
}
};
#endif

@ -3,7 +3,7 @@
#include <unordered_set>
#include "synth/voice.h"
#include "voice.h"
#define NUM_VOICES 32

@ -1,16 +1,16 @@
#include <iostream>
#include "synth/globals.h"
#include "synth/cc.h"
#include "globals.h"
#include "cc.h"
#include "synth/channel.h"
#include "channel.h"
inline float midiToNote(int note) {
return note + 24;
inline int midiToNote(int note) {
return note - 24;
}
inline float timeToStep(float t) {
return SAMPLE_RATE_INV / t;
return (float) SAMPLE_RATE_INV / t;
}
inline float ccToA(int x) {
@ -22,7 +22,7 @@ inline float ccToDR(int x) {
}
inline float ccToLFOStep(int x) {
return (float) PIx2 * 0.1f * expf((1 + x) * 6.907755278982137f / 128.0f) * SAMPLE_RATE_INV;
return (float) 0.1f * expf((1 + x) * 6.907755278982137f / 128.0f) * (float) SAMPLE_RATE_INV;
}
inline float ccToLFOPitchMod(int x) {
@ -34,7 +34,7 @@ inline float ccToLFOFltMod(int x) {
}
void Channel::loadPreset(const Preset * preset) {
settings.unison = 1 + floorf(preset->unison * 4.f / 128.f);
settings.unison = 1 + floorf(preset->unison * 8.f / 128.f);
settings.osc1Mode = Oscillator::Mode(preset->osc1Mode);
settings.osc2Mode = Oscillator::Mode(preset->osc2Mode);
@ -43,8 +43,8 @@ void Channel::loadPreset(const Preset * preset) {
settings.oscMix = preset->oscMix / 127.f;
settings.noiseMix = preset->noiseMix / 127.f;
settings.filter.type = Filter::Type(preset->filter.type);
settings.filter.slope = Filter::Slope(preset->filter.slope);
settings.filter.type = Filter::Type(floorf(3.f * preset->filter.type / 128.f));
settings.filter.slope = Filter::Slope(floorf(2.f * preset->filter.slope / 128.f));
settings.filter.freq = (float) FILTER_CV_MAX * preset->filter.freq / 127.f;
settings.filter.res = preset->filter.Q / 31.75f;
@ -68,18 +68,17 @@ void Channel::loadPreset(const Preset * preset) {
}
void Channel::noteOn(int note, int velocity) {
int range = 2 * (settings.unison - 1);
for(int i = -range; i <= range; i += 4) {
Voice* const voice = voiceManager->get(number);
voice->assign(&settings);
voice->noteOn(midiToNote(note), i * settings.oscDetune, 1.f / sqrtf(settings.unison), range ? 0.5f + 0.5f * i / range : 0.5f);
}
//printf("on: midi=%d note=%d\n", note, midiToNote(note));
Voice* const voice = voiceManager->get(number);
voice->assign(&settings);
voice->noteOn(midiToNote(note), velocity / 127.f, 1.f, 0.5f);
}
void Channel::noteOff(int note) {
Voice** voices = voiceManager->getChannelVoices(number);
for(Voice** voice = voices; *voice != NULL; ++voice) {
if((*voice)->note == midiToNote(note)) {
//printf("off: midi=%d note=%d\n", note, midiToNote(note));
(*voice)->noteOff();
}
}
@ -109,7 +108,6 @@ void Channel::control(int code, int value) {
case CC_FLT_Q: // Timbre / Harmonic Content (Standard MIDI)
settings.filter.res = value / 31.75f;
printf("Q=%f\n", M_SQRT1_2 + (double) settings.filter.res);
break;
case CC_AMP_REL: // Release Time (Standard MIDI)
@ -161,7 +159,7 @@ void Channel::control(int code, int value) {
break;
case CC_UNISON: // Unison Amount
settings.unison = 1 + floorf(value * 4.f / 128.f);
settings.unison = 1 + floorf(value * 8.f / 128.f);
break;
case CC_OSC_MIX: // Oscillator 1/2 Mix

@ -1,4 +1,4 @@
#include "synth/dsp/adsr.h"
#include "dsp/adsr.h"
void ADSR::reset() {
state = IDLE;

@ -1,5 +1,7 @@
#include "synth/dsp/filter.h"
#include "dsp/filter.h"
void Filter::assign(Settings const * settings) {
this->settings = settings;
freq = settings->freq;
res = settings->res;
}

@ -1,4 +1,4 @@
#include "synth/dsp/oscillator.h"
#include "dsp/oscillator.h"
void Oscillator::assign(Mode const * mode) {
this->mode = mode;

@ -1,6 +1,6 @@
#include <cmath>
#include "synth/dsp/reverb.h"
#include "dsp/reverb.h"
Reverb::Reverb() {
size_t interval = REVERB_FRAMES / REVERB_TAPS - 4096 / REVERB_TAPS;

@ -0,0 +1,19 @@
#include <cmath>
//#include <cstdio>
#include "luts.h"
#include "dsp/filter.h"
float sinLUT[SIN_LUT_SIZE];
float kLUT[K_LUT_SIZE];
void genLUTs() {
for(size_t i = 0; i < SIN_LUT_SIZE; ++i) {
sinLUT[i] = sin((i * 2 * M_PI) / SIN_LUT_SIZE);
}
for(size_t i = 0; i < K_LUT_SIZE; ++i) {
kLUT[i] = cvToK(i * FILTER_CV_MAX / (K_LUT_SIZE - 1));
}
}

@ -1,4 +1,4 @@
#include "synth/preset.h"
#include "preset.h"
const Preset DEFAULT_PRESET = {
.unison = 1,
@ -6,7 +6,7 @@ const Preset DEFAULT_PRESET = {
.osc1Mode = Oscillator::MODE_SAW,
.osc2Mode = Oscillator::MODE_SAW,
.oscMix = 64,
.oscDetune = 10,
.oscDetune = 6,
.osc2Pitch = 0,
.noiseMix = 0,
@ -20,9 +20,9 @@ const Preset DEFAULT_PRESET = {
.ampEnv = {
.attack = 0,
.decay = 80,
.decay = 56,
.sustain = 64,
.release = 0
.release = 56
},
.modEnv = {
@ -35,8 +35,8 @@ const Preset DEFAULT_PRESET = {
.modEnvFltGain = 56,
.keyTrack = 127,
.lfoFreq = 64,
.lfoPitchMod = 16,
.lfoPitchMod = 0,
.lfoFltMod = 64,
.reverb = 32
.reverb = 16
};

@ -1,18 +1,18 @@
#include <cstdio>
#include "synth/synth.h"
#include "synth.h"
void Synth::noteOn(int ch, int note, int vel) {
channels[ch].noteOn(note, vel);
printf("Note On: ch=%d note=%d vel=%d\n", ch, note, vel);
channels[ch].noteOn(note, vel);
}
void Synth::noteOff(int ch, int note) {
channels[ch].noteOff(note);
printf("Note Off: ch=%d note=%d\n", ch, note);
channels[ch].noteOff(note);
}
void Synth::control(int ch, int cc, int val) {
channels[ch].control(cc, val);
printf("Controller: ch=%d cc=%d val=%d\n", ch, cc, val);
channels[ch].control(cc, val);
}

@ -1,4 +1,4 @@
#include "synth/voice.h"
#include "voice.h"
void Voice::reset() {
adsrAmp.reset();
@ -9,15 +9,20 @@ void Voice::assign(Settings const * settings) {
this->settings = settings;
adsrAmp.assign(&settings->ampEnv);
adsrMod.assign(&settings->modEnv);
osc1.assign(&settings->osc1Mode);
osc2.assign(&settings->osc2Mode);
for(size_t i = 0; i < MAX_OSCS; ++i) {
if(i & 1) {
osc[i].assign(&settings->osc2Mode);
} else {
osc[i].assign(&settings->osc1Mode);
}
}
filter.assign(&settings->filter);
}
void Voice::noteOn(int note, float detune, float gain, float pan) {
void Voice::noteOn(int note, float velocity, float gain, float pan) {
this->note = note;
this->velocity = velocity;
this->gain = gain;
this->detune = detune;
this->pan = pan;
adsrAmp.noteOn();
adsrMod.noteOn();

@ -1,7 +1,7 @@
#include <cstdio>
#include <algorithm>
#include "synth/voicemanager.h"
#include "voicemanager.h"
#define VOICE_SCORE_MSB (8 * sizeof(score_t) - 1)

@ -1,18 +0,0 @@
float centLUT[] = {
1.0000000, 1.0004549, 1.0009100, 1.0013654, 1.0018209, 1.0022767, 1.0027326, 1.0031888,
1.0036452, 1.0041018, 1.0045586, 1.0050156, 1.0054728, 1.0059302, 1.0063878, 1.0068456,
1.0073037, 1.0077620, 1.0082204, 1.0086790, 1.0091379, 1.0095969, 1.0100563, 1.0105158,
1.0109755, 1.0114354, 1.0118955, 1.0123559, 1.0128164, 1.0132772, 1.0137382, 1.0141994,
1.0146607, 1.0151223, 1.0155841, 1.0160462, 1.0165083, 1.0169708, 1.0174334, 1.0178963,
1.0183593, 1.0188227, 1.0192862, 1.0197498, 1.0202137, 1.0206778, 1.0211421, 1.0216067,
1.0220715, 1.0225364, 1.0230016, 1.0234669, 1.0239326, 1.0243984, 1.0248644, 1.0253307,
1.0257971, 1.0262637, 1.0267307, 1.0271977, 1.0276650, 1.0281326, 1.0286002, 1.0290682,
1.0295364, 1.0300047, 1.0304732, 1.0309421, 1.0314111, 1.0318803, 1.0323497, 1.0328194,
1.0332892, 1.0337592, 1.0342295, 1.0347000, 1.0351708, 1.0356417, 1.0361128, 1.0365841,
1.0370557, 1.0375276, 1.0379995, 1.0384717, 1.0389441, 1.0394168, 1.0398897, 1.0403627,
1.0408360, 1.0413095, 1.0417832, 1.0422572, 1.0427313, 1.0432056, 1.0436803, 1.0441550,
1.0446301, 1.0451053, 1.0455807, 1.0460564, 1.0465323, 1.0470084, 1.0474846, 1.0479612,
1.0484380, 1.0489149, 1.0493921, 1.0498694, 1.0503471, 1.0508249, 1.0513029, 1.0517812,
1.0522597, 1.0527384, 1.0532173, 1.0536964, 1.0541759, 1.0546554, 1.0551351, 1.0556152,
1.0560954, 1.0565759, 1.0570565, 1.0575374, 1.0580184, 1.0584998, 1.0589813, 1.0594631
};

@ -1,10 +0,0 @@
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
};

@ -1,18 +0,0 @@
float noteLUT[] = {
8.1757994, 8.6619568, 9.1770239, 9.7227182, 10.3008614, 10.9133825, 11.5623255, 12.2498569,
12.9782715, 13.7500000, 14.5676174, 15.4338531, 16.3515987, 17.3239136, 18.3540478, 19.4454365,
20.6017227, 21.8267651, 23.1246510, 24.4997139, 25.9565430, 27.5000000, 29.1352348, 30.8677063,
32.7031975, 34.6478271, 36.7080956, 38.8908730, 41.2034454, 43.6535301, 46.2493019, 48.9994278,
51.9130859, 55.0000000, 58.2704697, 61.7354126, 65.4063950, 69.2956543, 73.4161911, 77.7817459,
82.4068909, 87.3070602, 92.4986038, 97.9988556, 103.8261719, 110.0000000, 116.5409393, 123.4708252,
130.8127899, 138.5913086, 146.8323822, 155.5634918, 164.8137817, 174.6141205, 184.9972076, 195.9977112,
207.6523438, 220.0000000, 233.0818787, 246.9416504, 261.6255798, 277.1826172, 293.6647644, 311.1269836,
329.6275635, 349.2282410, 369.9944153, 391.9954224, 415.3046875, 440.0000000, 466.1637573, 493.8833008,
523.2511597, 554.3652344, 587.3295288, 622.2539673, 659.2551270, 698.4564819, 739.9888306, 783.9908447,
830.6093750, 880.0000000, 932.3275146, 987.7666016, 1046.5023193, 1108.7304688, 1174.6590576, 1244.5079346,
1318.5102539, 1396.9129639, 1479.9776611, 1567.9816895, 1661.2187500, 1760.0000000, 1864.6550293, 1975.5332031,
2093.0046387, 2217.4609375, 2349.3181152, 2489.0158691, 2637.0205078, 2793.8259277, 2959.9553223, 3135.9633789,
3322.4375000, 3520.0000000, 3729.3100586, 3951.0664062, 4186.0092773, 4434.9218750, 4698.6362305, 4978.0317383,
5274.0410156, 5587.6518555, 5919.9106445, 6271.9267578, 6644.8750000, 7040.0000000, 7458.6201172, 7902.1328125,
8372.0185547, 8869.8437500, 9397.2724609, 9956.0634766, 10548.0820312, 11175.3037109, 11839.8212891, 12543.8535156
};

@ -1,9 +1,10 @@
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <mutex>
#include "synth/globals.h"
#include "synth/dsp/util.h"
#include "globals.h"
#include "util.h"
#include "synthapp.h"
#include "synthframe.h"
@ -30,13 +31,15 @@ static int paCallback(
firstPACallback = false;
}
app->synthMutex.lock();
PmEvent events[16];
int numEvents = Pm_Read(app->pmStream, events, 16);
for(int i = 0; i < numEvents; ++i) {
PmMessage msg = events[i].message;
int type = (msg >> 4) & 0xF;
int ch = msg & 0xF;
//int ch = msg & 0xF;
int ch = 0;
if(type == 0x8 || (type == 0x9 && Pm_MessageData2(msg) == 0)) { // Note Off
int note = Pm_MessageData1(msg);
@ -55,12 +58,9 @@ static int paCallback(
float *out = (float*) outputBuffer;
uint64_t started = nanos();
for(unsigned long i = 0; i < framesPerBuffer; i++) {
frame f = app->synth.tick();
*out++ = f.l;
*out++ = f.r;
}
app->synth.tick(out, framesPerBuffer * 2);
uint64_t finished = nanos();
app->synthMutex.unlock();
synthNanos += finished - started;
synthSamples += framesPerBuffer;

@ -1,7 +1,7 @@
#include <iostream>
#include "synth/cc.h"
#include "synth/preset.h"
#include "cc.h"
#include "preset.h"
#include "synthframe.h"
#include "synthapp.h"
@ -83,127 +83,177 @@ void SynthFrame::OnAbout(wxCommandEvent& event) {
void SynthFrame::OnUnisonScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_UNISON, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnOsc1ModeScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_OSC1MDE, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnOsc2ModeScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_OSC2MDE, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnOscDetScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_OSC_DET, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnOsc2PitScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_OSC2PIT, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnOscMixScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_OSC_MIX, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnNoiseMixScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_NOI_MIX, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnFltTypeScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_FLT_TYP, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnFltSlopeScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_FLT_SLP, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnFltFreqScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_FLT_FRQ, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnFltQScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_FLT_Q, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnKeyTrackScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_KEY_TRK, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnAmpAttackScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_AMP_ATK, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnAmpDecayScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_AMP_DEC, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnAmpSustainScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_AMP_SUS, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnAmpReleaseScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_AMP_REL, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnFltAttackScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_FLT_ATK, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnFltDecayScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_FLT_DEC, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnFltSustainScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_FLT_SUS, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnFltReleaseScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_FLT_REL, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnModEnvFltGainScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_MOD_FLT, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnLFOFreqScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_LFO_FRQ, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnLFOPitchScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_LFO_PIT, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnLFOFilterScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_LFO_FLT, event.GetPosition());
app.synthMutex.unlock();
}
void SynthFrame::OnReverbScroll(wxScrollEvent& event) {
SynthApp& app = wxGetApp();
app.synthMutex.lock();
app.synth.control(0, CC_RVB_SND, event.GetPosition());
app.synthMutex.unlock();
}
wxBEGIN_EVENT_TABLE(SynthFrame, wxFrame)

Loading…
Cancel
Save