#pragma once #include #include namespace monostep { // Tempo-synced delay + Schroeder reverb, applied after the voice. class FxProcessor { public: FxProcessor() = default; void prepare (double sampleRate, int numChannels); void reset(); struct Params { float delayTime = 0.35f; // normalized knob 0..1 float delayMix = 0.0f; // 0..1 dry/wet bool delaySync = true; // tempo-synced vs. free milliseconds float reverbRoom = 0.5f; // 0..1 float reverbMix = 0.0f; // 0..1 dry/wet float reverbDiff = 0.6f; // 0..1 allpass diffusion double bpm = 120.0; }; void setParams (const Params& p); void process (juce::AudioBuffer& buffer, int numSamples); // Shared knob-to-time mapping so the editor displays the same values the DSP uses. static float delayTimeSeconds (float knobNormalized, bool sync, double bpm); static juce::String delayTimeLabel (float knobNormalized, bool sync, double bpm); private: struct DelayChannel { std::vector memory; int writeIndex = 0; float smoothedDelay = 0.0f; float feedbackLp = 0.0f; }; struct ReverbChannel { std::vector combs[4]; std::vector allpasses[2]; int combIndex[4] = {}; int allpassIndex[2] = {}; }; void processDelay (juce::AudioBuffer& buffer, int numSamples); void processReverb (juce::AudioBuffer& buffer, int numSamples); double sampleRate = 44100.0; int numChannels = 2; int maxDelaySamples = 0; int combDelaySamples[2][4] = {}; int allpassDelaySamples[2][2] = {}; Params params; float currentDelaySeconds = 0.0f; std::vector delays; std::vector reverbs; }; } // namespace monostep