60 lines
2.1 KiB
TypeScript
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);
|
|
});
|
|
});
|