@@ -79,6 +79,15 @@ void KMeterState::prepare (double newSampleRate, int maxChannels)
7979 processor.setFallTime (peakFallTime);
8080 }
8181
82+ // Initialize loudness filters (one per channel for ITU/EBU K-weighting)
83+ loudnessFilters.resize (numChannels);
84+ const int maxBlockSize = 512 ; // Match processPendingAudio chunk size
85+ for (auto & filter : loudnessFilters)
86+ filter.prepare (sampleRate, maxBlockSize);
87+
88+ // Allocate temporary buffer for K-weighted samples
89+ filteredBuffer.resize (maxBlockSize);
90+
8291 reset ();
8392}
8493
@@ -105,6 +114,9 @@ void KMeterState::reset() noexcept
105114 for (auto & processor : levelProcessors)
106115 processor.reset ();
107116
117+ for (auto & filter : loudnessFilters)
118+ filter.reset ();
119+
108120 atomicPeakLevelDb.set (kMeterMinimumDecibel + scaleOffset);
109121 atomicAverageLevelDb.set (kMeterMinimumDecibel + scaleOffset);
110122 atomicPeakHoldLevelDb.set (kMeterMinimumDecibel + scaleOffset);
@@ -264,17 +276,31 @@ void KMeterState::processChannelLevels (int channel, const float* samples, int n
264276 auto & channelState = channels[channel];
265277 auto & processor = levelProcessors[channel];
266278
267- // Process peak
279+ // Determine if we need K-weighting (ITU BS.1770-4 or EBU R128)
280+ const bool needsKWeighting = meteringStandard == MeteringStandard::ituBS1770_4
281+ || meteringStandard == MeteringStandard::ebuR128;
282+
283+ // Apply K-weighting filter for ITU/EBU modes
284+ const float * samplesToProcess = samples;
285+ if (needsKWeighting)
286+ {
287+ jassert (numSamples <= static_cast <int > (filteredBuffer.size ()));
288+ std::copy (samples, samples + numSamples, filteredBuffer.begin ());
289+ loudnessFilters[channel].processBlock (filteredBuffer.data (), numSamples);
290+ samplesToProcess = filteredBuffer.data ();
291+ }
292+
293+ // Process peak (always from original samples, never filtered)
268294 float peak = 0 .0f ;
269295 processor.processPeak (samples, numSamples, peak);
270296
271297 // Update peak with fall
272298 const double timeDelta = numSamples / sampleRate;
273299 processor.processPeakWithFall (peak, timeDelta, channelState.currentPeak );
274300
275- // Process RMS
301+ // Process RMS (from K-weighted samples if ITU/EBU, otherwise from original)
276302 float rms = 0 .0f ;
277- processor.processRMS (samples , numSamples, rms);
303+ processor.processRMS (samplesToProcess , numSamples, rms);
278304 channelState.currentAverage = rms;
279305
280306 // CRITICAL: Peak must never fall below RMS average (physically impossible)
@@ -328,6 +354,7 @@ void KMeterState::processChannelLevels (int channel, const float* samples, int n
328354 }
329355
330356 // Update OVER counter (counts contiguous or total samples at or above threshold)
357+ // IMPORTANT: Always use ORIGINAL samples for clipping detection, never filtered
331358 int overflowsInBlock = 0 ;
332359 for (int i = 0 ; i < numSamples; ++i)
333360 {
@@ -351,7 +378,9 @@ void KMeterState::setMeteringStandard (MeteringStandard standard)
351378 if (meteringStandard != standard)
352379 {
353380 meteringStandard = standard;
354- // ITU BS.1770-4 and EBU R128 support will be added in Phase 4
381+ // Reset filters when switching metering standards
382+ for (auto & filter : loudnessFilters)
383+ filter.reset ();
355384 }
356385}
357386
0 commit comments