wavetable/plugin/Source/WavetableVoice.cpp

493 lines
17 KiB
C++
Raw Normal View History

2020-05-20 16:44:46 -07:00
#include "WavetableVoice.h"
#include "PluginProcessor.h"
//==============================================================================
2023-08-23 11:21:37 -07:00
WavetableVoice::WavetableVoice (WavetableAudioProcessor& p)
2020-05-20 16:44:46 -07:00
: proc (p)
2023-08-26 11:17:03 -07:00
, noise (proc.analogTables)
, sub (proc.analogTables)
2020-05-20 16:44:46 -07:00
{
2023-08-23 11:21:37 -07:00
filter.setNumChannels (2);
2024-10-11 17:55:15 -07:00
mtsClient = MTS_RegisterClient();
}
WavetableVoice::~WavetableVoice()
{
MTS_DeregisterClient (mtsClient);
2020-05-20 16:44:46 -07:00
}
void WavetableVoice::noteStarted()
{
oscillators[0].setWavetable (&proc.osc1Tables);
oscillators[1].setWavetable (&proc.osc2Tables);
2023-08-25 09:47:50 -07:00
2020-05-20 16:44:46 -07:00
fastKill = false;
startVoice();
auto note = getCurrentlyPlayingNote();
if (glideInfo.fromNote != -1 && (glideInfo.glissando || glideInfo.portamento))
{
noteSmoother.setTime (glideInfo.rate);
noteSmoother.setValueUnsmoothed (glideInfo.fromNote / 127.0f);
noteSmoother.setValue (note.initialNote / 127.0f);
}
else
{
noteSmoother.setValueUnsmoothed (note.initialNote / 127.0f);
}
proc.modMatrix.setPolyValue (*this, proc.modSrcVelocity, note.noteOnVelocity.asUnsignedFloat());
2023-09-29 09:01:29 -07:00
proc.modMatrix.setPolyValue (*this, proc.modSrcTimbre, note.timbre.asUnsignedFloat());
proc.modMatrix.setPolyValue (*this, proc.modSrcPitchbend, note.pitchbend.asUnsignedFloat());
2020-05-20 16:44:46 -07:00
proc.modMatrix.setPolyValue (*this, proc.modSrcPressure, note.pressure.asUnsignedFloat());
2023-08-23 08:45:23 -07:00
juce::ScopedValueSetter<bool> svs (disableSmoothing, true);
2020-05-20 16:44:46 -07:00
2023-08-23 11:21:37 -07:00
filter.reset();
filterADSR.reset();
2020-05-20 16:44:46 -07:00
for (auto& a : modADSRs)
2023-09-21 07:34:23 -07:00
a.reset();
2020-05-20 16:44:46 -07:00
for (auto& l : modLFOs)
l.reset();
updateParams (0);
snapParams();
updateParams (0);
snapParams();
2023-08-23 08:45:23 -07:00
2023-10-03 08:11:07 -07:00
for (auto idx = 0; auto& osc : oscillators)
2023-10-03 18:27:12 -07:00
{
auto& params = proc.oscParams[idx++];
auto retrig = params.retrig->getBoolValue();
auto voices = params.voices->getUserValue();
if (voices == 1)
{
osc.noteOn (retrig ? 0.0f : proc.rng.nextFloat());
}
else if (retrig)
{
float phases[8] = { 0.0f };
for (auto i = 0; i < voices; i++)
phases[i] = 1.0f / voices * i;
osc.noteOn (phases);
}
else
{
float phases[8];
for (auto& p : phases)
p = proc.rng.nextFloat();
osc.noteOn (phases);
}
}
2023-09-20 22:08:18 -07:00
2023-08-27 06:33:07 -07:00
noise.noteOn();
2023-10-03 08:11:07 -07:00
sub.noteOn (proc.subParams.retrig->getBoolValue() ? 0.0f : proc.rng.nextFloat());
2020-05-20 16:44:46 -07:00
2023-08-23 11:21:37 -07:00
filterADSR.noteOn();
2020-05-20 16:44:46 -07:00
for (auto& a : modADSRs)
2023-09-20 22:08:18 -07:00
a.noteOn();
2020-05-20 16:44:46 -07:00
2023-09-24 17:49:53 -07:00
for (auto idx = 0; auto& l : modLFOs)
2023-09-29 20:20:07 -07:00
l.noteOn (proc.lfoParams[idx++].retrig->getBoolValue() ? -1 : proc.rng.nextFloat());
2020-05-20 16:44:46 -07:00
modStepLFO.reset();
2023-09-29 20:20:07 -07:00
modStepLFO.noteOn (proc.stepLfoParams.retrig->getBoolValue() ? -1 : proc.rng.nextFloat());
2020-05-20 16:44:46 -07:00
adsr.reset();
adsr.noteOn();
}
void WavetableVoice::noteRetriggered()
{
auto note = getCurrentlyPlayingNote();
if (glideInfo.fromNote != -1 && (glideInfo.glissando || glideInfo.portamento))
{
noteSmoother.setTime (glideInfo.rate);
noteSmoother.setValue (note.initialNote / 127.0f);
}
else
{
noteSmoother.setValueUnsmoothed (note.initialNote / 127.0f);
}
proc.modMatrix.setPolyValue (*this, proc.modSrcVelocity, note.noteOnVelocity.asUnsignedFloat());
2023-09-29 09:01:29 -07:00
proc.modMatrix.setPolyValue (*this, proc.modSrcTimbre, note.timbre.asUnsignedFloat());
2020-05-20 16:44:46 -07:00
proc.modMatrix.setPolyValue (*this, proc.modSrcPressure, note.pressure.asUnsignedFloat());
2023-09-29 09:01:29 -07:00
proc.modMatrix.setPolyValue (*this, proc.modSrcPitchbend, note.pitchbend.asUnsignedFloat());
2020-05-20 16:44:46 -07:00
updateParams (0);
2023-10-07 10:57:13 -07:00
if (proc.filterParams.retrig->getBoolValue())
filterADSR.noteOn();
2020-05-20 16:44:46 -07:00
2023-10-07 10:57:13 -07:00
for (auto idx = 0; auto& a : modADSRs)
if (proc.envParams[idx++].retrig->getBoolValue())
a.noteOn();
2020-05-20 16:44:46 -07:00
2023-10-07 10:57:13 -07:00
if (proc.adsrParams.retrig->getBoolValue())
adsr.noteOn();
2020-05-20 16:44:46 -07:00
}
void WavetableVoice::noteStopped (bool allowTailOff)
{
adsr.noteOff();
2023-08-23 11:21:37 -07:00
filterADSR.noteOff();
2020-05-20 16:44:46 -07:00
for (auto& a : modADSRs)
a.noteOff();
if (! allowTailOff)
{
clearCurrentNote();
stopVoice();
}
}
void WavetableVoice::notePressureChanged()
{
auto note = getCurrentlyPlayingNote();
proc.modMatrix.setPolyValue (*this, proc.modSrcPressure, note.pressure.asUnsignedFloat());
}
void WavetableVoice::noteTimbreChanged()
{
auto note = getCurrentlyPlayingNote();
2023-09-29 09:01:29 -07:00
proc.modMatrix.setPolyValue (*this, proc.modSrcTimbre, note.timbre.asUnsignedFloat());
}
void WavetableVoice::notePitchbendChanged()
{
auto note = getCurrentlyPlayingNote();
proc.modMatrix.setPolyValue (*this, proc.modSrcPitchbend, note.pitchbend.asUnsignedFloat());
2020-05-20 16:44:46 -07:00
}
void WavetableVoice::setCurrentSampleRate (double newRate)
{
MPESynthesiserVoice::setCurrentSampleRate (newRate);
for (auto& osc : oscillators)
osc.setSampleRate (newRate);
2023-09-04 16:11:47 -07:00
noise.setSampleRate (newRate);
sub.setSampleRate (newRate);
2020-05-20 16:44:46 -07:00
2023-08-23 11:21:37 -07:00
filter.setSampleRate (newRate);
2020-05-20 16:44:46 -07:00
2023-08-23 11:21:37 -07:00
filterADSR.setSampleRate (newRate);
2020-05-20 16:44:46 -07:00
for (auto& l : modLFOs)
l.setSampleRate (newRate);
2023-09-04 16:11:47 -07:00
for (auto& a : modADSRs)
a.setSampleRate (newRate);
2020-05-20 16:44:46 -07:00
modStepLFO.setSampleRate (newRate);
noteSmoother.setSampleRate (newRate);
adsr.setSampleRate (newRate);
}
2023-08-23 08:45:23 -07:00
void WavetableVoice::renderNextBlock (juce::AudioBuffer<float>& outputBuffer, int startSample, int numSamples)
2020-05-20 16:44:46 -07:00
{
updateParams (numSamples);
// Run OSC
2023-08-26 21:59:44 -07:00
gin::ScratchBuffer preFilter (2, numSamples);
gin::ScratchBuffer postFilter (2, numSamples);
2020-05-21 21:39:47 -07:00
2023-08-26 21:59:44 -07:00
if (proc.oscParams[0].enable->isOn())
oscillators[0].processAdding (currentMidiNotes[0], oscParams[0], proc.filterParams.wt1->isOn() ? preFilter : postFilter);
if (proc.oscParams[1].enable->isOn())
oscillators[1].processAdding (currentMidiNotes[1], oscParams[1], proc.filterParams.wt2->isOn() ? preFilter : postFilter);
2020-05-20 16:44:46 -07:00
2023-08-26 11:17:03 -07:00
if (proc.subParams.enable->isOn())
2023-08-26 21:59:44 -07:00
sub.processAdding (subNote, subParams, proc.filterParams.sub->isOn() ? preFilter : postFilter);
2023-08-26 11:17:03 -07:00
if (proc.noiseParams.enable->isOn())
2023-08-26 21:59:44 -07:00
noise.processAdding (60.0f, noiseParams, proc.filterParams.noise->isOn() ? preFilter : postFilter);
2023-08-26 11:17:03 -07:00
2020-05-20 16:44:46 -07:00
// Apply velocity
float velocity = currentlyPlayingNote.noteOnVelocity.asUnsignedFloat();
2023-08-26 21:59:44 -07:00
preFilter.applyGain (gin::velocityToGain (velocity, ampKeyTrack));
postFilter.applyGain (gin::velocityToGain (velocity, ampKeyTrack));
2020-05-20 16:44:46 -07:00
2023-08-25 09:47:50 -07:00
// Apply filter
2023-08-23 11:21:37 -07:00
if (proc.filterParams.enable->isOn())
2023-08-26 21:59:44 -07:00
filter.process (preFilter);
postFilter.addFrom (0, 0, preFilter, 0, 0, numSamples);
postFilter.addFrom (1, 0, preFilter, 1, 0, numSamples);
2020-05-20 16:44:46 -07:00
// Run ADSR
2023-08-26 21:59:44 -07:00
adsr.processMultiplying (postFilter);
2020-05-20 16:44:46 -07:00
2023-08-23 08:45:23 -07:00
if (adsr.getState() == gin::AnalogADSR::State::idle)
2020-05-20 16:44:46 -07:00
{
clearCurrentNote();
stopVoice();
}
// Copy output to synth
2023-08-26 21:59:44 -07:00
outputBuffer.addFrom (0, startSample, postFilter, 0, 0, numSamples);
outputBuffer.addFrom (1, startSample, postFilter, 1, 0, numSamples);
2020-05-20 16:44:46 -07:00
finishBlock (numSamples);
}
void WavetableVoice::updateParams (int blockSize)
{
auto note = getCurrentlyPlayingNote();
proc.modMatrix.setPolyValue (*this, proc.modSrcNote, note.initialNote / 127.0f);
2024-10-11 17:55:15 -07:00
double retune_semitones = 0.0;
if (mtsClient)
retune_semitones = MTS_RetuningInSemitones (mtsClient, note.initialNote, -1);
2020-05-20 16:44:46 -07:00
for (int i = 0; i < Cfg::numOSCs; i++)
{
if (! proc.oscParams[i].enable->isOn()) continue;
2023-08-23 08:45:23 -07:00
currentMidiNotes[i] = noteSmoother.getCurrentValue() * 127.0f;
if (glideInfo.glissando) currentMidiNotes[i] = (float) juce::roundToInt (currentMidiNotes[i]);
2024-10-11 17:55:15 -07:00
currentMidiNotes[i] += float (retune_semitones);
2023-08-23 08:45:23 -07:00
currentMidiNotes[i] += float (note.totalPitchbendInSemitones);
currentMidiNotes[i] += getValue (proc.oscParams[i].tune) + getValue (proc.oscParams[i].finetune) / 100.0f;
2020-05-20 16:44:46 -07:00
2023-08-31 11:17:49 -07:00
oscParams[i].voices = int (proc.oscParams[i].voices->getProcValue());
oscParams[i].position = getValue (proc.oscParams[i].pos) / 100.0f;
oscParams[i].pan = getValue (proc.oscParams[i].pan);
oscParams[i].spread = getValue (proc.oscParams[i].spread) / 100.0f;
oscParams[i].detune = getValue (proc.oscParams[i].detune);
oscParams[i].gain = getValue (proc.oscParams[i].level);
oscParams[i].formant = getValue (proc.oscParams[i].formant);
oscParams[i].bend = getValue (proc.oscParams[i].bend);
2020-05-20 16:44:46 -07:00
}
2023-08-26 11:17:03 -07:00
if (proc.subParams.enable->isOn())
{
subNote = noteSmoother.getCurrentValue() * 127.0f;
if (glideInfo.glissando) subNote = (float) juce::roundToInt (subNote);
2024-10-11 17:55:15 -07:00
subNote += retune_semitones;
2023-08-26 11:17:03 -07:00
subNote += float (note.totalPitchbendInSemitones);
subNote += getValue (proc.subParams.tune);
switch (proc.subParams.wave->getUserValueInt())
{
2023-10-03 08:15:17 -07:00
case 0:
subParams.wave = gin::Wave::sine;
break;
case 1:
subParams.wave = gin::Wave::triangle;
break;
case 2:
subParams.wave = gin::Wave::sawUp;
break;
case 3:
subParams.wave = gin::Wave::pulse;
subParams.pw = 0.50f;
break;
case 4:
subParams.wave = gin::Wave::pulse;
subParams.pw = 0.25f;
break;
case 5:
subParams.wave = gin::Wave::pulse;
subParams.pw = 0.125f;
break;
2023-08-26 11:17:03 -07:00
}
subParams.leftGain = getValue (proc.subParams.level) * (1.0f - getValue (proc.subParams.pan));
subParams.rightGain = getValue (proc.subParams.level) * (1.0f + getValue (proc.subParams.pan));
}
if (proc.noiseParams.enable->isOn())
{
noiseParams.wave = proc.noiseParams.type->getUserValueInt() == 0 ? gin::Wave::whiteNoise : gin::Wave::pinkNoise;
2023-08-26 11:17:03 -07:00
noiseParams.leftGain = getValue (proc.noiseParams.level) * (1.0f - getValue (proc.noiseParams.pan));
noiseParams.rightGain = getValue (proc.noiseParams.level) * (1.0f + getValue (proc.noiseParams.pan));
}
2020-05-20 16:44:46 -07:00
ampKeyTrack = getValue (proc.adsrParams.velocityTracking);
2023-08-23 11:21:37 -07:00
if (! proc.filterParams.enable->isOn())
2020-05-20 16:44:46 -07:00
{
2023-08-23 11:21:37 -07:00
proc.modMatrix.setPolyValue (*this, proc.modSrcFilter, 0);
}
else
{
filterADSR.setAttack (getValue (proc.filterParams.attack));
filterADSR.setSustainLevel (getValue (proc.filterParams.sustain));
filterADSR.setDecay (getValue (proc.filterParams.decay));
filterADSR.setRelease (getValue (proc.filterParams.release));
2020-05-20 16:44:46 -07:00
2023-08-23 11:21:37 -07:00
filterADSR.process (blockSize);
2020-05-20 16:44:46 -07:00
2023-08-23 08:45:23 -07:00
float filterWidth = float (gin::getMidiNoteFromHertz (20000.0));
2023-08-23 11:21:37 -07:00
float filterEnv = filterADSR.getOutput();
float filterSens = getValue (proc.filterParams.velocityTracking);
2020-05-20 16:44:46 -07:00
filterSens = currentlyPlayingNote.noteOnVelocity.asUnsignedFloat() * filterSens + 1.0f - filterSens;
2023-08-23 11:21:37 -07:00
float n = getValue (proc.filterParams.frequency);
n += (currentlyPlayingNote.initialNote - 60) * getValue (proc.filterParams.keyTracking);
n += filterEnv * filterSens * getValue (proc.filterParams.amount) * filterWidth;
2020-05-20 16:44:46 -07:00
2023-08-23 08:45:23 -07:00
float f = gin::getMidiNoteInHertz (n);
2020-05-20 16:44:46 -07:00
float maxFreq = std::min (20000.0f, float (getSampleRate() / 2));
2023-08-23 08:45:23 -07:00
f = juce::jlimit (4.0f, maxFreq, f);
2020-05-20 16:44:46 -07:00
2023-08-23 11:21:37 -07:00
float q = gin::Q / (1.0f - (getValue (proc.filterParams.resonance) / 100.0f) * 0.99f);
2020-05-20 16:44:46 -07:00
2023-08-23 11:21:37 -07:00
switch (int (proc.filterParams.type->getProcValue()))
2020-05-20 16:44:46 -07:00
{
case 0:
2023-08-23 11:21:37 -07:00
filter.setType (gin::Filter::lowpass);
filter.setSlope (gin::Filter::db12);
2020-05-20 16:44:46 -07:00
break;
case 1:
2023-08-23 11:21:37 -07:00
filter.setType (gin::Filter::lowpass);
filter.setSlope (gin::Filter::db24);
2020-05-20 16:44:46 -07:00
break;
case 2:
2023-08-23 11:21:37 -07:00
filter.setType (gin::Filter::highpass);
filter.setSlope (gin::Filter::db12);
2020-05-20 16:44:46 -07:00
break;
case 3:
2023-08-23 11:21:37 -07:00
filter.setType (gin::Filter::highpass);
filter.setSlope (gin::Filter::db24);
2020-05-20 16:44:46 -07:00
break;
case 4:
2023-08-23 11:21:37 -07:00
filter.setType (gin::Filter::bandpass);
filter.setSlope (gin::Filter::db12);
2020-05-20 16:44:46 -07:00
break;
case 5:
2023-08-23 11:21:37 -07:00
filter.setType (gin::Filter::bandpass);
filter.setSlope (gin::Filter::db24);
2020-05-20 16:44:46 -07:00
break;
case 6:
2023-08-23 11:21:37 -07:00
filter.setType (gin::Filter::notch);
filter.setSlope (gin::Filter::db12);
2020-05-20 16:44:46 -07:00
break;
case 7:
2023-08-23 11:21:37 -07:00
filter.setType (gin::Filter::notch);
filter.setSlope (gin::Filter::db24);
2020-05-20 16:44:46 -07:00
break;
}
2023-08-23 11:21:37 -07:00
filter.setParams (f, q);
proc.modMatrix.setPolyValue (*this, proc.modSrcFilter, filterADSR.getOutput());
2020-05-20 16:44:46 -07:00
}
for (int i = 0; i < Cfg::numENVs; i++)
{
if (proc.envParams[i].enable->isOn())
{
modADSRs[i].setAttack (getValue (proc.envParams[i].attack));
modADSRs[i].setSustainLevel (getValue (proc.envParams[i].sustain));
modADSRs[i].setDecay (getValue (proc.envParams[i].decay));
modADSRs[i].setRelease (getValue (proc.envParams[i].release));
proc.modMatrix.setPolyValue (*this, proc.modSrcEnv[i], modADSRs[i].getOutput());
modADSRs[i].process (blockSize);
}
else
{
proc.modMatrix.setPolyValue (*this, proc.modSrcEnv[i], 0.0f);
}
}
for (int i = 0; i < Cfg::numLFOs; i++)
{
if (proc.lfoParams[i].enable->isOn())
{
2023-08-23 08:45:23 -07:00
gin::LFO::Parameters params;
2020-05-20 16:44:46 -07:00
float freq = 0;
if (proc.lfoParams[i].sync->getProcValue() > 0.0f)
2023-08-23 08:45:23 -07:00
freq = 1.0f / gin::NoteDuration::getNoteDurations()[size_t (proc.lfoParams[i].beat->getProcValue())].toSeconds (proc.playhead);
2020-05-20 16:44:46 -07:00
else
freq = getValue (proc.lfoParams[i].rate);
2023-08-23 08:45:23 -07:00
params.waveShape = (gin::LFO::WaveShape) int (proc.lfoParams[i].wave->getProcValue());
2020-05-20 16:44:46 -07:00
params.frequency = freq;
params.phase = getValue (proc.lfoParams[i].phase);
params.offset = getValue (proc.lfoParams[i].offset);
params.depth = getValue (proc.lfoParams[i].depth);
params.delay = getValue (proc.lfoParams[i].delay);
params.fade = getValue (proc.lfoParams[i].fade);
modLFOs[i].setParameters (params);
modLFOs[i].process (blockSize);
proc.modMatrix.setPolyValue (*this, proc.modSrcLFO[i], modLFOs[i].getOutput());
}
else
{
proc.modMatrix.setPolyValue (*this, proc.modSrcLFO[i], 0);
}
}
// Update Step LFO
if (proc.stepLfoParams.enable->isOn())
{
2023-08-23 08:45:23 -07:00
float freq = 1.0f / gin::NoteDuration::getNoteDurations()[size_t (proc.stepLfoParams.beat->getProcValue())].toSeconds (proc.playhead);
2020-05-20 16:44:46 -07:00
modStepLFO.setFreq (freq);
int n = int (proc.stepLfoParams.length->getProcValue());
modStepLFO.setNumPoints (n);
for (int i = n; --i >= 0;)
modStepLFO.setPoint (i, proc.stepLfoParams.level[i]->getProcValue());
modStepLFO.process (blockSize);
proc.modMatrix.setPolyValue (*this, proc.modSrcStep, modStepLFO.getOutput());
}
else
{
proc.modMatrix.setPolyValue (*this, proc.modSrcStep, 0);
}
adsr.setAttack (getValue (proc.adsrParams.attack));
adsr.setDecay (getValue (proc.adsrParams.decay));
adsr.setSustainLevel (getValue (proc.adsrParams.sustain));
adsr.setRelease (fastKill ? 0.01f : getValue (proc.adsrParams.release));
noteSmoother.process (blockSize);
}
bool WavetableVoice::isVoiceActive()
{
return isActive();
}
2023-08-23 11:21:37 -07:00
float WavetableVoice::getFilterCutoffNormalized()
2020-05-20 16:44:46 -07:00
{
2023-08-23 11:21:37 -07:00
float freq = filter.getFrequency();
2023-09-24 19:48:06 -07:00
auto range = proc.filterParams.frequency->getUserRange();
return range.convertTo0to1 (juce::jlimit (range.start, range.end, gin::getMidiNoteFromHertz (freq)));
2020-05-20 16:44:46 -07:00
}
2023-08-25 08:24:15 -07:00
gin::WTOscillator::Params WavetableVoice::getLiveWTParams (int osc)
{
gin::WTOscillator::Params p;
2023-08-31 11:17:49 -07:00
p.position = getValue (proc.oscParams[osc].pos) / 100.0f;
p.formant = getValue (proc.oscParams[osc].formant);
p.bend = getValue (proc.oscParams[osc].bend);
2023-08-25 08:24:15 -07:00
return p;
}
2024-06-28 09:59:49 -07:00
float WavetableVoice::getCurrentNote()
{
return noteSmoother.getCurrentValue() * 127.0f;
}