feat: programme loudness and true peak (ITU-R BS.1770-5 / EBU R 128) (#219)
* refactor: extract the shared inter-sample-peak oversampling helper
The polyphase oversample-then-max machinery of lc_peak also underpins the
BS.1770-5 true-peak meter, so it moves to _internal/peaks.py as
inter_sample_peak and lc_peak delegates to it (bit-identical behaviour).
* feat: programme loudness and true peak (ITU-R BS.1770-5 / EBU R 128)
New broadcast domain implementing the objective multichannel loudness
measurement algorithm and its EBU companions:
- k_weighting / k_weighting_coefficients: the Annex 1 two-stage pre-filter
with the Table 1-2 biquads verbatim at 48 kHz and an analog-prototype
redesign at other rates (response preserved within 0.016 dB).
- integrated_loudness: 400 ms gating blocks at 75 % overlap, Table 3
channel weights (LFE excluded) and the two-stage -70 LKFS / -10 LU gate
(Formulae 1-7).
- program_loudness: the full EBU Mode set of Tech 3341 as a frozen
ProgramLoudnessResult with .plot() (momentary and short-term series with
maxima, integrated loudness, relative threshold, loudness range with its
percentile edges, per-channel true peaks).
- loudness_range: the Tech 3342 algorithm with its cascaded -70 LUFS /
-20 LU gate and 10th-95th percentile spread, following the reference
implementation's nearest-rank indexing.
- true_peak_level: the Annex 2 inter-sample peak in dBTP by oversampling
to at least 192 kHz (4x at 48 kHz), sharing the lc_peak machinery.
- channel_weight: the Annex 3 position-dependent weights (Table 4) for
the BS.2051 advanced sound systems.
* test: EBU Tech 3341/3342 minimum-requirements signals for programme loudness
Every synthesizable case runs at its official tolerance: Tech 3341 cases
1-6 (integrated, +/-0.1 LU), 9-14 (momentary/short-term dynamics and
maxima, including the 20-step live sequences), 15-19 (true-peak tones,
+0.2/-0.4 dB) and 20-23 (the fs/4 period inside an fs/6 tone decimated
with 0-3 sample offsets); Tech 3342 LRA cases 1-4 (+/-1 LU) plus the
repetition-invariance clause. Alongside: the 997 Hz / -3.01 LKFS anchor
on L/C/R, the verbatim 48 kHz coefficient tables and the redesign match
at 44.1/96/192 kHz, the Annex 2 Attachment 1 under-read bound, the
Annex 3 Table 4 weight grid, LFE exclusion, gating behaviour and the
validation/edge paths. The case tables live in tests/reference_data.py,
shared with the conformance report.
* feat: programme-loudness conformance checks (8 EBU cases)
New 'Program loudness (ITU-R BS.1770 / EBU R 128)' domain in the
conformance registry: the Annex 1 997 Hz anchor, Tech 3341 integrated
cases 1/5/6, true-peak cases 15/19 and Tech 3342 LRA cases 1/3, all
synthesized from the shared reference tables. 274 checks total.
* docs: programme-loudness metering figure (EN/ES, light/dark)
A one-minute synthetic programme (ambience, dialogue, music, fade-out)
loudness-normalised to the R 128 target: momentary and short-term traces,
the integrated loudness on -23.0 LUFS, the LRA band between its
percentile edges and the summary box with the maxima and true peak.
* docs: programme-loudness guide (EN/ES), indices and API reference
The guide walks the chain from the K-weighting and the 997 Hz anchor
through gating, the EBU Mode time scales, the loudness range, the
true-peak meter and the Annex 3 multichannel weights, with the official
EBU signals as inline examples and the validation scope spelled out.
Wired into the documentation index, the sources-and-devices section
landing (EN/ES), the sidebar, the curated API quick table (thirteen
domains now), the generated API reference taxonomy, the bibliography and
the changelog.
* docs: add the programme-loudness guide to the README feature table
* fix: address programme-loudness review findings
- Reject sample rates below 16 kHz in the K-weighting redesign: at 8 kHz
the bilinear warping reaches 0.55 dB in band and drifts the 997 Hz
anchor to -2.89 LKFS, outside the +/-0.1 LU metering tolerance. The
response claim is now scoped honestly (within 0.02 dB at 32 kHz and
above) in the docstrings, the guides and the changelog.
- Reject non-finite input in integrated_loudness/program_loudness: a
single NaN would poison every gating block and silently read as
digital silence (-inf).
- Correct the EBU R 128 tolerance prose in the three guides: the 2023
edition permits +/-1.0 LU where the target is not practically
achievable and +/-0.2 LU for measurement error in QC workflows (the
+/-0.5 LU general tolerance was the 2014 rule).
- Drop the false 'Annex 2 minimum of 4 below 96 kHz' clause from the
true_peak_level docstring (the default is the smallest factor whose
oversampled rate reaches 192 kHz) and regenerate the API page.
- Reject a boolean oversample factor, make the second K-weighting
snippet self-contained, drop an unused unpacked variable in the
conformance checks and a redundant loudness-range guard.
* test: run the authentic-programme EBU loudness cases against the official test set
Tech 3341 cases 7-8 and Tech 3342 cases 5-6 use authentic programme
material whose licence covers technical testing only, so the audio is never
committed: the tests read the official EBU loudness test set from a
gitignored local directory (EBU_LOUDNESS_TEST_SET overrides it) and skip
when it is absent. One CI matrix cell fetches the set from the EBU with an
Actions cache so the four cases also run on every pull request. All four
pass within the official tolerances.
* fix: harden the EBU test-set download and address the loudness review smells
The EBU site rejects curl's default agent, so the CI download sends a
browser agent, verifies a sentinel file and drops the directory on failure
so a bad download is never cached (fresh cache key). The duplicated
empty-signal message becomes a module constant and the exception tests
build their inputs outside the raises blocks.
* test: pin the gating and LRA stages to committed authentic-programme series
The audio of the EBU authentic-programme cases cannot be committed, but the
per-block loudness series measured from it are plain measurement data that
cannot reconstruct the programmes. The 400 ms gate-input and 3 s short-term
series of both files are committed (26 kB, rounded three orders of magnitude
below the official tolerances) and the integrated-gating and loudness-range
stages now verify the four case values on every platform, network or not.
The full chain still runs against the original audio wherever the set is
present.
* ci: send a full browser agent when fetching the EBU loudness test set
* fix: apply the programme-loudness review round
The Tech 3341 true-peak conformance checks now judge with the asymmetric
+0.2/-0.4 dB window the specification defines. Long programmes recover the
inter-sample peak in bounded chunks (with a margin far beyond the FIR
support, verified exactly equal to the whole-signal computation) so the
memory use no longer scales with duration. Non-finite loudspeaker positions
and channel weights are rejected, the momentary and short-term steps
enforce the 10 Hz minimum update rate the API documents, the curated API
table states the real 16 kHz floor, the plot's short-term label became a
caller-overridable default, the download sentinel and the oracle skip check
both files, the series test closes its npz handle and the Spanish landing
names the exact BS.1770-5 revision.
feat: programme loudness and true peak (ITU-R BS.1770-5 / EBU R 128) (#219)
* refactor: extract the shared inter-sample-peak oversampling helper
The polyphase oversample-then-max machinery of lc_peak also underpins the
BS.1770-5 true-peak meter, so it moves to _internal/peaks.py as
inter_sample_peak and lc_peak delegates to it (bit-identical behaviour).
* feat: programme loudness and true peak (ITU-R BS.1770-5 / EBU R 128)
New broadcast domain implementing the objective multichannel loudness
measurement algorithm and its EBU companions:
- k_weighting / k_weighting_coefficients: the Annex 1 two-stage pre-filter
with the Table 1-2 biquads verbatim at 48 kHz and an analog-prototype
redesign at other rates (response preserved within 0.016 dB).
- integrated_loudness: 400 ms gating blocks at 75 % overlap, Table 3
channel weights (LFE excluded) and the two-stage -70 LKFS / -10 LU gate
(Formulae 1-7).
- program_loudness: the full EBU Mode set of Tech 3341 as a frozen
ProgramLoudnessResult with .plot() (momentary and short-term series with
maxima, integrated loudness, relative threshold, loudness range with its
percentile edges, per-channel true peaks).
- loudness_range: the Tech 3342 algorithm with its cascaded -70 LUFS /
-20 LU gate and 10th-95th percentile spread, following the reference
implementation's nearest-rank indexing.
- true_peak_level: the Annex 2 inter-sample peak in dBTP by oversampling
to at least 192 kHz (4x at 48 kHz), sharing the lc_peak machinery.
- channel_weight: the Annex 3 position-dependent weights (Table 4) for
the BS.2051 advanced sound systems.
* test: EBU Tech 3341/3342 minimum-requirements signals for programme loudness
Every synthesizable case runs at its official tolerance: Tech 3341 cases
1-6 (integrated, +/-0.1 LU), 9-14 (momentary/short-term dynamics and
maxima, including the 20-step live sequences), 15-19 (true-peak tones,
+0.2/-0.4 dB) and 20-23 (the fs/4 period inside an fs/6 tone decimated
with 0-3 sample offsets); Tech 3342 LRA cases 1-4 (+/-1 LU) plus the
repetition-invariance clause. Alongside: the 997 Hz / -3.01 LKFS anchor
on L/C/R, the verbatim 48 kHz coefficient tables and the redesign match
at 44.1/96/192 kHz, the Annex 2 Attachment 1 under-read bound, the
Annex 3 Table 4 weight grid, LFE exclusion, gating behaviour and the
validation/edge paths. The case tables live in tests/reference_data.py,
shared with the conformance report.
* feat: programme-loudness conformance checks (8 EBU cases)
New 'Program loudness (ITU-R BS.1770 / EBU R 128)' domain in the
conformance registry: the Annex 1 997 Hz anchor, Tech 3341 integrated
cases 1/5/6, true-peak cases 15/19 and Tech 3342 LRA cases 1/3, all
synthesized from the shared reference tables. 274 checks total.
* docs: programme-loudness metering figure (EN/ES, light/dark)
A one-minute synthetic programme (ambience, dialogue, music, fade-out)
loudness-normalised to the R 128 target: momentary and short-term traces,
the integrated loudness on -23.0 LUFS, the LRA band between its
percentile edges and the summary box with the maxima and true peak.
* docs: programme-loudness guide (EN/ES), indices and API reference
The guide walks the chain from the K-weighting and the 997 Hz anchor
through gating, the EBU Mode time scales, the loudness range, the
true-peak meter and the Annex 3 multichannel weights, with the official
EBU signals as inline examples and the validation scope spelled out.
Wired into the documentation index, the sources-and-devices section
landing (EN/ES), the sidebar, the curated API quick table (thirteen
domains now), the generated API reference taxonomy, the bibliography and
the changelog.
* docs: add the programme-loudness guide to the README feature table
* fix: address programme-loudness review findings
- Reject sample rates below 16 kHz in the K-weighting redesign: at 8 kHz
the bilinear warping reaches 0.55 dB in band and drifts the 997 Hz
anchor to -2.89 LKFS, outside the +/-0.1 LU metering tolerance. The
response claim is now scoped honestly (within 0.02 dB at 32 kHz and
above) in the docstrings, the guides and the changelog.
- Reject non-finite input in integrated_loudness/program_loudness: a
single NaN would poison every gating block and silently read as
digital silence (-inf).
- Correct the EBU R 128 tolerance prose in the three guides: the 2023
edition permits +/-1.0 LU where the target is not practically
achievable and +/-0.2 LU for measurement error in QC workflows (the
+/-0.5 LU general tolerance was the 2014 rule).
- Drop the false 'Annex 2 minimum of 4 below 96 kHz' clause from the
true_peak_level docstring (the default is the smallest factor whose
oversampled rate reaches 192 kHz) and regenerate the API page.
- Reject a boolean oversample factor, make the second K-weighting
snippet self-contained, drop an unused unpacked variable in the
conformance checks and a redundant loudness-range guard.
* test: run the authentic-programme EBU loudness cases against the official test set
Tech 3341 cases 7-8 and Tech 3342 cases 5-6 use authentic programme
material whose licence covers technical testing only, so the audio is never
committed: the tests read the official EBU loudness test set from a
gitignored local directory (EBU_LOUDNESS_TEST_SET overrides it) and skip
when it is absent. One CI matrix cell fetches the set from the EBU with an
Actions cache so the four cases also run on every pull request. All four
pass within the official tolerances.
* fix: harden the EBU test-set download and address the loudness review smells
The EBU site rejects curl's default agent, so the CI download sends a
browser agent, verifies a sentinel file and drops the directory on failure
so a bad download is never cached (fresh cache key). The duplicated
empty-signal message becomes a module constant and the exception tests
build their inputs outside the raises blocks.
* test: pin the gating and LRA stages to committed authentic-programme series
The audio of the EBU authentic-programme cases cannot be committed, but the
per-block loudness series measured from it are plain measurement data that
cannot reconstruct the programmes. The 400 ms gate-input and 3 s short-term
series of both files are committed (26 kB, rounded three orders of magnitude
below the official tolerances) and the integrated-gating and loudness-range
stages now verify the four case values on every platform, network or not.
The full chain still runs against the original audio wherever the set is
present.
* ci: send a full browser agent when fetching the EBU loudness test set
* fix: apply the programme-loudness review round
The Tech 3341 true-peak conformance checks now judge with the asymmetric
+0.2/-0.4 dB window the specification defines. Long programmes recover the
inter-sample peak in bounded chunks (with a margin far beyond the FIR
support, verified exactly equal to the whole-signal computation) so the
memory use no longer scales with duration. Non-finite loudspeaker positions
and channel weights are rejected, the momentary and short-term steps
enforce the 10 Hz minimum update rate the API documents, the curated API
table states the real 16 kHz floor, the plot's short-term label became a
caller-overridable default, the download sentinel and the oracle skip check
both files, the series test closes its npz handle and the Spanish landing
names the exact BS.1770-5 revision.