Files
biggy 4748bfe768 refactor(fx): give each FX slot independent DSP history
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.
2026-09-09 14:32:58 +02:00

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