#pragma once #include #include #include "PitchShifter.h" class HorizontEngine { public: HorizontEngine(); void prepare (double sampleRate); void reset(); void setLowCut (float hz); void setHighCut (float hz); void setDecay (float seconds); void setSize (float size); void setDiffusion (float diffusion); void setBrightness (float brightness); void setFeedback (float feedback); void setPitch (float semitones); void setDryWet (float dryWet); void processSample (float inL, float inR, float& outL, float& outR); float getLastWetL() const noexcept { return lastWetL; } float getLastWetR() const noexcept { return lastWetR; } private: struct SVF { void reset() { ic1 = 0.0f; ic2 = 0.0f; g = 0.0f; } void setG (float gIn) { g = gIn; a1 = 1.0f / (1.0f + g * (g + k)); a2 = g * a1; } void process (float x) { const float v3 = x - ic2; const float v1 = a1 * ic1 + a2 * v3; const float v2 = ic2 + g * v1; ic1 = 2.0f * v1 - ic1; ic2 = 2.0f * v2 - ic2; low = v2; high = x - k * v1 - v2; } float g = 0.0f; float k = 1.41421356f; float a1 = 0.0f; float a2 = 0.0f; float ic1 = 0.0f; float ic2 = 0.0f; float low = 0.0f; float high = 0.0f; }; struct Allpass { void prepare (int length) { len = length; xb.assign ((size_t) len + 1, 0.0f); yb.assign ((size_t) len + 1, 0.0f); writePos = 0; } void reset() { std::fill (xb.begin(), xb.end(), 0.0f); std::fill (yb.begin(), yb.end(), 0.0f); writePos = 0; } float process (float x, float g) { int rp = writePos - len; if (rp < 0) rp += len + 1; const float xd = xb[(size_t) rp]; const float yd = yb[(size_t) rp]; xb[(size_t) writePos] = x; const float y = -g * x + xd + g * yd; yb[(size_t) writePos] = y; ++writePos; if (writePos > len) writePos = 0; return y; } std::vector xb; std::vector yb; int len = 0; int writePos = 0; }; struct Comb { void prepare (int maxLength) { buffer.assign ((size_t) maxLength + 4, 0.0f); maxLen = maxLength; writePos = 0; dampState = 0.0f; } void reset() { std::fill (buffer.begin(), buffer.end(), 0.0f); dampState = 0.0f; writePos = 0; } float process (float input, float delayLen, float dampCoef, float gain) { const float rp = (float) writePos - delayLen; int i0 = (int) std::floor (rp); const float frac = rp - (float) i0; i0 %= maxLen; if (i0 < 0) i0 += maxLen; int i1 = i0 + 1; if (i1 >= maxLen) i1 -= maxLen; const float y = buffer[(size_t) i0] + frac * (buffer[(size_t) i1] - buffer[(size_t) i0]); buffer[(size_t) writePos] = input + dampState * gain; dampState += dampCoef * (y - dampState); ++writePos; if (writePos >= maxLen) writePos = 0; return dampState; } std::vector buffer; int maxLen = 0; int writePos = 0; float dampState = 0.0f; }; struct DCBlock { void reset() { xm = 0.0f; ym = 0.0f; } float process (float x) { const float y = x - xm + 0.999f * ym; xm = x; ym = y; return y; } float xm = 0.0f; float ym = 0.0f; }; static constexpr std::array baseLen441 = { 1553.f, 1787.f, 2017.f, 2237.f, 2437.f, 2657.f, 2879.f, 3073.f }; static constexpr std::array rightOffset = { 79.f, 67.f, 53.f, 41.f, 29.f, 23.f, 13.f, 7.f }; double sampleRate = 44100.0; juce::SmoothedValue lowCutG; juce::SmoothedValue highCutG; juce::SmoothedValue decayS; juce::SmoothedValue sizeS; juce::SmoothedValue diffS; juce::SmoothedValue dampS; juce::SmoothedValue feedbackS; juce::SmoothedValue ratioS; juce::SmoothedValue wetS; SVF hpL, lpL, hpR, lpR; DCBlock dcL, dcR; Allpass diffL1, diffL2, diffR1, diffR2; PitchShifter shiftL, shiftR; std::array combL; std::array combR; std::array baseLen; std::array baseLenR; float lowCutHz = 40.0f; float highCutHz = 14000.0f; float decaySec = 3.5f; float sizeVal = 1.0f; float diffusionVal = 0.6f; float brightnessVal = 0.75f; float feedbackVal = 0.6f; float pitchVal = 0.0f; float dryWetVal = 0.35f; float lastWetL = 0.0f; float lastWetR = 0.0f; };