Replace the single shared array of 9 effect units with a 2D array of kNumFxSlots × kNumEffectTypes, so each slot owns its own DSP state. This prevents state bleed when the same effect type appears in multiple slots and allows reordering without carrying over internal history. Track activeTypes to skip unchanged slots. Remove the dry buffer since it was unused.
115 lines
3.4 KiB
C++
115 lines
3.4 KiB
C++
#include "FXProcessor.h"
|
|
#include "EffectUnits/Hyper.h"
|
|
#include "EffectUnits/Chorus.h"
|
|
#include "EffectUnits/Flanger.h"
|
|
#include "EffectUnits/Phaser.h"
|
|
#include "EffectUnits/Distortion.h"
|
|
#include "EffectUnits/EQ.h"
|
|
#include "EffectUnits/Compressor.h"
|
|
#include "EffectUnits/Delay.h"
|
|
#include "EffectUnits/Reverb.h"
|
|
|
|
namespace serum
|
|
{
|
|
|
|
FXProcessor::FXProcessor()
|
|
{
|
|
for (auto& slotUnits : units)
|
|
{
|
|
slotUnits[0] = std::make_unique<HyperUnit>();
|
|
slotUnits[1] = std::make_unique<ChorusUnit>();
|
|
slotUnits[2] = std::make_unique<FlangerUnit>();
|
|
slotUnits[3] = std::make_unique<PhaserUnit>();
|
|
slotUnits[4] = std::make_unique<DistortionUnit>();
|
|
slotUnits[5] = std::make_unique<EQUnit>();
|
|
slotUnits[6] = std::make_unique<CompressorUnit>();
|
|
slotUnits[7] = std::make_unique<DelayUnit>();
|
|
slotUnits[8] = std::make_unique<ReverbUnit>();
|
|
}
|
|
}
|
|
|
|
FXProcessor::~FXProcessor() = default;
|
|
|
|
void FXProcessor::prepare (double sampleRate, int maxBlockSize)
|
|
{
|
|
for (auto& slotUnits : units)
|
|
for (auto& u : slotUnits)
|
|
u->prepare (sampleRate, maxBlockSize);
|
|
wet.setSize (2, maxBlockSize, false, false, true);
|
|
reset();
|
|
}
|
|
|
|
void FXProcessor::reset()
|
|
{
|
|
for (auto& u : units)
|
|
u->reset();
|
|
dry.clear();
|
|
wet.clear();
|
|
}
|
|
|
|
juce::String FXProcessor::fxTypeName (int type)
|
|
{
|
|
switch ((FxType) type)
|
|
{
|
|
case FxType::Off: return "Off";
|
|
case FxType::Hyper: return "Hyper";
|
|
case FxType::Chorus: return "Chorus";
|
|
case FxType::Flanger: return "Flanger";
|
|
case FxType::Phaser: return "Phaser";
|
|
case FxType::Distortion: return "Distortion";
|
|
case FxType::EQ: return "EQ";
|
|
case FxType::Compressor: return "Compressor";
|
|
case FxType::Delay: return "Delay";
|
|
case FxType::Reverb: return "Reverb";
|
|
default: return {};
|
|
}
|
|
}
|
|
|
|
void FXProcessor::process (juce::AudioBuffer<float>& buffer, const FxSlotParams* slots, int numSlots)
|
|
{
|
|
const int n = buffer.getNumSamples();
|
|
if (n <= 0)
|
|
return;
|
|
|
|
for (int s = 0; s < numSlots; ++s)
|
|
{
|
|
const FxSlotParams& slot = slots[s];
|
|
if (slot.type == (int) FxType::Off)
|
|
continue;
|
|
|
|
const int unitIdx = slot.type - 1;
|
|
if (unitIdx < 0 || unitIdx >= (int) units.size())
|
|
continue;
|
|
|
|
dry.copyFrom (0, 0, buffer, 0, 0, n);
|
|
dry.copyFrom (1, 1, buffer, 1, 0, n);
|
|
wet.copyFrom (0, 0, buffer, 0, 0, n);
|
|
wet.copyFrom (1, 1, buffer, 1, 0, n);
|
|
|
|
units[(size_t) unitIdx]->process (wet.getWritePointer (0), wet.getWritePointer (1), n, slot.p);
|
|
|
|
const float mix = juce::jlimit (0.0f, 1.0f, slot.mix);
|
|
if (mix >= 1.0f)
|
|
{
|
|
buffer.copyFrom (0, 0, wet, 0, 0, n);
|
|
buffer.copyFrom (1, 0, wet, 1, 0, n);
|
|
}
|
|
else if (mix > 0.0f)
|
|
{
|
|
const float* dL = dry.getReadPointer (0);
|
|
const float* dR = dry.getReadPointer (1);
|
|
const float* wL = wet.getReadPointer (0);
|
|
const float* wR = wet.getReadPointer (1);
|
|
float* oL = buffer.getWritePointer (0);
|
|
float* oR = buffer.getWritePointer (1);
|
|
for (int i = 0; i < n; ++i)
|
|
{
|
|
oL[i] = dL[i] * (1.0f - mix) + wL[i] * mix;
|
|
oR[i] = dR[i] * (1.0f - mix) + wR[i] * mix;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace serum
|