0ad1ec1midiboot: in-browser MIDI workstation with pluggable instrumentsJeremy Magland 1// General MIDI program names, used to label imported tracks.
2export const GM_PROGRAMS: string[] = [
3 'Acoustic Grand Piano', 'Bright Acoustic Piano', 'Electric Grand Piano', 'Honky-tonk Piano',
4 'Electric Piano 1', 'Electric Piano 2', 'Harpsichord', 'Clavinet',
5 'Celesta', 'Glockenspiel', 'Music Box', 'Vibraphone',
6 'Marimba', 'Xylophone', 'Tubular Bells', 'Dulcimer',
7 'Drawbar Organ', 'Percussive Organ', 'Rock Organ', 'Church Organ',
8 'Reed Organ', 'Accordion', 'Harmonica', 'Tango Accordion',
9 'Acoustic Guitar (nylon)', 'Acoustic Guitar (steel)', 'Electric Guitar (jazz)', 'Electric Guitar (clean)',
10 'Electric Guitar (muted)', 'Overdriven Guitar', 'Distortion Guitar', 'Guitar Harmonics',
11 'Acoustic Bass', 'Electric Bass (finger)', 'Electric Bass (pick)', 'Fretless Bass',
12 'Slap Bass 1', 'Slap Bass 2', 'Synth Bass 1', 'Synth Bass 2',
13 'Violin', 'Viola', 'Cello', 'Contrabass',
14 'Tremolo Strings', 'Pizzicato Strings', 'Orchestral Harp', 'Timpani',
15 'String Ensemble 1', 'String Ensemble 2', 'Synth Strings 1', 'Synth Strings 2',
16 'Choir Aahs', 'Voice Oohs', 'Synth Voice', 'Orchestra Hit',
17 'Trumpet', 'Trombone', 'Tuba', 'Muted Trumpet',
18 'French Horn', 'Brass Section', 'Synth Brass 1', 'Synth Brass 2',
19 'Soprano Sax', 'Alto Sax', 'Tenor Sax', 'Baritone Sax',
20 'Oboe', 'English Horn', 'Bassoon', 'Clarinet',
21 'Piccolo', 'Flute', 'Recorder', 'Pan Flute',
22 'Blown Bottle', 'Shakuhachi', 'Whistle', 'Ocarina',
23 'Lead 1 (square)', 'Lead 2 (sawtooth)', 'Lead 3 (calliope)', 'Lead 4 (chiff)',
24 'Lead 5 (charang)', 'Lead 6 (voice)', 'Lead 7 (fifths)', 'Lead 8 (bass + lead)',
25 'Pad 1 (new age)', 'Pad 2 (warm)', 'Pad 3 (polysynth)', 'Pad 4 (choir)',
26 'Pad 5 (bowed)', 'Pad 6 (metallic)', 'Pad 7 (halo)', 'Pad 8 (sweep)',
27 'FX 1 (rain)', 'FX 2 (soundtrack)', 'FX 3 (crystal)', 'FX 4 (atmosphere)',
28 'FX 5 (brightness)', 'FX 6 (goblins)', 'FX 7 (echoes)', 'FX 8 (sci-fi)',
29 'Sitar', 'Banjo', 'Shamisen', 'Koto',
30 'Kalimba', 'Bag pipe', 'Fiddle', 'Shanai',
31 'Tinkle Bell', 'Agogo', 'Steel Drums', 'Woodblock',
32 'Taiko Drum', 'Melodic Tom', 'Synth Drum', 'Reverse Cymbal',
33 'Guitar Fret Noise', 'Breath Noise', 'Seashore', 'Bird Tweet',
34 'Telephone Ring', 'Helicopter', 'Applause', 'Gunshot',
35];
cdfbf5bRender whole tracks through direct-import instrument modulesJeremy Magland 37// Coarse mapping from a GM program number to the nearest builtin synth,
38// used when importing a MIDI file so playback lands roughly in the right
39// family without a soundfont. Channel 10 is handled separately (drum kit).
40export function gmProgramInstrument(program: number | undefined): string {
41 if (program === undefined) return 'piano';
42 const p = Math.max(0, Math.min(127, Math.floor(program)));
43 if (p <= 7) return 'piano'; // pianos
44 if (p <= 15) return 'epiano'; // chromatic percussion
45 if (p <= 23) return 'organ'; // organs and accordions
46 if (p <= 31) return 'pluck'; // guitars
47 if (p <= 39) return 'synthbass'; // basses
48 if (p <= 55) return 'strings'; // strings, ensembles, choirs
49 if (p <= 71) return 'brass'; // brass and reeds
50 if (p <= 87) return 'lead'; // pipes and synth leads
51 if (p <= 103) return 'strings'; // pads and FX
52 return 'pluck'; // ethnic, percussive, effects
53}
ca4875dCarry instrument ids and knob settings in exported MIDI filesJeremy Magland 55// Representative GM program per instrument id, written as a program
56// change on export so foreign players pick a plausible patch. The
57// simulated percussion has no true GM program; the nearest percussive
58// program is used. Unknown ids get none.
59const GM_PROGRAM_FOR_INSTRUMENT: Record<string, number> = {
60 piano: 0, epiano: 4, organ: 16, pluck: 24, synthbass: 38,
61 strings: 48, brass: 61, lead: 80,
62 bass: 32, 'bass-muted': 32, recorder: 74, woodblock: 115,
63 triangle: 112, cymbal2: 119, maraca: 113,
64};
66export function gmProgramForInstrument(instrumentId: string): number | null {
67 return GM_PROGRAM_FOR_INSTRUMENT[instrumentId] ?? null;
68}
0ad1ec1midiboot: in-browser MIDI workstation with pluggable instrumentsJeremy Magland 70export function noteName(pitch: number): string {
71 const names = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
72 return `${names[((pitch % 12) + 12) % 12]}${Math.floor(pitch / 12) - 1}`;
73}