Analog synthesis engine for Klang Modular
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

124 lines
3.9 KiB

#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"
#include "../perf.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) {
nano_t started = nanos();
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];
float step = cvToStep(pitch[0]);
float stepEnd = cvToStep(pitch[1]);
float stepStep = (stepEnd - step) / frameCount;
for(size_t i = 0; i < frameCount; ++i) {
phaseStepBuf[i] = step * driftBuf[i];
step += stepStep;
}
float phaseBuf[frameCount];
for(size_t i = 0; i < frameCount; ++i) {
phaseBuf[i] = phase;
phase += phaseStepBuf[i];
if(unlikely(phase >= 1.f)) {
phase -= 1.f;
}
}
if(*mode == MODE_SINE) {
for(size_t i = 0, j = 0; i < frameCount; ++i, j += 2) {
float value = fastSin(phaseBuf[i]);
out[j] += gain[0] * value;
out[j + 1] += gain[1] * value;
}
} else if(*mode == MODE_SAW) {
for(size_t i = 0, j = 0; i < frameCount; ++i, j += 2) {
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[0] * value;
out[j + 1] += gain[1] * value;
}
} else if (*mode == MODE_SQUARE) {
float antiphaseBuf[frameCount];
for(size_t i = 0; i < frameCount; ++i) {
float antiphase = phaseBuf[i] + 0.5f;
if(unlikely(antiphase >= 1.f)) {
antiphase -= 1;
}
antiphaseBuf[i] = antiphase;
}
for(size_t i = 0, j = 0; i < frameCount; ++i, j += 2) {
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(antiphaseBuf[i], phaseStepBuf[i]); // Layer output of Poly BLEP on top (flop)
out[j] += gain[0] * value;
out[j + 1] += gain[1] * value;
}
}
nano_t finished = nanos();
perfNanos.oscillator += finished - started;
}
inline float polyBlep(float t, float dt) {
// t-t^2/2 +1/2
// 0 < t <= 1
// discontinuities between 0 & 1
if(unlikely(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(unlikely(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