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); }