Files
2026-05-27 23:47:53 +01:00

60 lines
2.1 KiB
TypeScript

import { describe, expect, it } from "@suaveplan/testing/runner";
import fc from "fast-check";
import { yinPitch } from "../src/algorithms/yin.js";
describe("yinPitch property tests", () => {
it("estimated pitch confidence is always in [0,1]", {
timeout: 30_000,
}, () => {
fc.assert(
fc.property(fc.integer({ min: 80, max: 2000 }), (freq) => {
const sr = 48_000;
const n = 4096;
const f = new Float32Array(n);
for (let i = 0; i < n; i++)
f[i] = Math.sin((2 * Math.PI * freq * i) / sr);
const r = yinPitch(f, sr, 20, 20_000);
return r.confidence >= 0 && r.confidence <= 1;
}),
{ numRuns: 30 }
);
});
it("estimated pitch is non-negative", { timeout: 30_000 }, () => {
fc.assert(
fc.property(fc.integer({ min: 100, max: 4000 }), (freq) => {
const sr = 48_000;
const n = 4096;
const f = new Float32Array(n);
for (let i = 0; i < n; i++)
f[i] = Math.sin((2 * Math.PI * freq * i) / sr);
const r = yinPitch(f, sr, 20, 20_000);
return r.pitch >= 0;
}),
{ numRuns: 30 }
);
});
it("silence always yields zero pitch", { timeout: 5_000 }, () => {
fc.assert(
fc.property(fc.integer({ min: 1024, max: 8192 }), (n) => {
const r = yinPitch(new Float32Array(n), 48_000, 20, 20_000);
return r.pitch === 0 && r.confidence === 0;
}),
{ numRuns: 20 }
);
});
});
describe("440 Hz sine estimation", () => {
it("yields midi 69 for a 440 Hz tone", { timeout: 5_000 }, () => {
const sr = 48_000;
const n = 4096;
const f = new Float32Array(n);
for (let i = 0; i < n; i++)
f[i] = Math.sin((2 * Math.PI * 440 * i) / sr);
const r = yinPitch(f, sr, 20, 20_000);
expect(Math.abs(r.pitch - 440)).toBeLessThan(1);
});
});