Files
NeuralSynth/Source/SynthVoice.h
2025-10-25 17:57:05 +00:00

140 lines
4.7 KiB
C++

#pragma once
#include <JuceHeader.h>
#include <functional> // <-- for std::function used by WaveShaper
#include <memory>
#include <cmath>
#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<float>& 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<float>::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<float,
juce::dsp::DelayLineInterpolationTypes::Linear>;
using IIR = juce::dsp::IIR::Filter<float>;
using Gain = juce::dsp::Gain<float>;
using WaveShaper = juce::dsp::WaveShaper<float, std::function<float(float)>>; // <-- fix
using Chorus = juce::dsp::Chorus<float>;
using Reverb = juce::dsp::Reverb;
using Limiter = juce::dsp::Limiter<float>;
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<float> svf;
// ==== Chain (FX, EQ, master, limiter) ==================================
Chain chain;
// ==== Scratch buffer (properly allocated) ===============================
juce::AudioBuffer<float> tempBuffer;
juce::dsp::AudioBlock<float> tempBlock;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NeuralSynthVoice)
};