Upload files to "Source"
This commit is contained in:
@@ -14,7 +14,24 @@ void NeuralSynthVoice::prepare (const juce::dsp::ProcessSpec& newSpec)
|
||||
|
||||
// --- Oscillator
|
||||
osc.prepare (spec.sampleRate);
|
||||
setWaveform (0); // default to sine
|
||||
osc.setWave (BlepWave::Sine);
|
||||
|
||||
// --- Wavetable oscillator factory banks ---
|
||||
wtOsc.prepare (spec.sampleRate);
|
||||
morphLfo.prepare (spec.sampleRate);
|
||||
currentWtBankIndex = -1;
|
||||
wtOsc2.prepare (spec.sampleRate);
|
||||
morphLfo2.prepare (spec.sampleRate);
|
||||
currentWtBankIndex2 = -1;
|
||||
|
||||
const auto& library = WT::FactoryLibrary::get();
|
||||
if (! library.empty())
|
||||
{
|
||||
wtOsc.setBank (library.front().bank);
|
||||
currentWtBankIndex = 0;
|
||||
wtOsc2.setBank (library.front().bank);
|
||||
currentWtBankIndex2 = 0;
|
||||
}
|
||||
|
||||
// --- Scratch buffer (IMPORTANT: allocate real memory)
|
||||
tempBuffer.setSize ((int) spec.numChannels, (int) spec.maximumBlockSize,
|
||||
@@ -23,6 +40,10 @@ void NeuralSynthVoice::prepare (const juce::dsp::ProcessSpec& newSpec)
|
||||
|
||||
// --- Prepare chain elements
|
||||
chain.prepare (spec);
|
||||
chain.get<masterIndex>().setRampDurationSeconds (0.02f);
|
||||
chain.get<limiterIndex>().setThreshold (-1.0f);
|
||||
chain.get<limiterIndex>().setRelease (0.05f);
|
||||
chain.get<limiterIndex>().reset();
|
||||
|
||||
// Set maximum delay sizes BEFORE runtime changes
|
||||
{
|
||||
@@ -73,20 +94,109 @@ void NeuralSynthVoice::renderNextBlock (juce::AudioBuffer<float>& outputBuffer,
|
||||
if (! adsr.isActive())
|
||||
clearCurrentNote();
|
||||
|
||||
// Apply pending waveform change (from GUI / processor thread)
|
||||
const int wf = pendingWaveform.exchange (-1, std::memory_order_acq_rel);
|
||||
if (wf != -1)
|
||||
setWaveform (wf);
|
||||
|
||||
// --- Generate oscillator into temp buffer
|
||||
// --- Generate oscillator into temp buffer (BLEP or Wavetable)
|
||||
tempBuffer.clear();
|
||||
const int numCh = juce::jmin ((int) spec.numChannels, tempBuffer.getNumChannels());
|
||||
|
||||
const auto& library = WT::FactoryLibrary::get();
|
||||
const int librarySize = (int) library.size();
|
||||
|
||||
if (librarySize > 0 && shared.wtBank)
|
||||
{
|
||||
const int targetBank = juce::jlimit (0, librarySize - 1,
|
||||
(int) std::lround (shared.wtBank->load()));
|
||||
if (targetBank != currentWtBankIndex)
|
||||
{
|
||||
wtOsc.setBank (library[(size_t) targetBank].bank);
|
||||
currentWtBankIndex = targetBank;
|
||||
}
|
||||
}
|
||||
|
||||
if (librarySize > 0 && shared.wt2Bank)
|
||||
{
|
||||
const int targetBank2 = juce::jlimit (0, librarySize - 1,
|
||||
(int) std::lround (shared.wt2Bank->load()));
|
||||
if (targetBank2 != currentWtBankIndex2)
|
||||
{
|
||||
wtOsc2.setBank (library[(size_t) targetBank2].bank);
|
||||
currentWtBankIndex2 = targetBank2;
|
||||
}
|
||||
}
|
||||
|
||||
const bool useWTLayerA = (shared.wtOn && shared.wtOn->load() > 0.5f)
|
||||
&& wtOsc.getFrameCount() > 0;
|
||||
const bool useWTLayerB = (shared.wt2On && shared.wt2On->load() > 0.5f)
|
||||
&& wtOsc2.getFrameCount() > 0;
|
||||
|
||||
const float morphMaxA = wtOsc.getMaxMorph();
|
||||
const float morphBaseA = shared.wtMorph
|
||||
? juce::jlimit (0.0f, morphMaxA, shared.wtMorph->load())
|
||||
: 0.0f;
|
||||
const float lfoDepthA = shared.wtLfoDepth ? shared.wtLfoDepth->load() : 0.0f;
|
||||
const float lfoRateA = shared.wtLfoRate ? shared.wtLfoRate->load() : 1.0f;
|
||||
const int lfoShapeA = shared.wtLfoShape ? (int) std::lround (shared.wtLfoShape->load()) : 0;
|
||||
|
||||
morphLfo.setRate (lfoRateA);
|
||||
morphLfo.setShape (lfoShapeA);
|
||||
|
||||
const float depthFramesA = juce::jlimit (0.0f, morphMaxA, lfoDepthA);
|
||||
|
||||
const float morphMaxB = wtOsc2.getMaxMorph();
|
||||
const float morphBaseB = shared.wt2Morph
|
||||
? juce::jlimit (0.0f, morphMaxB, shared.wt2Morph->load())
|
||||
: 0.0f;
|
||||
const float lfoDepthB = shared.wt2LfoDepth ? shared.wt2LfoDepth->load() : 0.0f;
|
||||
const float lfoRateB = shared.wt2LfoRate ? shared.wt2LfoRate->load() : 0.3f;
|
||||
const int lfoShapeB = shared.wt2LfoShape ? (int) std::lround (shared.wt2LfoShape->load()) : 0;
|
||||
|
||||
morphLfo2.setRate (lfoRateB);
|
||||
morphLfo2.setShape (lfoShapeB);
|
||||
|
||||
const float depthFramesB = juce::jlimit (0.0f, morphMaxB, lfoDepthB);
|
||||
|
||||
const float levelA = shared.wtLevel ? juce::jlimit (0.0f, 1.0f, shared.wtLevel->load()) : 0.0f;
|
||||
const float levelB = shared.wt2Level ? juce::jlimit (0.0f, 1.0f, shared.wt2Level->load()) : 0.0f;
|
||||
const float safeLevelSum = juce::jlimit (0.5f, 2.0f, levelA + levelB + 0.0001f);
|
||||
const float mixGain = 0.45f / safeLevelSum;
|
||||
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
const float s = osc.process();
|
||||
float sampleA = useWTLayerA ? 0.0f : osc.process();
|
||||
if (useWTLayerA)
|
||||
{
|
||||
const float lfoValueA = morphLfo.process();
|
||||
const float headroomNegA = juce::jmin (depthFramesA, morphBaseA);
|
||||
const float headroomPosA = juce::jmin (depthFramesA, morphMaxA - morphBaseA);
|
||||
const float offsetA = (lfoValueA >= 0.0f ? lfoValueA * headroomPosA
|
||||
: lfoValueA * headroomNegA);
|
||||
const float morphValueA = juce::jlimit (0.0f, morphMaxA, morphBaseA + offsetA);
|
||||
sampleA = wtOsc.process (morphValueA);
|
||||
}
|
||||
else
|
||||
{
|
||||
morphLfo.process(); // advance for consistency
|
||||
}
|
||||
|
||||
float sampleB = 0.0f;
|
||||
if (useWTLayerB)
|
||||
{
|
||||
const float lfoValueB = morphLfo2.process();
|
||||
const float headroomNegB = juce::jmin (depthFramesB, morphBaseB);
|
||||
const float headroomPosB = juce::jmin (depthFramesB, morphMaxB - morphBaseB);
|
||||
const float offsetB = (lfoValueB >= 0.0f ? lfoValueB * headroomPosB
|
||||
: lfoValueB * headroomNegB);
|
||||
const float morphValueB = juce::jlimit (0.0f, morphMaxB, morphBaseB + offsetB);
|
||||
sampleB = wtOsc2.process (morphValueB);
|
||||
}
|
||||
else
|
||||
{
|
||||
morphLfo2.process();
|
||||
}
|
||||
|
||||
const float combined = mixGain * ((sampleA * levelA) + (sampleB * levelB));
|
||||
|
||||
for (int ch = 0; ch < numCh; ++ch)
|
||||
tempBuffer.getWritePointer (ch)[i] = s;
|
||||
tempBuffer.getWritePointer (ch)[i] = combined;
|
||||
}
|
||||
|
||||
auto block = tempBlock.getSubBlock (0, (size_t) numSamples);
|
||||
@@ -321,10 +431,22 @@ void NeuralSynthVoice::renderNextBlock (juce::AudioBuffer<float>& outputBuffer,
|
||||
void NeuralSynthVoice::noteStarted()
|
||||
{
|
||||
const float freqHz = (float) getCurrentlyPlayingNote().getFrequencyInHertz();
|
||||
const float initPhase = shared.wtPhase
|
||||
? juce::jlimit (0.0f, 1.0f, shared.wtPhase->load())
|
||||
: 0.0f;
|
||||
|
||||
// Oscillator frequency and phase retrigger
|
||||
// Oscillator frequency and phase retrigger (BLEP + WT)
|
||||
osc.setFrequency (freqHz);
|
||||
osc.resetPhase (0.0f);
|
||||
osc.resetPhase (initPhase);
|
||||
wtOsc.setFrequency (freqHz);
|
||||
wtOsc.resetPhase (initPhase);
|
||||
morphLfo.reset();
|
||||
const float initPhaseB = shared.wt2Phase
|
||||
? juce::jlimit (0.0f, 1.0f, shared.wt2Phase->load())
|
||||
: initPhase;
|
||||
wtOsc2.setFrequency (freqHz);
|
||||
wtOsc2.resetPhase (initPhaseB);
|
||||
morphLfo2.reset();
|
||||
|
||||
// Chorus snapshot
|
||||
if (shared.chorusCentre) chain.get<chorusIndex>().setCentreDelay (shared.chorusCentre->load());
|
||||
@@ -372,6 +494,7 @@ void NeuralSynthVoice::notePitchbendChanged()
|
||||
{
|
||||
const float freqHz = (float) getCurrentlyPlayingNote().getFrequencyInHertz();
|
||||
osc.setFrequency (freqHz);
|
||||
wtOsc.setFrequency (freqHz);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@@ -384,15 +507,3 @@ void NeuralSynthVoice::noteStopped (bool allowTailOff)
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
void NeuralSynthVoice::setWaveform (int waveformType)
|
||||
{
|
||||
switch (juce::jlimit (0, 3, waveformType))
|
||||
{
|
||||
case 0: osc.setWave (BlepWave::Sine); break;
|
||||
case 1: osc.setWave (BlepWave::Saw); break;
|
||||
case 2: osc.setWave (BlepWave::Square); break;
|
||||
case 3: osc.setWave (BlepWave::Triangle); break;
|
||||
default: osc.setWave (BlepWave::Sine); break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user