horizont/Source/HorizontEngine.cpp

224 lines
6.1 KiB
C++

#include "HorizontEngine.h"
HorizontEngine::HorizontEngine()
{
prepare (44100.0);
}
void HorizontEngine::prepare (double sr)
{
sampleRate = sr;
const float srScale = (float) (sr / 44100.0);
for (int i = 0; i < 8; ++i)
{
baseLen[(size_t) i] = baseLen441[(size_t) i] * srScale;
baseLenR[(size_t) i] = (baseLen441[(size_t) i] + rightOffset[(size_t) i]) * srScale;
const int maxL = (int) std::ceil (baseLen441[(size_t) i] * 2.0f * srScale) + 8;
combL[(size_t) i].prepare (maxL);
combR[(size_t) i].prepare (maxL);
}
diffL1.prepare ((int) (221.0f * srScale));
diffL2.prepare ((int) (113.0f * srScale));
diffR1.prepare ((int) (229.0f * srScale));
diffR2.prepare ((int) (107.0f * srScale));
shiftL.prepare (sr);
shiftR.prepare (sr);
lowCutG.reset (sr, 0.04);
setLowCut (lowCutHz);
lowCutG.setCurrentAndTargetValue (lowCutG.getTargetValue());
highCutG.reset (sr, 0.04);
setHighCut (highCutHz);
highCutG.setCurrentAndTargetValue (highCutG.getTargetValue());
decayS.reset (sr, 0.06);
setDecay (decaySec);
decayS.setCurrentAndTargetValue (decayS.getTargetValue());
sizeS.reset (sr, 0.06);
setSize (sizeVal);
sizeS.setCurrentAndTargetValue (sizeS.getTargetValue());
diffS.reset (sr, 0.05);
setDiffusion (diffusionVal);
diffS.setCurrentAndTargetValue (diffS.getTargetValue());
dampS.reset (sr, 0.05);
setBrightness (brightnessVal);
dampS.setCurrentAndTargetValue (dampS.getTargetValue());
feedbackS.reset (sr, 0.06);
setFeedback (feedbackVal);
feedbackS.setCurrentAndTargetValue (feedbackS.getTargetValue());
ratioS.reset (sr, 0.08);
setPitch (pitchVal);
ratioS.setCurrentAndTargetValue (ratioS.getTargetValue());
wetS.reset (sr, 0.03);
setDryWet (dryWetVal);
wetS.setCurrentAndTargetValue (wetS.getTargetValue());
reset();
}
void HorizontEngine::reset()
{
for (auto& c : combL)
c.reset();
for (auto& c : combR)
c.reset();
diffL1.reset();
diffL2.reset();
diffR1.reset();
diffR2.reset();
hpL.reset();
lpL.reset();
hpR.reset();
lpR.reset();
dcL.reset();
dcR.reset();
shiftL.reset();
shiftR.reset();
lastWetL = 0.0f;
lastWetR = 0.0f;
}
void HorizontEngine::setLowCut (float hz)
{
lowCutHz = hz;
hz = juce::jmin (hz, (float) (sampleRate * 0.48));
lowCutG.setTargetValue (std::tan (juce::MathConstants<float>::pi * hz / (float) sampleRate));
}
void HorizontEngine::setHighCut (float hz)
{
highCutHz = hz;
hz = juce::jmin (hz, (float) (sampleRate * 0.48));
highCutG.setTargetValue (std::tan (juce::MathConstants<float>::pi * hz / (float) sampleRate));
}
void HorizontEngine::setDecay (float seconds)
{
decaySec = seconds;
decayS.setTargetValue (juce::jmax (0.1f, seconds));
}
void HorizontEngine::setSize (float size)
{
sizeVal = size;
sizeS.setTargetValue (juce::jlimit (0.2f, 2.5f, size));
}
void HorizontEngine::setDiffusion (float diffusion)
{
diffusionVal = diffusion;
diffS.setTargetValue (0.72f * juce::jlimit (0.0f, 1.0f, diffusion));
}
void HorizontEngine::setBrightness (float brightness)
{
brightnessVal = brightness;
const float b = juce::jlimit (0.0f, 1.0f, brightness);
float fc = 500.0f + 19500.0f * std::pow (b, 1.5f);
fc = juce::jmin (fc, (float) (sampleRate * 0.48f));
dampS.setTargetValue (1.0f - std::exp (-2.0f * juce::MathConstants<float>::pi * fc / (float) sampleRate));
}
void HorizontEngine::setFeedback (float feedback)
{
feedbackVal = feedback;
feedbackS.setTargetValue (juce::jlimit (0.0f, 0.99f, feedback));
}
void HorizontEngine::setPitch (float semitones)
{
pitchVal = semitones;
ratioS.setTargetValue (std::pow (2.0f, semitones / 12.0f));
}
void HorizontEngine::setDryWet (float dryWet)
{
dryWetVal = dryWet;
wetS.setTargetValue (juce::jlimit (0.0f, 1.0f, dryWet));
}
void HorizontEngine::processSample (float inL, float inR, float& outL, float& outR)
{
const float sizeCur = sizeS.getNextValue();
const float rtCur = decayS.getNextValue();
const float dampC = dampS.getNextValue();
const float diffG = diffS.getNextValue();
const float fbGain = feedbackS.getNextValue();
const float ratio = ratioS.getNextValue();
const float wetVal = wetS.getNextValue();
const float gLow = lowCutG.getNextValue();
const float gHigh = highCutG.getNextValue();
hpL.setG (gLow);
hpL.process (inL);
lpL.setG (gHigh);
lpL.process (hpL.high);
hpR.setG (gLow);
hpR.process (inR);
lpR.setG (gHigh);
lpR.process (hpR.high);
const float fl = lpL.low;
const float fr = lpR.low;
const float invRt = 1.0f / (rtCur * sizeCur * (float) sampleRate);
const float loopInvRt = invRt * (1.0f - 0.7f * fbGain);
const float shiftOutL = shiftL.process (lastWetL, ratio);
const float shiftOutR = shiftR.process (lastWetR, ratio);
const float dL = diffL2.process (diffL1.process (fl, diffG), diffG);
const float dR = diffR2.process (diffR1.process (fr, diffG), diffG);
float wetL = 0.0f;
float wetR = 0.0f;
for (int i = 0; i < 8; ++i)
{
const float lenL = baseLen[(size_t) i] * sizeCur;
const float gainL = std::exp (-6.907755f * lenL * loopInvRt);
wetL += 0.25f * combL[(size_t) i].process (dL, lenL, dampC, gainL);
const float lenR = baseLenR[(size_t) i] * sizeCur;
const float gainR = std::exp (-6.907755f * lenR * loopInvRt);
wetR += 0.25f * combR[(size_t) i].process (dR, lenR, dampC, gainR);
}
// recirculate the shifted wet outside the comb bank: loop gain is exactly
// fbGain (< 1) so it cannot self-oscillate; gated off at 0 semitones
if (std::fabs (ratio - 1.0f) > 1e-4f)
{
wetL += fbGain * shiftOutL;
wetR += fbGain * shiftOutR;
}
wetL = dcL.process (wetL);
wetR = dcR.process (wetR);
wetL = std::tanh (wetL);
wetR = std::tanh (wetR);
lastWetL = wetL;
lastWetR = wetR;
outL = inL * (1.0f - wetVal) + wetL * wetVal;
outR = inR * (1.0f - wetVal) + wetR * wetVal;
}