#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