mirror of
https://codeberg.org/armin/saftpumpe.git
synced 2026-09-01 04:10:46 +02:00
164 lines
6.5 KiB
C++
164 lines
6.5 KiB
C++
#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)
|
|
})
|
|
{
|
|
inputScope.fill(0.0f);
|
|
outputScope.fill(0.0f);
|
|
fftInput.fill(0.0f);
|
|
fftOutput.fill(0.0f);
|
|
inputSpectrum.fill(0.0f);
|
|
outputSpectrum.fill(0.0f);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
float outL, outR;
|
|
if (bypassed) {
|
|
outL = inL;
|
|
outR = inR;
|
|
} else {
|
|
outL = compressorL.processSample(inL);
|
|
outR = compressorR.processSample(inR);
|
|
}
|
|
|
|
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());
|
|
if (gr > maxGR) maxGR = gr;
|
|
|
|
outputScope[scopeIndex] = outL;
|
|
fftOutput[fftIndex] = outL;
|
|
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);
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|