94 lines
2.5 KiB
TypeScript
94 lines
2.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { inspectSoundFont } from "../../src/core/soundfont";
|
|
import { allNotesOffMessages, webMidiSchedule } from "../../src/core/web-midi";
|
|
import type { MidiDocument } from "../../src/core/midi";
|
|
|
|
const midi: MidiDocument = {
|
|
name: "local.mid",
|
|
format: 0,
|
|
division: 480,
|
|
warnings: [],
|
|
tracks: [
|
|
{
|
|
name: "Notes",
|
|
events: [
|
|
{
|
|
kind: "channel",
|
|
tick: 0,
|
|
order: 0,
|
|
status: 9,
|
|
channel: 2,
|
|
data: Uint8Array.of(64, 99),
|
|
},
|
|
{
|
|
kind: "channel",
|
|
tick: 480,
|
|
order: 1,
|
|
status: 8,
|
|
channel: 2,
|
|
data: Uint8Array.of(64, 0),
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
|
|
describe("explicit local playback helpers", () => {
|
|
it("creates channel-only Web MIDI schedules and panic messages", () => {
|
|
expect(webMidiSchedule(midi)).toEqual([
|
|
{ offsetMs: 0, data: [0x92, 64, 99] },
|
|
{ offsetMs: 500, data: [0x82, 64, 0] },
|
|
]);
|
|
expect(allNotesOffMessages()).toHaveLength(16);
|
|
expect(allNotesOffMessages()[2]).toEqual([0xb2, 123, 0]);
|
|
});
|
|
|
|
it("inspects a bounded RIFF SoundFont without synthesizing it", () => {
|
|
const info = list("INFO", chunk("INAM", text("Fixture bank\0")));
|
|
const phdr = chunk("phdr", new Uint8Array(38 * 2));
|
|
const pdta = list("pdta", phdr);
|
|
const body = bytes(text("sfbk"), info, pdta);
|
|
const fixture = bytes(text("RIFF"), le32(body.length), body);
|
|
expect(inspectSoundFont("fixture.sf2", fixture)).toMatchObject({
|
|
bankName: "Fixture bank",
|
|
presets: 1,
|
|
sections: ["INFO", "pdta"],
|
|
});
|
|
fixture[8] = 0;
|
|
expect(() => inspectSoundFont("bad.sf2", fixture)).toThrow(/sfbk/u);
|
|
});
|
|
});
|
|
|
|
function list(type: string, ...children: Uint8Array[]): Uint8Array {
|
|
return chunk("LIST", bytes(text(type), ...children));
|
|
}
|
|
|
|
function chunk(id: string, data: Uint8Array): Uint8Array {
|
|
return bytes(
|
|
text(id),
|
|
le32(data.length),
|
|
data,
|
|
...(data.length & 1 ? [Uint8Array.of(0)] : []),
|
|
);
|
|
}
|
|
|
|
function bytes(...parts: Uint8Array[]): Uint8Array {
|
|
const output = new Uint8Array(
|
|
parts.reduce((sum, part) => sum + part.length, 0),
|
|
);
|
|
let offset = 0;
|
|
for (const part of parts) {
|
|
output.set(part, offset);
|
|
offset += part.length;
|
|
}
|
|
return output;
|
|
}
|
|
|
|
function text(value: string): Uint8Array {
|
|
return new TextEncoder().encode(value);
|
|
}
|
|
|
|
function le32(value: number): Uint8Array {
|
|
return Uint8Array.of(value, value >>> 8, value >>> 16, value >>> 24);
|
|
}
|