import { describe, expect, it } from "@suaveplan/testing/runner"; import { GM_LEVEL1_FAMILIES, GM_LEVEL1_PATCHES, GM_LEVEL1_PERCUSSION, GM_LEVEL2_VARIATIONS, GS_VARIATIONS, gm2Key, XG_VARIATIONS, } from "../src/gm/index.js"; import { MIDI_MESSAGE_TYPES } from "../src/message/message.types.js"; import { midi1ToUmp, umpToMidi1 } from "../src/ump/ump.js"; describe("General MIDI Level 1 table integrity", () => { it("has all 128 programs", { timeout: 5000 }, () => { expect(Object.keys(GM_LEVEL1_PATCHES).length).toBe(128); for (let i = 0; i < 128; i += 1) { expect(typeof GM_LEVEL1_PATCHES[i]).toBe("string"); } }); it("GM patch 0 is Acoustic Grand Piano", { timeout: 5000 }, () => { expect(GM_LEVEL1_PATCHES[0]).toBe("Acoustic Grand Piano"); }); it("has percussion notes 35..81 (47 entries)", { timeout: 5000 }, () => { expect(Object.keys(GM_LEVEL1_PERCUSSION).length).toBe(81 - 35 + 1); expect(GM_LEVEL1_PERCUSSION[36]).toBe("Bass Drum 1"); expect(GM_LEVEL1_PERCUSSION[42]).toBe("Closed Hi Hat"); expect(GM_LEVEL1_PERCUSSION[81]).toBe("Open Triangle"); }); it("families list has 16 entries", { timeout: 5000 }, () => { expect(GM_LEVEL1_FAMILIES.length).toBe(16); }); it("GM2 / GS / XG dialects expose at least one variation each", { timeout: 5000, }, () => { expect(Object.keys(GM_LEVEL2_VARIATIONS).length).toBeGreaterThan(0); expect(Object.keys(GS_VARIATIONS).length).toBeGreaterThan(0); expect(Object.keys(XG_VARIATIONS).length).toBeGreaterThan(0); }); it("gm2Key composes the same way both tables encode", { timeout: 5000, }, () => { expect(gm2Key(4, 1)).toBe(4 * 256 + 1); expect(GM_LEVEL2_VARIATIONS[gm2Key(4, 1)]).toBe( "Detuned Electric Piano 1" ); }); }); describe("UMP framing invariants", () => { it("group bits map to bits 24..27 of word 0", { timeout: 5000 }, () => { const packet = midi1ToUmp( { type: "noteOn", channel: 0, note: 60, velocity: 100 }, 3 ); expect(((packet.words[0] as number) >>> 24) & 0xf).toBe(3); }); it("channel-voice round-trip preserves all 16 channels", { timeout: 5000, }, () => { for (let ch = 0; ch < 16; ch += 1) { const msg = { type: "noteOn" as const, channel: ch, note: 60, velocity: 64, }; expect(umpToMidi1(midi1ToUmp(msg, 0))).toEqual(msg); } }); }); describe("Message-type registry", () => { it("MIDI_MESSAGE_TYPES contains all 18 variants exactly once", { timeout: 5000, }, () => { expect(MIDI_MESSAGE_TYPES.length).toBe(18); expect(new Set(MIDI_MESSAGE_TYPES).size).toBe(18); }); });