#pragma once #include #include #include class Delay { public: void prepare(double sr) { sampleRate = sr; int maxSamples = static_cast(sr * 2.0); bufferL.resize(maxSamples, 0.0f); bufferR.resize(maxSamples, 0.0f); writePos = 0; } void setParameters(float timeMs, float fb, float mx, bool pingpong = false, bool invert = false, bool flatten = false) { timeSamplesF = timeMs * 0.001f * static_cast(sampleRate); float bufMax = static_cast(bufferL.size()) - 1.0f; timeSamplesF = std::clamp(timeSamplesF, 1.0f, bufMax); feedback = std::clamp(fb, 0.0f, 0.95f); mix = std::clamp(mx, 0.0f, 1.0f); pingPong = pingpong; pingSign = invert ? -1 : 1; // Flatten reduces the bounce *width* by 60% (0.4 of full) without // touching the echo tail — tail length is governed solely by feedback. pingStrength = flatten ? 0.4f : 1.0f; timeSamplesI = static_cast(std::round(timeSamplesF)); if (timeSamplesI < 1) timeSamplesI = 1; } void process(float& left, float& right) { int bufSize = static_cast(bufferL.size()); float readPosF = static_cast(writePos) - timeSamplesF; while (readPosF < 0.0f) readPosF += static_cast(bufSize); int i0 = static_cast(readPosF) % bufSize; int i1 = (i0 + 1) % bufSize; float frac = readPosF - std::floor(readPosF); if (pingPong) { // Mono echo line. The tail length is set by feedback alone; the // ping-pong bounce is applied as alternating L/R panning whose // width is scaled by pingStrength, so flattening narrows the image // without shortening the number of repeats. float out = bufferL[i0] * (1.0f - frac) + bufferL[i1] * frac; float in = 0.5f * (left + right); bufferL[writePos] = in + out * feedback; pingCounter += 1; if (pingCounter >= timeSamplesI) { pingCounter -= timeSamplesI; pingPhase = -pingPhase; } float s = pingStrength * static_cast(pingPhase * pingSign); float lPan = 0.5f + 0.5f * s; float rPan = 0.5f - 0.5f * s; float wet = out * mix; left = left * (1.0f - mix) + wet * lPan; right = right * (1.0f - mix) + wet * rPan; } else { float outL = bufferL[i0] * (1.0f - frac) + bufferL[i1] * frac; float outR = bufferR[i0] * (1.0f - frac) + bufferR[i1] * frac; bufferL[writePos] = left + outL * feedback; bufferR[writePos] = right + outR * feedback; left = left * (1.0f - mix) + outL * mix; right = right * (1.0f - mix) + outR * mix; } writePos = (writePos + 1) % bufSize; } private: std::vector bufferL, bufferR; int writePos = 0; double sampleRate = 44100; float timeSamplesF = 8820.0f; int timeSamplesI = 8820; float feedback = 0.3f; float mix = 0.25f; bool pingPong = false; int pingSign = 1; float pingStrength = 1.0f; int pingCounter = 0; int pingPhase = 1; };