Files
suaveplan/packages/media/audio/audio-analysis-loudness/tests/property.test.ts
2026-05-27 23:47:53 +01:00

55 lines
2.0 KiB
TypeScript

import { describe, it } from "@suaveplan/testing/runner";
import fc from "fast-check";
import { LoudnessMeter, samplePeakLinear, truePeakDbtp } from "../src/index.js";
import { buildDoc, sine } from "./helpers/build-doc.js";
describe("LoudnessMeter properties", () => {
it("LUFS is monotonic with gain on a sine", {
timeout: 60000,
}, async () => {
await fc.assert(
fc.asyncProperty(
fc.double({ min: 0.05, max: 0.5, noNaN: true }),
fc.double({ min: 0.51, max: 0.95, noNaN: true }),
async (a, b) => {
const sr = 48000;
const dur = 3.5;
const meter = new LoudnessMeter();
const lo = await meter.analyze(
buildDoc([sine(a, 1000, sr, dur)], sr)
);
const hi = await meter.analyze(
buildDoc([sine(b, 1000, sr, dur)], sr)
);
return hi.lufs >= lo.lufs - 0.5;
}
),
{ numRuns: 6 }
);
});
it("true peak ≥ sample peak for arbitrary blocks", {
timeout: 30000,
}, () => {
fc.assert(
fc.property(
fc.array(fc.double({ min: -1, max: 1, noNaN: true }), {
minLength: 32,
maxLength: 4096,
}),
(arr) => {
const ch = Float32Array.from(arr);
const sp = samplePeakLinear([ch]);
// Below Float32 quantisation, sample peak cannot be
// round-trip compared in dB: skip such degenerate inputs.
if (sp < 1e-6) return true;
const spDb = 20 * Math.log10(sp);
const tpDb = truePeakDbtp([ch]);
return tpDb >= spDb - 0.1;
}
),
{ numRuns: 25 }
);
});
});