gelyk/Source/PluginProcessor.cpp

189 lines
6.3 KiB
C++
Raw Normal View History

2026-08-29 13:17:11 +02:00
#include "PluginProcessor.h"
#include "PluginEditor.h"
#include "GitInfo.h"
namespace gelyk
{
namespace
{
juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout()
{
juce::AudioProcessorValueTreeState::ParameterLayout layout;
auto addGain = [&](juce::String id, juce::String name, float def)
{
layout.add(std::make_unique<juce::AudioParameterFloat>(
id, name, juce::NormalisableRange<float>(-24.0f, 24.0f, 0.01f), def));
};
auto addQ = [&](juce::String id, juce::String name, float def)
{
layout.add(std::make_unique<juce::AudioParameterFloat>(
id, name, juce::NormalisableRange<float>(0.1f, 6.0f, 0.01f), def));
};
auto addFreq = [&](juce::String id, juce::String name, float def)
{
auto range = juce::NormalisableRange<float>(20.0f, 20000.0f, 0.5f);
range.setSkewForCentre(1000.0f);
layout.add(std::make_unique<juce::AudioParameterFloat>(id, name, range, def));
};
2026-08-29 13:17:11 +02:00
addGain("gain0", "Low Shelf Gain", 0.0f);
addGain("gain1", "Low Gain", 0.0f);
addGain("gain2", "Low-Mid Gain", 0.0f);
addGain("gain3", "Mid Gain", 0.0f);
addGain("gain4", "High-Mid Gain", 0.0f);
addGain("gain5", "High Gain", 0.0f);
addGain("gain6", "High Shelf Gain", 0.0f);
2026-08-29 13:17:11 +02:00
addQ("q0", "Low Shelf Q", 0.71f);
addQ("q1", "Low Q", 1.0f);
addQ("q2", "Low-Mid Q", 1.0f);
addQ("q3", "Mid Q", 1.0f);
addQ("q4", "High-Mid Q", 1.0f);
addQ("q5", "High Q", 1.0f);
addQ("q6", "High Shelf Q", 0.71f);
2026-08-29 13:17:11 +02:00
addFreq("freq0", "Low Shelf Freq", 60.0f);
addFreq("freq1", "Low Freq", 160.0f);
addFreq("freq2", "Low-Mid Freq", 400.0f);
addFreq("freq3", "Mid Freq", 800.0f);
addFreq("freq4", "High-Mid Freq", 1600.0f);
addFreq("freq5", "High Freq", 4000.0f);
addFreq("freq6", "High Shelf Freq", 10000.0f);
2026-08-29 13:17:11 +02:00
layout.add(std::make_unique<juce::AudioParameterFloat>(
"master", "Master", juce::NormalisableRange<float>(-24.0f, 24.0f, 0.01f), 0.0f));
layout.add(std::make_unique<juce::AudioParameterBool>(
"analyserEnabled", "Analyser", true));
return layout;
}
const char* const bandTypes[7] = { "lowShelf", "peak", "peak", "peak", "peak", "peak", "highShelf" };
const float bandFreqs[7] = { 60.0f, 160.0f, 400.0f, 800.0f, 1600.0f, 4000.0f, 10000.0f };
2026-08-29 13:17:11 +02:00
} // namespace
GelykEQAudioProcessor::GelykEQAudioProcessor()
: AudioProcessor(BusesProperties()
.withInput("Input", juce::AudioChannelSet::stereo(), true)
.withOutput("Output", juce::AudioChannelSet::stereo(), true)),
apvts(*this, nullptr, "Parameters", createParameterLayout())
{
buildVersion = ProjectInfo::versionString;
gitInfo = juce::String(GELYK_GIT_BRANCH) + " @ " + juce::String(GELYK_GIT_SHA) +
(juce::String(GELYK_GIT_DIRTY_STR).isNotEmpty() ? " (dirty)" : "") +
" | " + juce::String(GELYK_BUILD_DATE_STR);
}
void GelykEQAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
{
currentSampleRate = sampleRate;
for (int i = 0; i < numBands; ++i)
{
bands[i].type = (FilterBand::Type)
(juce::String(bandTypes[i]) == "lowShelf" ? 0 :
(juce::String(bandTypes[i]) == "highShelf" ? 2 : 1));
bands[i].freq = bandFreqs[i];
bands[i].prepare(sampleRate, samplesPerBlock);
bands[i].reset();
}
analyser.prepare(sampleRate, samplesPerBlock);
analyser.startBackground();
rebuildCoefficients();
}
void GelykEQAudioProcessor::releaseResources()
{
analyser.stopBackground();
}
void GelykEQAudioProcessor::rebuildCoefficients()
{
for (int i = 0; i < numBands; ++i)
{
float gain = apvts.getRawParameterValue("gain" + juce::String(i))->load();
float q = apvts.getRawParameterValue("q" + juce::String(i))->load();
bands[i].gainDb = gain;
bands[i].q = q;
bands[i].prepare(currentSampleRate, 512);
}
}
void GelykEQAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer&)
{
juce::ScopedNoDenormals noDenormals;
const int numChannels = buffer.getNumChannels();
const int numSamples = buffer.getNumSamples();
if (numChannels < 1)
return;
// Feed the analyser with the input.
if (apvts.getRawParameterValue("analyserEnabled")->load() > 0.5f)
{
const float* chans[2] = { buffer.getReadPointer(0),
numChannels > 1 ? buffer.getReadPointer(1) : buffer.getReadPointer(0) };
analyser.push(chans, numChannels > 1 ? 2 : 1, numSamples);
}
// Refresh band coefficients (cheap) from smoothed params each block.
for (int i = 0; i < numBands; ++i)
{
float gain = apvts.getRawParameterValue("gain" + juce::String(i))->load();
float q = apvts.getRawParameterValue("q" + juce::String(i))->load();
float freq = apvts.getRawParameterValue("freq" + juce::String(i))->load();
if (std::abs(gain - bands[i].gainDb) > 0.001f ||
std::abs(q - bands[i].q) > 0.001f ||
std::abs(freq - bands[i].freq) > 0.5f)
2026-08-29 13:17:11 +02:00
{
bands[i].gainDb = gain;
bands[i].q = q;
bands[i].freq = freq;
2026-08-29 13:17:11 +02:00
bands[i].prepare(currentSampleRate, numSamples);
}
}
juce::dsp::AudioBlock<float> block(buffer);
for (int i = 0; i < numBands; ++i)
bands[i].process(block);
float master = apvts.getRawParameterValue("master")->load();
if (std::abs(master) > 0.001f)
buffer.applyGain(juce::Decibels::decibelsToGain(master));
}
void GelykEQAudioProcessor::getStateInformation(juce::MemoryBlock& destData)
{
auto state = apvts.copyState();
std::unique_ptr<juce::XmlElement> xml(state.createXml());
copyXmlToBinary(*xml, destData);
}
void GelykEQAudioProcessor::setStateInformation(const void* data, int sizeInBytes)
{
std::unique_ptr<juce::XmlElement> xml(getXmlFromBinary(data, sizeInBytes));
if (xml != nullptr && xml->hasTagName(apvts.state.getType()))
apvts.replaceState(juce::ValueTree::fromXml(*xml));
}
juce::AudioProcessorEditor* GelykEQAudioProcessor::createEditor()
{
return new GelykEQAudioProcessorEditor(*this);
}
} // namespace gelyk
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
return new gelyk::GelykEQAudioProcessor();
}