28 m_windowF(fftSize, dsp::WindowingFunction<float>::hann)
40 m_useLevelProcessing = useLevelProcessing;
41 m_useBufferProcessing = useBufferProcessing;
42 m_useSpectrumProcessing = useSepctrumProcessing;
47 return m_useLevelProcessing;
52 return m_useBufferProcessing;
57 return m_useSpectrumProcessing;
62 m_sampleRate =
static_cast<unsigned long>(sampleRate);
63 m_samplesPerCentiSecond =
static_cast<int>(sampleRate * 0.01f);
64 m_bufferSize = bufferSize;
65 m_missingSamplesForCentiSecond =
static_cast<int>(m_samplesPerCentiSecond + 0.5f);
66 m_centiSecondBuffer.setSize(2, m_missingSamplesForCentiSecond,
false,
true,
false);
72 m_samplesPerCentiSecond = 0;
74 m_centiSecondBuffer.clear();
75 m_missingSamplesForCentiSecond = 0;
80 m_holdTimeMs = holdTimeMs;
82 startTimer(m_holdTimeMs);
87 std::lock_guard<std::mutex> lock(m_callbackListenersMutex);
88 m_callbackListeners.add(listener);
93 std::lock_guard<std::mutex> lock(m_callbackListenersMutex);
94 m_callbackListeners.remove(m_callbackListeners.indexOf(listener));
102 int numChannels = buffer.getNumChannels();
104 if (numChannels != m_centiSecondBuffer.getNumChannels())
105 m_centiSecondBuffer.setSize(numChannels, m_samplesPerCentiSecond,
false,
true,
true);
112 m_FFTdata.resize(numChannels);
113 m_FFTdataPos.resize(numChannels, 0);
114 for (
auto& channelFFTdata : m_FFTdata)
115 channelFFTdata.resize(fftSize * 2, 0.0f);
118 int availableSamples = buffer.getNumSamples();
121 while (availableSamples >= m_missingSamplesForCentiSecond)
123 int writePos = m_samplesPerCentiSecond - m_missingSamplesForCentiSecond;
125 for (
int i = 0; i < numChannels; ++i)
130 m_centiSecondBuffer.copyFrom(i, writePos, buffer.getReadPointer(i) + readPos, m_missingSamplesForCentiSecond);
136 auto peak = m_centiSecondBuffer.getMagnitude(i, 0, m_samplesPerCentiSecond);
137 auto rms = m_centiSecondBuffer.getRMSLevel(i, 0, m_samplesPerCentiSecond);
138 auto hold = std::max(peak, m_level.
GetLevel(i + 1).
hold);
146 processSpectrumForChannel(i, m_centiSecondBuffer.getReadPointer(i), m_samplesPerCentiSecond);
151 BroadcastData(&m_level);
154 BroadcastData(&m_centiSecondBuffer);
157 BroadcastData(&m_spectrum);
160 readPos += m_missingSamplesForCentiSecond;
161 availableSamples -= m_missingSamplesForCentiSecond;
162 m_missingSamplesForCentiSecond = m_samplesPerCentiSecond;
166 if (availableSamples > 0)
168 int writePos = m_samplesPerCentiSecond - m_missingSamplesForCentiSecond;
169 for (
int i = 0; i < numChannels; ++i)
171 m_centiSecondBuffer.copyFrom(i, writePos, buffer.getReadPointer(i) + readPos, availableSamples);
173 m_missingSamplesForCentiSecond -= availableSamples;
177void ProcessorDataAnalyzer::processSpectrumForChannel(
int channelIndex,
const float* channelData,
int numSamples)
179 int samplesProcessed = 0;
182 while (samplesProcessed < numSamples)
184 int samplesNeeded = fftSize - m_FFTdataPos[channelIndex];
185 int samplesAvailable = numSamples - samplesProcessed;
186 int samplesToCopy = std::min(samplesNeeded, samplesAvailable);
189 juce::FloatVectorOperations::copy(
190 m_FFTdata[channelIndex].data() + m_FFTdataPos[channelIndex],
191 channelData + samplesProcessed,
195 m_FFTdataPos[channelIndex] += samplesToCopy;
196 samplesProcessed += samplesToCopy;
199 if (m_FFTdataPos[channelIndex] >= fftSize)
201 performFFTAndUpdateSpectrum(channelIndex);
204 const int hopSize = (fftSize * 3) / 4;
207 juce::FloatVectorOperations::copy(
208 m_FFTdata[channelIndex].data(),
209 m_FFTdata[channelIndex].data() + hopSize,
213 m_FFTdataPos[channelIndex] = fftSize - hopSize;
218void ProcessorDataAnalyzer::performFFTAndUpdateSpectrum(
int channelIndex)
220 float* fftData = m_FFTdata[channelIndex].data();
223 m_windowF.multiplyWithWindowingTable(fftData, fftSize);
226 m_fwdFFT.performFrequencyOnlyForwardTransform(fftData);
229 ProcessorSpectrumData::SpectrumBands spectrumBands = m_spectrum.
GetSpectrum(channelIndex);
233 const float nyquistFreq = m_sampleRate * 0.5f;
235 const float minDisplayFreq = 20.0f;
236 const float maxDisplayFreq = std::min(20000.0f, nyquistFreq);
238 spectrumBands.minFreq = minDisplayFreq;
239 spectrumBands.maxFreq = maxDisplayFreq;
242 const int usableFFTBins = fftSize / 2;
243 const float binFrequency = m_sampleRate /
static_cast<float>(fftSize);
245 const float fftScale = 1.0f /
static_cast<float>(fftSize);
246 const float windowCompensation = 2.0f;
249 const float smoothingFactor = 0.6f;
257 float t = bandIndex * invBandCount;
258 float bandStartFreq = minDisplayFreq * std::pow(maxDisplayFreq / minDisplayFreq, t);
259 float bandEndFreq = minDisplayFreq * std::pow(maxDisplayFreq / minDisplayFreq, t + invBandCount);
261 int startBin =
static_cast<int>(bandStartFreq / binFrequency);
262 int endBin =
static_cast<int>(bandEndFreq / binFrequency);
264 startBin = juce::jlimit(0, usableFFTBins - 1, startBin);
265 endBin = juce::jlimit(startBin + 1, usableFFTBins, endBin);
267 int numBins = endBin - startBin;
270 float bandSumSquared = 0.0f;
271 for (
int bin = startBin; bin < endBin; ++bin)
273 float magnitude = fftData[bin] * fftScale * windowCompensation;
274 bandSumSquared += magnitude * magnitude;
278 float rmsValue = std::sqrt(bandSumSquared / numBins);
281 const float minMagnitude = 0.00001f;
282 rmsValue = std::max(rmsValue, minMagnitude);
284 float leveldB = juce::Decibels::gainToDecibels(rmsValue);
285 leveldB = juce::jlimit(spectrumBands.mindB, spectrumBands.maxdB, leveldB);
286 float normalizedLevel = juce::jmap(leveldB, spectrumBands.mindB, spectrumBands.maxdB, 0.0f, 1.0f);
289 float previousLevel = spectrumBands.bandsPeak[bandIndex];
292 if (normalizedLevel > previousLevel)
295 smoothedLevel = 0.1f * previousLevel + 0.9f * normalizedLevel;
300 smoothedLevel = smoothingFactor * previousLevel + (1.0f - smoothingFactor) * normalizedLevel;
303 spectrumBands.bandsPeak[bandIndex] = smoothedLevel;
304 spectrumBands.bandsHold[bandIndex] = std::max(smoothedLevel, spectrumBands.bandsHold[bandIndex]);
307 m_spectrum.
SetSpectrum(channelIndex, spectrumBands);
308 juce::FloatVectorOperations::clear(fftData, fftSize * 2);
311void ProcessorDataAnalyzer::BroadcastData(AbstractProcessorData* data)
313 std::lock_guard<std::mutex> lock(m_callbackListenersMutex);
314 for (Listener* l : m_callbackListeners)
315 l->processingDataChanged(data);
323void ProcessorDataAnalyzer::FlushHold()
327 for (
auto i = 0; i < channelCount; ++i)
334 for (
auto i = 0; i < channelCount; ++i)
336 ProcessorSpectrumData::SpectrumBands spectrumBands = m_spectrum.
GetSpectrum(i);
340 spectrumBands.bandsHold[j] = 0.0f;
unsigned long GetSampleRate()
Returns the sample rate associated with this buffer.
void SetSampleRate(unsigned long rate)
Sets the sample rate associated with this buffer.
bool isLevelProcessingUsed()
void addListener(Listener *listener)
void analyzeData(const juce::AudioBuffer< float > &buffer)
Submits a new audio buffer for analysis.
void removeListener(Listener *listener)
static int getGlobalMaxdB()
void setUseProcessingTypes(bool useLevelProcessing, bool useBufferProcessing, bool useSepctrumProcessing)
Configures which data types (level, spectrum, audio signal) the analyzer computes.
static int getGlobalMindB()
bool isBufferProcessingUsed()
bool isSepctrumProcessingUsed()
void timerCallback() override
Timer callback that broadcasts pending data changes to all registered listeners.
void initializeParameters(double sampleRate, int bufferSize)
void setHoldTime(int holdTimeMs)
void SetLevel(unsigned long channel, LevelVal level)
LevelVal GetLevel(unsigned long channel)
unsigned long GetChannelCount() override
Returns the number of audio channels this data object covers.
unsigned long GetChannelCount() override
Returns the number of audio channels this data object covers.
const SpectrumBands & GetSpectrum(unsigned long channel)
void SetSpectrum(unsigned long channel, SpectrumBands spectrum)
Per-channel level values in both linear and dB domains.
float hold
Linear hold level.