Files
STSheets/tests/pipeline/quantize.test.ts
T

51 lines
2.2 KiB
TypeScript

import { test, expect } from "bun:test";
import { quantize, snapTicks } 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 (median) duration", () => {
// snapped durations: 8, 4, 8 steps -> median 8
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);
expect(q[0]!.dur).toBe(8 * 240);
});
test("one 70 ms ghost note does not collapse a long chord (median duration)", () => {
const q = quantize([t(0.5, 2.5, 60), t(0.5, 2.5, 64), t(0.5, 0.57, 67)], g);
expect(q).toHaveLength(3);
for (const n of q) expect(n.dur).toBe(16 * 240);
});
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);
}
});
test("snapTicks matches quantize ticks, incl. chord clusters and adjacent repeated pitches", () => {
// two same-pitch notes 20 ms apart merge; the next repeat is on the adjacent 16th (125 ms at 120 bpm)
const notes = [t(0.1, 0.2, 60), t(0.12, 0.2, 60), t(0.225, 0.3, 60)];
expect(snapTicks(notes, g)).toEqual([0, 0, 240]);
expect(quantize(notes, g).map((n) => n.tick)).toEqual([0, 240]);
});