Skip to content

Commit 143353e

Browse files
committed
Improved KMeter
1 parent a3d95a2 commit 143353e

File tree

3 files changed

+36
-5
lines changed

3 files changed

+36
-5
lines changed

modules/yup_dsp/metering/yup_KMeterState.cpp

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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

modules/yup_dsp/metering/yup_KMeterState.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,8 @@ class YUP_API KMeterState
353353

354354
// Level processors
355355
std::vector<LevelProcessor> levelProcessors; // One per channel
356+
std::vector<LoudnessFilter> loudnessFilters; // One per channel (for ITU/EBU K-weighting)
357+
std::vector<float> filteredBuffer; // Temporary buffer for K-weighted samples
356358

357359
// Atomic state for UI thread (wait-free reads)
358360
Atomic<float> atomicPeakLevelDb { -100.0f };

modules/yup_dsp/metering/yup_LoudnessFilter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ class YUP_API LoudnessFilter
152152
Biquad<float, double> highpassFilter; // Stage 2: Highpass (38 Hz, Q=0.5)
153153

154154
//==============================================================================
155-
YUP_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LoudnessFilter)
155+
YUP_LEAK_DETECTOR (LoudnessFilter)
156156
};
157157

158158
} // namespace yup

0 commit comments

Comments
 (0)