Hello, i’m beginner at audio plugin development, i follow matkat music to made EQ plugin, and successfully build my first plugin. can everyone here give me a tutorial about how to smooth the analyzer
here is the shot
#pragma once
#include <JuceHeader.h>
enum FFTOrder
{
order2048 = 11,
order4096 = 12,
order8192 = 13
};
template <typename BlockType>
struct FFTDataGenerator
{
/**
produces the FFT data from an audio buffer.
*/
void produceFFTDataForRendering(const juce::AudioBuffer<float> &audioData, const float negativeInfinity)
{
const auto fftSize = getFFTSize();
fftData.assign(fftData.size(), 0);
auto *readIndex = audioData.getReadPointer(0);
std::copy(readIndex, readIndex + fftSize, fftData.begin());
// first apply a windowing function to our data
window->multiplyWithWindowingTable(fftData.data(), fftSize); // [1]
// then render our FFT data..
forwardFFT->performFrequencyOnlyForwardTransform(fftData.data()); // [2]
int numBins = (int)fftSize / 2;
// normalize the fft values.
for (int i = 0; i < numBins; ++i)
{
auto v = fftData[i];
if (!std::isinf(v) && !std::isnan(v))
{
v /= float(numBins);
}
else
{
v = 0.f;
}
fftData[i] = Decibels::gainToDecibels(v, negativeInfinity);
}
// convert them to decibels
for (int i = 0; i < numBins; ++i)
{
smoothedValue[i].skip(audioData.getNumSamples());
auto v = juce::Decibels::gainToDecibels(fftData[i], negativeInfinity);
if (v < smoothedValue[i].currentValue)
{
v = smoothedValue[i].currentValue;
}
else
{
smoothedValue[i].setCurrentAndTargetValue(v);
}
fftData[i] = smoothedValue[i].getCurrentValue();
}
fftDataFifo.push(fftData);
}
void changeOrder(FFTOrder newOrder)
{
// when you change order, recreate the window, forwardFFT, fifo, fftData
// also reset the fifoIndex
// things that need recreating should be created on the heap via std::make_unique<>
order = newOrder;
auto fftSize = getFFTSize();
forwardFFT = std::make_unique<juce::dsp::FFT>(order);
window = std::make_unique<juce::dsp::WindowingFunction<float>>(fftSize, juce::dsp::WindowingFunction<float>::blackmanHarris);
fftData.clear();
fftData.resize(fftSize * 2, 0);
fftDataFifo.prepare(fftData.size());
}
//==============================================================================
int getFFTSize() const { return 1 << order; }
int getNumAvailableFFTDataBlocks() const { return fftDataFifo.getNumAvailableForReading(); }
//==============================================================================
bool getFFTData(BlockType &fftData) { return fftDataFifo.pull(fftData); }
void setSampleRate(double newSampleRate)
{
smoothedValue.resize(getFFTSize() / 2);
for (auto &v : smoothedValue)
{
v.reset(sampleRate, 0.1);
v.setCurrentAndTargetValue(-48.0);
}
}
private:
FFTOrder order;
BlockType fftData;
std::unique_ptr<juce::dsp::FFT> forwardFFT;
std::unique_ptr<juce::dsp::WindowingFunction<float>> window;
double sampleRate;
Fifo<BlockType> fftDataFifo;
Array<LinearSmoothedValue<float>> smoothedValue;
};
Code above is how to get FFT data and pass them into FIFO and then consume by path generator to generate path.
what i mean about Smoothing is using linear smoothing, let say freq 50Hz, if current level value is more than prev value, it will jump to targeted Y, if less, will decrease 1px time by time