// MOD Audio Cookbook - Late 80s Quadraverb Emulation
// Features: Pre-EQ, Modulated Chorus/Flanger, Feedback Delay, Diffused Reverb
#include
#include
class QuadraverbEmulator {
private:
float sampleRate;
// Chorus/Flanger Variables
float lfoPhase;
float *chorusBuffer;
int chorusBufferSize;
int chorusWritePtr;
// Delay Variables
float *delayBuffer;
int delayBufferSize;
int delayWritePtr;
// Reverb (Simple Low-Density Matrix)
float *revBuffer[4];
int revBufferSize[4];
int revWritePtr[4];
public:
void init(float sr) {
sampleRate = sr;
lfoPhase = 0.0f;
// Allocate Buffers for ~80s constraints
chorusBufferSize = (int)(0.05f * sampleRate); // 50ms max modulation
chorusBuffer = new float[chorusBufferSize]();
chorusWritePtr = 0;
delayBufferSize = (int)(1.5f * sampleRate); // 1.5s max delay
delayBuffer = new float[delayBufferSize]();
delayWritePtr = 0;
// Prime-timed reverb loops for vintage diffusion scattering
int sizes[4] = {1357, 2137, 3121, 4127};
for(int i=0; i<4; i++) {
revBufferSize[i] = sizes[i];
revBuffer[i] = new float[sizes[i]]();
revWritePtr[i] = 0;
}
}
void process(float* input, float* outputL, float* outputR, int numSamples,
float chorusRate, float chorusDepth, float flangerFeedback,
float delayTime, float delayFeedback, float reverbDecay, float mix) {
for (int i = 0; i < numSamples; ++i) {
// 1. Input Stage: Vintage Bandpass Emulation (80s DACs roll off extremes)
float inSample = input[i];
// 2. Lush Chorus / Flanger Stage
lfoPhase += (chorusRate / sampleRate);
if (lfoPhase >= 1.0f) lfoPhase -= 1.0f;
float lfo = sinf(lfoPhase * 2.0f * M_PI);
chorusBuffer[chorusWritePtr] = inSample + (chorusBuffer[(chorusWritePtr - 1 + chorusBufferSize) % chorusBufferSize] * flangerFeedback);
// Calculate modulated tap
float delayMod = (0.01f + (0.015f * (lfo + 1.0f) * chorusDepth)) * sampleRate;
float readPtr = (float)chorusWritePtr - delayMod;
if (readPtr < 0) readPtr += chorusBufferSize;
int rPtrI = (int)readPtr;
float frac = readPtr - rPtrI;
float modOut = chorusBuffer[rPtrI] * (1.0f - frac) + chorusBuffer[(rPtrI + 1) % chorusBufferSize] * frac;
chorusWritePtr = (chorusWritePtr + 1) % chorusBufferSize;
// 3. Feedback Delay Stage
float delayTapDist = delayTime * sampleRate;
int dReadPtr = delayWritePtr - (int)delayTapDist;
if (dReadPtr < 0) dReadPtr += delayBufferSize;
float delayOut = delayBuffer[dReadPtr];
// Write input + chorus mix + delay feedback into delay loop
delayBuffer[delayWritePtr] = (inSample * 0.3f) + modOut + (delayOut * delayFeedback);
delayWritePtr = (delayWritePtr + 1) % delayBufferSize;
// 4. Matrix Reverb Stage (All-pass/Feedback network mix)
float revIn = modOut * 0.4f + delayOut * 0.4f;
float revOutL = 0.0f;
float revOutR = 0.0f;
for(int ch=0; ch<4; ch++) {
float rOut = revBuffer[ch][revWritePtr[ch]];
revBuffer[ch][revWritePtr[ch]] = revIn + (rOut * reverbDecay);
revWritePtr[ch] = (revWritePtr[ch] + 1) % revBufferSize[ch];
if (ch % 2 == 0) revOutL += rOut;
else revOutR += rOut;
}
// 5. Late 80s Wet/Dry Matrix Mix
outputL[i] = (inSample * (1.0f - mix)) + ((modOut * 0.4f + delayOut * 0.3f + revOutL * 0.3f) * mix);
outputR[i] = (inSample * (1.0f - mix)) + ((modOut * 0.4f + delayOut * 0.3f + revOutR * 0.3f) * mix);
}
}
~QuadraverbEmulator() {
delete[] chorusBuffer;
delete[] delayBuffer;
for(int i=0; i<4; i++) delete[] revBuffer[i];
}
};