Files
STSheets/tests/pipeline/quantize.test.ts
T
2026-10-30 05:00:20 +01:00

36 lines
1.4 KiB
TypeScript

import { test, expect } from "bun:test";
import { quantize } from "../../src/pipeline/quantize.ts";
import type { TimedNote } from "../../src/core/types.ts";
const t = (onset: number, offset: number, pitch: number): TimedNote => ({ onset, offset, pitch });
const g = { bpm: 120, firstDownbeat: 0.1, beatsPerBar: 4 };
test("snaps onsets and durations to the 16th grid relative to the first downbeat", () => {
const q = quantize([t(0.1 + 0.5 + 0.02, 0.1 + 1.0, 60), t(0.1 + 0.75 - 0.015, 0.1 + 1.0, 64)], g);
expect(q.map((n) => [n.tick, n.dur, n.pitch])).toEqual([[960, 960, 60], [1440, 480, 64]]);
});
test("drops notes before the first downbeat", () => {
const q = quantize([t(0.0, 0.3, 60), t(0.1, 0.4, 62)], g);
expect(q.map((n) => n.pitch)).toEqual([62]);
});
test("merges chord notes within 35 ms, with one consistent duration", () => {
const q = quantize([t(0.6, 1.6, 60), t(0.625, 1.1, 64), t(0.63, 1.6, 67)], g);
expect(new Set(q.map((n) => n.tick)).size).toBe(1);
expect(new Set(q.map((n) => n.dur)).size).toBe(1);
expect(q).toHaveLength(3);
});
test("collapses exact duplicates, enforces min duration", () => {
const q = quantize([t(0.1, 0.101, 60), t(0.1, 0.101, 60), t(0.225, 3, 62)], g);
expect(q.map((n) => [n.tick, n.pitch])).toEqual([[0, 60], [240, 62]]);
expect(q[0]!.dur).toBe(240);
for (const n of q) {
expect(n.tick % 240).toBe(0);
expect(n.dur % 240).toBe(0);
expect(n.dur).toBeGreaterThanOrEqual(240);
}
});