# gm/level1 ## Purpose The General MIDI Level 1 patch and percussion tables — the canonical instrument-name lookups every GM-compliant playback engine, score editor, and patch-browser needs. Without a shared source of truth, every consumer ends up hand-typing the same 128-entry table with the same handful of typos ("Honky-Tonk Piano" vs "Honky-tonk Piano" being a perennial favourite). This module exposes three immutable, exhaustive tables: the 128-entry patch map (`GM_LEVEL1_PATCHES`, program 0..127), the 47-entry percussion map (`GM_LEVEL1_PERCUSSION`, notes 35..81), and the 16-entry family list (`GM_LEVEL1_FAMILIES`, grouped in eight-program blocks). Spelling and capitalisation follow the official MMA specification. ## Features - Frozen `GM_LEVEL1_PATCHES` covering every program 0..127 with the spec-canonical name. - Frozen `GM_LEVEL1_PERCUSSION` covering channel-10 drum notes 35..81. - Frozen `GM_LEVEL1_FAMILIES` for UI grouping (program ÷ 8). - All tables use the MMA canonical capitalisation — diff-compatible with other specs. ## Usage: Basic ```ts import { GM_LEVEL1_PATCHES, GM_LEVEL1_PERCUSSION } from "@suaveplan/midi"; console.log(GM_LEVEL1_PATCHES[0]); // "Acoustic Grand Piano" console.log(GM_LEVEL1_PERCUSSION[36]); // "Bass Drum 1" ``` ## Usage: Advanced ```ts import { GM_LEVEL1_FAMILIES, GM_LEVEL1_PATCHES } from "@suaveplan/midi"; // Build a hierarchical patch browser for a UI tree component. function buildPatchTree() { const tree = GM_LEVEL1_FAMILIES.map((family, i) => ({ name: family, patches: Array.from({ length: 8 }, (_, j) => { const program = i * 8 + j; return { program, name: GM_LEVEL1_PATCHES[program]! }; }), })); return tree; } ``` ## API Reference - `GM_LEVEL1_PATCHES: Readonly>` — frozen map, 128 entries (programs 0..127). - `GM_LEVEL1_PERCUSSION: Readonly>` — frozen map, 47 entries (notes 35..81 on channel 10). - `GM_LEVEL1_FAMILIES: readonly string[]` — 16 instrument-family names, indexed by `Math.floor(program / 8)`. ## Implementation Notes Both maps are declared as `Object.freeze({ ... })` rather than via numeric arrays because programs and percussion notes are sparse from the consumer's perspective — looking up `PERCUSSION[36]` directly is clearer than `PERCUSSION[36 - 35]`. The freeze guarantees downstream compose patterns (`{ ...GM_LEVEL1_PATCHES, 0: 'Custom Piano' }`) produce a new object rather than mutating the canonical table.