#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( id, name, juce::NormalisableRange(-24.0f, 24.0f, 0.01f), def)); }; auto addQ = [&](juce::String id, juce::String name, float def) { layout.add(std::make_unique( id, name, juce::NormalisableRange(0.1f, 6.0f, 0.01f), def)); }; addGain("gain0", "Low Shelf Gain", 0.0f); addGain("gain1", "Low-Mid Gain", 0.0f); addGain("gain2", "Mid Gain", 0.0f); addGain("gain3", "High-Mid Gain", 0.0f); addGain("gain4", "High Shelf Gain", 0.0f); addQ("q0", "Low Shelf Q", 0.71f); addQ("q1", "Low-Mid Q", 1.0f); addQ("q2", "Mid Q", 1.0f); addQ("q3", "High-Mid Q", 1.0f); addQ("q4", "High Shelf Q", 0.71f); layout.add(std::make_unique( "master", "Master", juce::NormalisableRange(-24.0f, 24.0f, 0.01f), 0.0f)); layout.add(std::make_unique( "analyserEnabled", "Analyser", true)); return layout; } const char* const bandTypes[5] = { "lowShelf", "peak", "peak", "peak", "highShelf" }; const float bandFreqs[5] = { 60.0f, 200.0f, 1000.0f, 4000.0f, 10000.0f }; } // 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& 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(); if (std::abs(gain - bands[i].gainDb) > 0.001f || std::abs(q - bands[i].q) > 0.001f) { bands[i].gainDb = gain; bands[i].q = q; bands[i].prepare(currentSampleRate, numSamples); } } juce::dsp::AudioBlock 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 xml(state.createXml()); copyXmlToBinary(*xml, destData); } void GelykEQAudioProcessor::setStateInformation(const void* data, int sizeInBytes) { std::unique_ptr 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(); }