Meter loudness and check deliveries
core/loudness measures loudness to ITU-R BS.1770-4 and EBU R 128, runs
the classic programme meters (peak, PPM, VU, RMS), computes file statistics
for quality control and the gain that reaches a delivery target. Meters run
on the audio thread without allocating; any thread reads them lock-free.
Uses: core/loudness (USES loudness). Runnable version:
examples/loudness/meter.
1. Prepare a loudness meter
Section titled “1. Prepare a loudness meter”Construct and prepare on the control thread. This is where memory is allocated (LD-R34, LD-R69). Each channel carries a role, so that BS.1770 weights apply (LD-R7, LD-R8):
loudness::LoudnessMeter meter (2); // channel capacity, up to 64auto layout = meter.prepare ({ 48'000, { loudness::ChannelRole::named ("left"), loudness::ChannelRole::named ("right") }, false });if (! layout) show (layout.error); // names the parameter: sample_rate, channels, ...For 5.1, use *loudness::standardLayout (6).value (L R C LFE Ls Rs); for
text such as “L, R, C”, parseChannelRoles.
2. Feed it on the audio thread
Section titled “2. Feed it on the audio thread”meter.process (channels, frames); // noexcept SHUFFLE_RT: no allocation, lock or I/Oprocess takes float or double channel pointers. A reading set is
published at each 100 ms step (LD-R10, LD-R36). A reset from any thread
(meter.requestReset()) takes effect at the next block and starts a new
generation (LD-R35).
To meter the device output, implement the OutputStage port. Call
meter.process from its process, which runs on the audio thread after the
mix. Prepare the meter on the control thread before you attach the stage.
For a point inside the graph, the engine’s TapNode carries a
ProgrammeMeter on any audio pin (EN-R129 to EN-R131).
3. Read the values
Section titled “3. Read the values”const auto r = meter.readings(); // any thread, lock-free, one instantif (r.integrated.valid && ! r.integrated.belowFloor) draw (r.integrated.value); // LUFS; also momentary, shortTerm, loudnessRange, truePeakmeter.takePeaksSinceLastRead (samplePeaksDb, truePeaksDb); // the display owner onlyAn invalid reading never looks real (LD-R30); peaks between two refreshes are never lost (LD-R38). True peak oversamples by rate (LD-R27) and never reads below the sample peak (LD-R28).
4. Programme meters: peak, PPM, VU, RMS
Section titled “4. Programme meters: peak, PPM, VU, RMS”loudness::ProgrammeMeter ppm (2);loudness::ProgrammeMeterSettings settings;settings.type = loudness::ProgrammeMeterType::ppmType2; // samplePeak, truePeak, ppmType1, vu, rmsppm.prepare (settings); // control threadppm.process (channels, frames); // audio threadconst auto level = ppm.readings().channels[0].levelDb;Ballistics are defined in time, not blocks (LD-R46); peak-type meters have a hold and a latched clip (LD-R41, LD-R42).
5. Check a delivery
Section titled “5. Check a delivery”A target is plain data: integrated loudness, tolerance, true-peak ceiling and an optional maximum LRA (LD-R63). Four are built in: EBU R 128 broadcast, ATSC A/85, Streaming −14 and Streaming −16 (LD-R64).
const auto measured = loudness::measuredLoudness (meter.readings());for (const auto& target : loudness::builtInTargets()) if (auto n = loudness::normalisation (measured, target)) report (target.name, n.value->gainDb, n.value->ceilingBinds, n.value->withinTolerance);The proposed gain never exceeds the true-peak ceiling; when the ceiling
binds, shortfallLu says how far the target is missed (LD-R66). Applying
the gain is another domain’s work.
6. Measure a file
Section titled “6. Measure a file”FileAnalyser runs on a worker. formats decodes the file and hands over
the blocks:
loudness::FileAnalyser analyser;analyser.start ({ 48'000, roles }); // FileAnalysisSettings// for each decoded block; false means stop (cancelled or failed):if (! analyser.add (channels, frames)) stopDecoding();auto stats = analyser.finish(); // FileStatistics, or the error of a cancelFileStatistics reports peaks, RMS, DC offset, clip events, silence,
integrated loudness, LRA, maximum momentary and short-term, true peak and
PSR (LD-R56 to LD-R59); a statistic the file is too short for is absent
with a reason (LD-R60). measuredLoudness (*stats.value) feeds normalisation.
7. Commands
Section titled “7. Commands”Users, remotes and assistants act through a LoudnessCommandTarget:
loudness.reset_meter, loudness.set_meter_options, loudness.analyse_file, loudness.estimate_delay,
loudness.compute_normalisation_gain, loudness.create_target,
loudness.edit_target and loudness.delete_target (LD-R71). Resets,
analyses and delay estimates are queued for the host
(takeResetRequests, takeAnalyseRequests, takeDelayRequests); give
readings back with setReadings for snapshots and the gain (LD-R72).
Reference: specs/loudness.md, core/loudness.
© 2026 Umberto Sorbo. The software is licensed under the Apache License, Version 2.0, and provided “as is”, without warranties or conditions of any kind; see the disclaimer.
SHUFFLE Framework and the SHUFFLE Framework logo are trademarks of Umberto Sorbo; the licence grants no rights to them (trademarks). Other names belong to their owners. Fonts: IBM Plex Sans and JetBrains Mono, SIL Open Font License 1.1.
Contact: shuffleframework@gmail.com · Instagram @shuffleframework. Security problems: write to the same address with “Security” in the subject, not in a public issue.