saftpumpe/Source/PluginProcessor.cpp

164 lines
6.5 KiB
C++
Raw Permalink Normal View History

2026-07-12 14:18:24 +02:00
#include "PluginProcessor.h"
#include "PluginEditor.h"
SaftpumpeProcessor::SaftpumpeProcessor()
: AudioProcessor(BusesProperties()
.withInput("Input", juce::AudioChannelSet::stereo(), true)
.withOutput("Output", juce::AudioChannelSet::stereo(), true)),
parameters(*this, nullptr, "Parameters", {
std::make_unique<juce::AudioParameterFloat>(
thresholdID, "Threshold", juce::NormalisableRange<float>(-48.0f, 0.0f, 0.1f), -20.0f),
std::make_unique<juce::AudioParameterFloat>(
ratioID, "Ratio", juce::NormalisableRange<float>(1.0f, 20.0f, 0.1f, 0.5f), 4.0f),
std::make_unique<juce::AudioParameterFloat>(
attackID, "Attack", juce::NormalisableRange<float>(0.001f, 0.1f, 0.001f, 0.5f), 0.03f),
std::make_unique<juce::AudioParameterFloat>(
releaseID, "Release", juce::NormalisableRange<float>(0.05f, 1.5f, 0.01f, 0.5f), 0.3f),
std::make_unique<juce::AudioParameterFloat>(
kneeID, "Knee", juce::NormalisableRange<float>(0.0f, 12.0f, 0.5f), 6.0f),
std::make_unique<juce::AudioParameterFloat>(
makeupID, "Makeup", juce::NormalisableRange<float>(0.0f, 24.0f, 0.1f), 0.0f),
std::make_unique<juce::AudioParameterFloat>(
mixID, "Mix", juce::NormalisableRange<float>(0.0f, 1.0f, 0.01f), 1.0f),
std::make_unique<juce::AudioParameterFloat>(
autoReleaseID, "Auto Release", juce::NormalisableRange<float>(0.0f, 1.0f, 0.01f), 1.0f),
std::make_unique<juce::AudioParameterBool>(
bypassID, "Bypass", false)
2026-07-12 14:18:24 +02:00
})
{
inputScope.fill(0.0f);
outputScope.fill(0.0f);
fftInput.fill(0.0f);
fftOutput.fill(0.0f);
inputSpectrum.fill(0.0f);
outputSpectrum.fill(0.0f);
2026-07-12 14:18:24 +02:00
}
SaftpumpeProcessor::~SaftpumpeProcessor() {}
void SaftpumpeProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) {
compressorL.prepare(sampleRate, samplesPerBlock);
compressorR.prepare(sampleRate, samplesPerBlock);
compressorL.reset();
compressorR.reset();
scopeIndex = 0;
fftIndex = 0;
2026-07-12 14:18:24 +02:00
}
void SaftpumpeProcessor::releaseResources() {}
void SaftpumpeProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer&) {
juce::ScopedNoDenormals noDenormals;
auto totalNumInputChannels = getTotalNumInputChannels();
auto totalNumOutputChannels = getTotalNumOutputChannels();
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
buffer.clear(i, 0, buffer.getNumSamples());
updateCompressorParams();
bool bypassed = parameters.getRawParameterValue(bypassID)->load() > 0.5f;
2026-07-12 14:18:24 +02:00
float maxInput = 0.0f;
float maxOutput = 0.0f;
float maxGR = 0.0f;
int numSamples = buffer.getNumSamples();
float* channelDataL = buffer.getWritePointer(0);
float* channelDataR = (totalNumInputChannels > 1) ? buffer.getWritePointer(1) : nullptr;
for (int sample = 0; sample < numSamples; ++sample) {
float inL = channelDataL[sample];
float inR = channelDataR ? channelDataR[sample] : inL;
float absIn = std::max(std::abs(inL), std::abs(inR));
if (absIn > maxInput) maxInput = absIn;
inputScope[scopeIndex] = inL;
fftInput[fftIndex] = inL;
2026-07-12 14:18:24 +02:00
float outL, outR;
if (bypassed) {
outL = inL;
outR = inR;
} else {
outL = compressorL.processSample(inL);
outR = compressorR.processSample(inR);
}
2026-07-12 14:18:24 +02:00
channelDataL[sample] = outL;
if (channelDataR) channelDataR[sample] = outR;
float absOut = std::max(std::abs(outL), std::abs(outR));
if (absOut > maxOutput) maxOutput = absOut;
float gr = bypassed ? 0.0f : std::abs(compressorL.getGainReduction());
2026-07-12 14:18:24 +02:00
if (gr > maxGR) maxGR = gr;
outputScope[scopeIndex] = outL;
fftOutput[fftIndex] = outL;
2026-07-12 14:18:24 +02:00
scopeIndex = (scopeIndex + 1) % scopeSize;
fftIndex++;
if (fftIndex >= fftSize) {
fftIndex = 0;
auto computeSpectrum = [&](const std::array<float, fftSize>& input,
std::array<float, fftSize / 2>& spectrum) {
std::array<float, fftSize * 2> buf{};
std::copy(input.begin(), input.end(), buf.begin());
window.multiplyWithWindowingTable(buf.data(), fftSize);
fft.performFrequencyOnlyForwardTransform(buf.data(), true);
for (int i = 0; i < fftSize / 2; ++i) {
float db = (buf[i] > 0.00001f) ? 20.0f * std::log10(buf[i]) : -100.0f;
float norm = juce::jlimit(0.0f, 1.0f, (db + 80.0f) / 80.0f);
spectrum[i] = spectrum[i] * 0.7f + norm * 0.3f;
}
};
computeSpectrum(fftInput, inputSpectrum);
computeSpectrum(fftOutput, outputSpectrum);
}
2026-07-12 14:18:24 +02:00
}
currentInputLevel = currentInputLevel * 0.8f + (maxInput > 0.0001f ? 20.0f * std::log10(maxInput) : -100.0f) * 0.2f;
currentOutputLevel = currentOutputLevel * 0.8f + (maxOutput > 0.0001f ? 20.0f * std::log10(maxOutput) : -100.0f) * 0.2f;
currentGainReduction = currentGainReduction * 0.7f + maxGR * 0.3f;
}
void SaftpumpeProcessor::updateCompressorParams() {
CompressorDSP::Params p;
p.threshold = parameters.getRawParameterValue(thresholdID)->load();
p.ratio = parameters.getRawParameterValue(ratioID)->load();
p.attack = parameters.getRawParameterValue(attackID)->load();
p.release = parameters.getRawParameterValue(releaseID)->load();
p.knee = parameters.getRawParameterValue(kneeID)->load();
p.makeup = parameters.getRawParameterValue(makeupID)->load();
p.mix = parameters.getRawParameterValue(mixID)->load();
p.autoRelease = parameters.getRawParameterValue(autoReleaseID)->load() > 0.5f;
compressorL.setParams(p);
compressorR.setParams(p);
}
juce::AudioProcessorEditor* SaftpumpeProcessor::createEditor() {
return new SaftpumpeEditor(*this);
}
void SaftpumpeProcessor::getStateInformation(juce::MemoryBlock& destData) {
auto state = parameters.copyState();
std::unique_ptr<juce::XmlElement> xml(state.createXml());
copyXmlToBinary(*xml, destData);
}
void SaftpumpeProcessor::setStateInformation(const void* data, int sizeInBytes) {
std::unique_ptr<juce::XmlElement> xml(getXmlFromBinary(data, sizeInBytes));
if (xml && xml->hasTagName(parameters.state.getType())) {
parameters.replaceState(juce::ValueTree::fromXml(*xml));
}
}
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() {
return new SaftpumpeProcessor();
}