Skip to content

Commit 68a911d

Browse files
committed
Added support of infiniter LUFS measurement period by dspu::ILUFSMeter
1 parent a525593 commit 68a911d

File tree

3 files changed

+86
-26
lines changed

3 files changed

+86
-26
lines changed

CHANGELOG

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
=== 1.0.31 ===
66
* Updated and extended interface of dspu::RingBuffer.
77
* Optimizations: dspu::MeterGraph now uses dspu::RingBuffer instead of dsp::ShiftBuffer.
8+
* Added support of infiniter LUFS measurement period by dspu::ILUFSMeter.
89
* Fixed crash in dspu::SpectralProcessor::clear method.
910

1011
=== 1.0.30 ===

include/lsp-plug.in/dsp-units/meters/ILUFSMeter.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
2-
* Copyright (C) 2024 Linux Studio Plugins Project <https://lsp-plug.in/>
3-
* (C) 2024 Vladimir Sadovnikov <[email protected]>
2+
* Copyright (C) 2025 Linux Studio Plugins Project <https://lsp-plug.in/>
3+
* (C) 2025 Vladimir Sadovnikov <[email protected]>
44
*
55
* This file is part of lsp-dsp-units
66
* Created on: 16 нояб. 2024 г.
@@ -97,7 +97,7 @@ namespace lsp
9797
uint32_t nMSSize; // Overall number of blocks available in buffer
9898
uint32_t nMSHead; // Current position to write new block to buffer
9999
int32_t nMSInt; // Number of blocks to integrate
100-
int32_t nMSCount; // Count of processed blocks
100+
int64_t nMSCount; // Count of processed blocks
101101

102102
uint32_t nSampleRate; // Sample rate
103103
uint32_t nChannels; // Number of channels
@@ -136,7 +136,9 @@ namespace lsp
136136
status_t init(size_t channels, float max_int_time = 60, float block_period = dspu::bs::LUFS_MEASURE_PERIOD_MS);
137137

138138
private:
139-
float compute_gated_loudness(float threshold);
139+
float compute_gated_loudness(float threshold) const;
140+
inline float compute_infinite_loudness(float threshold) const;
141+
void clear_block_buffers();
140142

141143
public:
142144
/**

src/main/meters/ILUFSMeter.cpp

Lines changed: 79 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
2-
* Copyright (C) 2024 Linux Studio Plugins Project <https://lsp-plug.in/>
3-
* (C) 2024 Vladimir Sadovnikov <[email protected]>
2+
* Copyright (C) 2025 Linux Studio Plugins Project <https://lsp-plug.in/>
3+
* (C) 2025 Vladimir Sadovnikov <[email protected]>
44
*
55
* This file is part of lsp-dsp-units
66
* Created on: 16 нояб. 2024 г.
@@ -52,6 +52,8 @@ namespace lsp
5252
*/
5353
static constexpr float GATING_REL_THRESH = 0.1f;
5454

55+
static constexpr size_t MIN_GATING_BLOCKS = 64;
56+
5557
ILUFSMeter::ILUFSMeter()
5658
{
5759
construct();
@@ -272,6 +274,15 @@ namespace lsp
272274
if (fIntTime == period)
273275
return;
274276

277+
// Reset accumulated loudness and block counter if needed if switching from infinite mode to finite one
278+
if (fIntTime <= 0)
279+
{
280+
nMSCount = -3; // The period has changed from finite to infinite
281+
clear_block_buffers();
282+
}
283+
else if (period <= 0.0f) // The period has changed from infinite to finite
284+
clear_block_buffers();
285+
275286
fIntTime = period;
276287
nFlags |= F_UPD_TIME;
277288
}
@@ -282,13 +293,12 @@ namespace lsp
282293
return STATUS_OK;
283294

284295
// Reallocate ring buffers for RMS estimation and lookahead
285-
const size_t blk_count = dspu::millis_to_samples(sample_rate, fBlockPeriod * 0.25f); // 75% overlapping
286-
size_t int_count = (dspu::seconds_to_samples(sample_rate, fMaxIntTime) + blk_count - 1) / blk_count;
287-
288-
const size_t int_szof = align_size(int_count * sizeof(float), DEFAULT_ALIGN);
289-
int_count = int_szof / sizeof(float);
296+
const size_t blk_size = dspu::millis_to_samples(sample_rate, fBlockPeriod * 0.25f); // 75% overlapping
297+
const size_t int_count = (dspu::seconds_to_samples(sample_rate, fMaxIntTime) + blk_size - 1) / blk_size;
290298

291-
size_t to_alloc = int_szof;
299+
const size_t int_szof = align_size(lsp_max(int_count, MIN_GATING_BLOCKS) * sizeof(float), DEFAULT_ALIGN);
300+
const size_t blk_count = int_szof / sizeof(float);
301+
const size_t to_alloc = int_szof;
292302

