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
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
|
|
|