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