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.
 
 
 

135 lines
3.9 KiB

#ifndef __VOICE_H__
#define __VOICE_H__
#include "globals.h"
#include "preset.h"
#include "dsp/oscillator.h"
#include "dsp/filter.h"
#include "dsp/adsr.h"
#include "perf.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;
FilterStereo::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];
FilterStereo 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) {
nano_t started = nanos();
const size_t frameCount = bufferSize / 2;
float basePitchBuf[2];
basePitchBuf[0] = note + pitchMod[0];
basePitchBuf[1] = note + pitchMod[frameCount - 1];
float oscPitchBuf[MAX_OSCS][2];
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;
oscPitchBuf[i][0] = noteToCV(basePitchBuf[0] + detune + transpose);
oscPitchBuf[i][1] = noteToCV(basePitchBuf[1] + detune + transpose);
detune += settings->oscDetune;
}
float pan, panStep;
if(settings->unison > 2) {
pan = 0.f;
panStep = 2.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 = settings->unison > 1 ? ((i & 1) ? settings->oscMix : (1 - settings->oscMix)) : 1.f;
float gain[2];
gain[0] = baseGain * sinf((1.f - pan) * (float) M_PI_2);
gain[1] = baseGain * sinf(pan * (float) M_PI_2);
pan += (i & 1) * panStep;
osc[i].tick(oscBuf, bufferSize, oscPitchBuf[i], gain);
}
float noiseBuf[bufferSize];
if(settings->unison > 2) { // Stereo noise
for(size_t i = 0; i < bufferSize; ++i) {
noiseBuf[i] = (1.f / 2.f) * (whiteNoise() + whiteNoise() + whiteNoise());
}
} else { // Mono noise
for(size_t i = 0; i < bufferSize; i += 2 ) {
float noise = (1.f / 2.f) * (whiteNoise() + whiteNoise() + whiteNoise());
noiseBuf[i] = noise;
noiseBuf[i + 1] = noise;
}
}
for(size_t i = 0; i < bufferSize; ++i) {
oscBuf[i] += settings->noiseMix * (noiseBuf[i] - oscBuf[i]);
}
float keyTrack = settings->keyTrack * (basePitchBuf[0] - 24) * (1.f / 12.f);
float keyTrackEnd = settings->keyTrack * (basePitchBuf[1] - 24) * (1.f / 12.f);
float keyTrackDelta = (keyTrackEnd - keyTrack) / frameCount;
float fltModBuf[frameCount];
for(size_t i = 0; i < frameCount; ++i) {
fltModBuf[i] = settings->modEnvFltGain * adsrMod.tick() + keyTrack + fltMod[i];
keyTrack += keyTrackDelta;
}
filter.tick(oscBuf, out, bufferSize, fltModBuf);
for(size_t i = 0; i < bufferSize; ++i) {
out[i] *= gain * adsrAmp.tick();
}
nano_t finished = nanos();
perfNanos.voice += finished - started;
}
};
#endif