Audio safety math
Decibel arithmetic, dose rules and loudness metering.
- Repository
audio-safety-mathon GitHub- Cite
- doi:10.5281/zenodo.24044023 (all versions, via Zenodo)
- Layer
- Layer 0
- Licence
- Apache-2.0
- Status
- released
Claims
| Claim | Level | Statement | Evidence |
|---|---|---|---|
safety:DB-PRODUCT v1.0.0 | P | For amplitude ratios a, b > 0, 20·log₁₀(a·b) = 20·log₁₀ a + 20·log₁₀ b: gains in a chain multiply, so their decibels add. | |
safety:INCOHERENT-SUM-BOUNDS v1.0.0 | P | For n ≥ 1 levels Lᵢ, max Lᵢ ≤ 10·log₁₀ Σ 10^(Lᵢ/10) ≤ max Lᵢ + 10·log₁₀ n: unrelated sources together are at least as loud as the loudest one and at most 10·log₁₀ n dB louder. |
|
safety:EXCHANGE-RATE v1.0.0 | P | With allowed time T(L) = T_c / 2^((L − L₀)/q) for criterion level L₀, exchange rate q ≠ 0 and criterion duration T_c, T(L + q) = T(L)/2, so a dose of t hours at L equals a dose of t/2 hours at L + q. | |
safety:THREE-DB-IS-ABOUT-DOUBLING v1.0.0 | P | 3.0102 < 10·log₁₀ 2 < 3.0103: doubling the power adds just over 3 dB, so a 3 dB exchange rate approximates the equal-energy rule. | |
safety:TWA-INVERTS-DOSE v1.0.0 | P | A whole criterion duration T_c ≠ 0 at a steady level L is a dose of 100·2^((L − L₀)/q) percent, and the time-weighted average L₀ + q·log₂(D/100) of that dose is L again (q ≠ 0). |
|
safety:NIOSH-REL v1.0.0 | R | The default dose parameters are the NIOSH recommended exposure limit for occupational noise: 85 dBA as an 8-hour time-weighted average, with a 3 dB exchange rate (every 3 dB more halves the allowed time). (Homo sapiens (workers); Recommended exposure limit in a NIOSH criteria document (US federal guidance), checked against the document's text (Chapter 1, Section 1.1), used as the default parameters of allowed_time and noise_dose; not re-derived here.) | |
safety:DB-CONVERSIONS v1.0.0 | T | amplitude_to_db, db_to_amplitude, power_to_db and db_to_power match the formulas evaluated in 50-digit arithmetic within 1e-12, decibels of products add within 1e-12 dB in the code, conversions round-trip within 1e-13 relative, and 10·log₁₀ 2 in the code lies between 3.0102 and 3.0103. |
|
safety:LEVEL-SUMS v1.0.0 | T | sum_incoherent_db, energetic_mean_db and leq match the formulas evaluated in 50-digit arithmetic within 1e-12; in the floating-point code the incoherent sum always lies within the proved bounds, and levels from −5000 to 7000 dB give finite results, as does leq of a segment at 6000 dB lasting 10⁻³³⁰ of the longest (which matches 50-digit arithmetic), because each sum is factored around its largest term, with the durations entering as logarithms. |
|
safety:LEVELS-MATCH-REFERENCE v1.0.0 | T | rms_dbfs and normalize (on signal-core's zero-phase 12 Hz high-pass) match lifesound.py's rms_dbfs and normalize, run in numpy with lifesound.py's own box_lowpass and highpass, within 1e-12, and peak_dbfs, and rms_dbfs of samples whose squares underflow or overflow an f64, match 20·log₁₀ max|x| and 20·log₁₀ √(mean(x²)) in 50-digit arithmetic; rms_dbfs no longer adds 1e-12 before the logarithm, so silence is an error rather than −240 dBFS. normalize reaches the RMS target unless the peak ceiling binds, and then puts the peak at the ceiling; a signal left with only rounding residue after the high-pass (a constant, exact in binary or not) is an error rather than noise scaled to the target, and its gain overflows only when the result is too large for an f64 (not for a ceiling that does not bind, nor to lift subnormal samples); samples so large that the high-pass's running sums overflow, which takes a peak of at least about 1.8 × 10³⁰⁸/(len(x) + L) for the box length L, are an error on x, even when the result would fit. | |
safety:DOSE-MATCHES-REFERENCE v1.0.0 | T | allowed_time, noise_dose and twa_from_dose match the formulas evaluated in 50-digit arithmetic within 1e-12 (with the NIOSH parameters, 88 dBA is allowed exactly 4 h and 100 dBA 15 min); in the floating-point code one exchange rate more halves the allowed time and keeps the dose of half the time within 1e-13 relative, and the TWA of a full day at a steady level returns that level within 1e-12 dB; the shares of the dose are summed as powers of two around the largest, so 10⁻¹⁰ hours at 3160 dBA, whose 2^((L − L₀)/q) overflows an f64, still gives its dose of 4.49 × 10²⁹⁹ %. |
|
safety:K-WEIGHTING-COEFFICIENTS v1.0.0 | T | k_weighting builds both K-weighting stages from fixed analogue prototypes by the bilinear transform prewarped at each stage's corner frequency; at 48 kHz it reproduces the coefficients published in ITU-R BS.1770 within 1e-14, at other rates it matches the same construction in 50-digit arithmetic within 1e-12 (1e-9 at the lowest rate, 3364 Hz), and below 6 kHz its response at 44.1, 96 and 192 kHz stays within 0.01 dB of the 48 kHz filter. | |
safety:K-WEIGHT-FILTER v1.0.0 | T | k_weight filters each channel of an interleaved signal through both stages from rest and matches scipy.signal.lfilter with the reference coefficients within 1e-12. |
|
safety:LOUDNESS-MATCHES-PYLOUDNORM v1.0.0 | T | integrated_loudness and momentary_loudness match pyloudnorm 0.2.0's meter run with the reference K-weighting within 1e-9 LU, for mono, stereo and 5.0 signals and signals that exercise both gates; short_term_loudness and a 5.1 case with an LFE weight of 0 match a direct transcription of the BS.1770 formulas within 1e-9 LU; a gain of g adds 20·log₁₀ g LU and a copied channel adds 10·log₁₀ 2 within 1e-10. |
|
safety:EBU-3341 v1.0.0 | T | On the EBU Tech 3341 test signals 1 to 6 and 9 (1 kHz sines at 48 kHz), integrated_loudness reads −22.993 (case 1), −32.993 (2), −23.014 (3 and 4), −22.979 (5) and −23.016 (6) LUFS, momentary and short-term loudness stay within 0.007 LU of −23 or −33 LUFS (cases 1 and 2) and short-term within 0.014 LU of −23 LUFS (case 9): all inside the published ±0.1 LU, and the integrated readings within 1e-6 LU of pyloudnorm's. |
|
safety:INPUT-VALIDATION v1.0.0 | T | Every function rejects NaN and infinite input, silence where a single level is asked for (a zero ratio, an all-zero signal, a signal left with only rounding residue after normalize's high-pass, every loudness block below the absolute gate), negative durations and weights, mismatched lists, buffers that are not whole frames, sample rates below 3364 Hz for loudness, and results that overflow an f64, including a filtered signal or a loudness energy that overflows on the way; Rust returns SafetyError, Python raises ValueError (OverflowError for a negative or too-large integer argument such as a sample rate, TypeError for a non-integer one) and JavaScript throws an Error. The meters return −∞ for a silent window. | |
safety:IMPLEMENTATIONS-AGREE v1.0.0 | T | The Rust crate, the Python bindings and the WebAssembly bindings all reproduce every golden vector. Every expected output comes from an independent reference (mpmath, lifesound.py with numpy, scipy, pyloudnorm, or a transcription of BS.1770), never from the Rust output; the error cases in errors.json are the crate's own input rules. | |
Depends on
signal-corereleasedevidence-kernelreleased
Used by
functional-music-generatorsplanned
Posts about this repo
No posts about this repo yet.