#pragma once #include #include // <-- for std::function used by WaveShaper #include #include #include "NeuralSharedParams.h" #include "BlepOsc.h" #include "WavetableOsc.h" //============================================================================== // A single polyBLEP oscillator voice with per-voice ADSR, filter ADSR, // flanger (delayline), simple delay, chorus, reverb, distortion, EQ, master. class NeuralSynthVoice : public juce::MPESynthesiserVoice { public: explicit NeuralSynthVoice (NeuralSharedParams& sharedParams); // JUCE voice API void prepare (const juce::dsp::ProcessSpec& spec); void renderNextBlock (juce::AudioBuffer& outputBuffer, int startSample, int numSamples) override; void noteStarted() override; void noteStopped (bool allowTailOff) override; void notePitchbendChanged() override; void notePressureChanged() override {} void noteTimbreChanged() override {} void noteKeyStateChanged() override {} private: struct MorphLFO { void prepare (double sr) { sampleRate = juce::jmax (1.0, sr); updateIncrement(); } void reset() { phase = 0.0f; } void setRate (float hz) { rate = juce::jlimit (0.0f, 30.0f, hz); updateIncrement(); } void setShape (int idx) { shape = juce::jlimit (0, 3, idx); } float process() { float value = 0.0f; switch (shape) { case 1: value = 1.0f - 4.0f * std::abs(phase - 0.5f); break; // Triangle case 2: value = 2.0f * phase - 1.0f; break; // Ramp up case 3: value = 1.0f - 2.0f * phase; break; // Ramp down default: value = std::sin (juce::MathConstants::twoPi * phase); break; // Sine } phase += phaseInc; if (phase >= 1.0f) phase -= 1.0f; return value; } private: void updateIncrement() { phaseInc = (float) (rate / (float) sampleRate); if (phaseInc < 0.0f) phaseInc = 0.0f; } double sampleRate { 44100.0 }; float rate { 1.0f }; float phase { 0.0f }; float phaseInc { 0.0f }; int shape { 0 }; }; //=== Processing chain (without oscillator) =============================== using DelayLine = juce::dsp::DelayLine; using IIR = juce::dsp::IIR::Filter; using Gain = juce::dsp::Gain; using WaveShaper = juce::dsp::WaveShaper>; // <-- fix using Chorus = juce::dsp::Chorus; using Reverb = juce::dsp::Reverb; using Limiter = juce::dsp::Limiter; // Separate functions for different parts void renderReverb(juce::dsp::AudioBlock &block); void renderSimpleDelay(juce::dsp::AudioBlock &block); void renderADSR(int numSamples, int numCh); void renderChorus(juce::dsp::AudioBlock &block); void renderFlanger(int numSamples, int numCh); void renderDistortion(int numSamples, int numCh, juce::dsp::AudioBlock &block); void renderEQ(juce::dsp::AudioBlock &block); enum ChainIndex { flangerIndex = 0, delayIndex, chorusIndex, reverbIndex, distortionPreGain, distortionIndex, distortionPostLPF, eqLowIndex, eqMidIndex, eqHighIndex, masterIndex, limiterIndex }; using Chain = juce::dsp::ProcessorChain< DelayLine, // flanger DelayLine, // simple delay Chorus, // chorus Reverb, // reverb Gain, // distortion pre-gain (drive) WaveShaper, // distortion waveshaper IIR, // tone / post-EQ for distortion IIR, // EQ low IIR, // EQ mid IIR, // EQ high Gain, // master gain Limiter // safety limiter >; private: NeuralSharedParams& shared; juce::dsp::ProcessSpec spec {}; // ==== Oscillators ====================================================== BlepOsc osc; // polyBLEP oscillator WT::Osc wtOsc; // wavetable oscillator (shared bank) MorphLFO morphLfo; int currentWtBankIndex { -1 }; WT::Osc wtOsc2; // secondary wavetable oscillator MorphLFO morphLfo2; int currentWtBankIndex2 { -1 }; // ==== Envelopes & Filter =============================================== juce::ADSR adsr; juce::ADSR filterAdsr; juce::dsp::StateVariableTPTFilter svf; // ==== Chain (FX, EQ, master, limiter) ================================== Chain chain; // ==== Scratch buffer (properly allocated) =============================== juce::AudioBuffer tempBuffer; juce::dsp::AudioBlock tempBlock; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NeuralSynthVoice) };