293303
uint8_t *buf = realloc_aligned<uint8_t>(pVarData, to_alloc, DEFAULT_ALIGN);
294304
if (buf == NULL)
@@ -298,10 +308,10 @@ namespace lsp
298308
vLoudness = advance_ptr_bytes<float>(buf, int_count);
299309

300310
// Update parameters
301-
fAvgCoeff = 0.25f / float(blk_count);
311+
fAvgCoeff = 0.25f / float(blk_size);
302312
nSampleRate = uint32_t(sample_rate);
303-
nBlockSize = uint32_t(blk_count);
304-
nMSSize = uint32_t(int_count);
313+
nBlockSize = uint32_t(blk_size);
314+
nMSSize = uint32_t(blk_count);
305315
nFlags = F_UPD_ALL;
306316

307317
// Clear all buffers
@@ -310,7 +320,7 @@ namespace lsp
310320
return STATUS_OK;
311321
}
312322

313-
float ILUFSMeter::compute_gated_loudness(float threshold)
323+
float ILUFSMeter::compute_gated_loudness(float threshold) const
314324
{
315325
float loudness = 0.0f;
316326
if (nMSCount <= 0)
@@ -333,6 +343,22 @@ namespace lsp
333343
return (count > 0) ? loudness / float(count) : 0.0f;
334344
}
335345

346+
float ILUFSMeter::compute_infinite_loudness(float threshold) const
347+
{
348+
float loudness = 0.0f;
349+
if (nMSCount <= 0)
350+
return loudness;
351+
352+
const float mult = 1.0 / float(nMSCount);
353+
for (ssize_t j=0; j<nMSSize; ++j)
354+
{
355+
const float lj = vLoudness[j];
356+
loudness += mult * lj;
357+
}
358+
359+
return loudness;
360+
}
361+
336362
void ILUFSMeter::process(float *out, size_t count, float gain)
337363
{
338364
update_settings();
@@ -377,17 +403,40 @@ namespace lsp
377403

378404
loudness += c->fWeight * s;
379405
}
380-
vLoudness[nMSHead] = loudness;
381-
nMSHead = (nMSHead + 1) % nMSSize;
382-
nMSCount = lsp_min(nMSCount + 1, nMSInt); // Increment number of blocks for processing
383406

384407
// Compute integrated loudness, two-stage
385408
// There is no necessity to apply the second stage if the loudness threshold
386409
// is less than absolute threshold
387-
loudness = compute_gated_loudness(GATING_ABS_THRESH);
388-
const float thresh = loudness * GATING_REL_THRESH;
389-
if (thresh > GATING_ABS_THRESH)
390-
loudness = compute_gated_loudness(thresh);
410+
if (fIntTime > 0.0f)
411+
{
412+
// Put the loudness value to the buffer
413+
vLoudness[nMSHead] = loudness;
414+
nMSHead = (nMSHead + 1) % nMSSize;
415+
nMSCount = lsp_min(nMSCount + 1, nMSInt); // Increment number of blocks for processing
416+
417+
// Two-stage loudness analysis.
418+
// 1. Compute absolute gating threshold
419+
loudness = compute_gated_loudness(GATING_ABS_THRESH);
420+
const float thresh = loudness * GATING_REL_THRESH;
421+
422+
// 2. Compute relative gating loudness if relative threshold is greater than absolute
423+
if (thresh > GATING_ABS_THRESH)
424+
loudness = compute_gated_loudness(thresh);
425+
}
426+
else
427+
{
428+
// Process block only if it is above gating threshold
429+
if (loudness > GATING_ABS_THRESH)
430+
{
431+
// Put the loudness value to the buffer
432+
vLoudness[nMSHead] += loudness;
433+
nMSHead = (nMSHead + 1) % nMSSize;
434+
nMSCount += 1;
435+
}
436+
437+
// For the infinite mode we can not compute gated loudness because do not have enough data for it
438+
loudness = compute_infinite_loudness(GATING_ABS_THRESH);
439+
}
391440

392441
// Convert the loudness for output. Because we use amplitude decibels,
393442
// we need to extract square root
@@ -456,17 +505,25 @@ namespace lsp
456505
nFlags = 0;
457506
}
458507

459-
void ILUFSMeter::clear()
508+
void ILUFSMeter::clear_block_buffers()
460509
{
461510
for (size_t i=0; i<nChannels; ++i)
462511
{
463512
channel_t *c = &vChannels[i];
464-
465-
c->sFilter.clear();
466513
for (size_t i=0; i<4; ++i)
467514
c->vBlock[i] = 0.0f;
468515
}
469516
dsp::fill_zero(vLoudness, nMSSize);
517+
}
518+
519+
void ILUFSMeter::clear()
520+
{
521+
for (size_t i=0; i<nChannels; ++i)
522+
{
523+
channel_t *c = &vChannels[i];
524+
c->sFilter.clear();
525+
}
526+
clear_block_buffers();
470527

471528
fLoudness = 0.0f;
472529

0 commit comments

Comments
 (0)