# gm/level2 ## Purpose General MIDI Level 2 added a bank-select-driven variation layer on top of GM1. Each GM1 program (0..127) gains optional variation banks selected by Bank-Select LSB (CC 32). Bank 0 is always the GM1 default; banks 1..127 are reserved for GM2 variations. This module exposes the variation-name lookup so engines can render `(program, bankLsb)` pairs into a user-visible patch name. The table here mirrors the GM2 v1.2 *mandatory* variation set. Full-coverage engines (hundreds of additional variations) layer their own tables on top via `Object.assign({}, GM_LEVEL2_VARIATIONS, …)` — keeping the base set small means importing this module costs negligible bundle size for consumers that only need the GM1 fallback path. ## Features - `GM_LEVEL2_VARIATIONS` frozen map keyed by `program * 256 + bankLsb`. - `gm2Key(program, bankLsb)` helper for composing the lookup key. - Bank 0 entries are deliberately omitted; consumers fall back to `GM_LEVEL1_PATCHES`. - Composable with downstream extension tables via the frozen-object idiom. ## Usage: Basic ```ts import { GM_LEVEL2_VARIATIONS, gm2Key } from "@suaveplan/midi"; const name = GM_LEVEL2_VARIATIONS[gm2Key(4, 1)]; // "Detuned Electric Piano 1" ``` ## Usage: Advanced ```ts import { GM_LEVEL1_PATCHES, GM_LEVEL2_VARIATIONS, gm2Key, } from "@suaveplan/midi"; function resolvePatchName(program: number, bankLsb: number): string { if (bankLsb === 0) return GM_LEVEL1_PATCHES[program] ?? `Program ${program}`; return ( GM_LEVEL2_VARIATIONS[gm2Key(program, bankLsb)] ?? GM_LEVEL1_PATCHES[program] ?? `Program ${program}` ); } ``` ## API Reference - `GM_LEVEL2_VARIATIONS: Readonly>` — frozen map keyed by `program * 256 + bankLsb`. - `gm2Key(program: number, bankLsb: number): number` — pure function returning the composite lookup key. ## Implementation Notes The composite key encoding (`program * 256 + bankLsb`) is preferred over a nested `Record>` because a flat key has constant-time lookup with no intermediate object allocation, and because both forms have the same `Object.freeze` cost. Variations are added selectively — full GM2 / GS / XG coverage is huge and varies by vendor, so consumers can choose which dialect tables to spread on top.