/** * MIDI file format constants for Standard MIDI File conversion */ // MIDI file header constants export const MIDI_HEADER = { // Header chunk identifier CHUNK_TYPE: new Uint8Array([0x4D, 0x54, 0x68, 0x64]), // "MThd" // Header chunk size (always 6 bytes) HEADER_SIZE: new Uint8Array([0x00, 0x00, 0x00, 0x06]), // MIDI file formats FORMAT_0: new Uint8Array([0x00, 0x00]), // Single track FORMAT_1: new Uint8Array([0x00, 0x01]), // Multi-track, synchronous FORMAT_2: new Uint8Array([0x00, 0x02]), // Multi-track, asynchronous }; // Track chunk constants export const MIDI_TRACK = { // Track chunk identifier CHUNK_TYPE: new Uint8Array([0x4D, 0x54, 0x72, 0x6B]), // "MTrk" // End of track meta event END_OF_TRACK: new Uint8Array([0x00, 0xFF, 0x2F, 0x00]), }; // Timing constants export const MIDI_TIMING = { // Ticks Per Quarter Note (TPQN) - 480 provides good resolution TPQN: 480, TPQN_BYTES: new Uint8Array([0x01, 0xE0]), // 480 in big-endian 16-bit }; // MIDI event types export const MIDI_EVENTS = { // Channel voice messages (status bytes) NOTE_OFF: 0x80, NOTE_ON: 0x90, POLY_PRESSURE: 0xA0, CONTROL_CHANGE: 0xB0, PROGRAM_CHANGE: 0xC0, CHANNEL_PRESSURE: 0xD0, PITCH_BEND: 0xE0, // System exclusive SYSEX: 0xF0, // Meta events META_EVENT: 0xFF, META_SEQUENCE_NUMBER: 0x00, META_TEXT: 0x01, META_COPYRIGHT: 0x02, META_TRACK_NAME: 0x03, META_INSTRUMENT_NAME: 0x04, META_LYRIC: 0x05, META_MARKER: 0x06, META_CUE_POINT: 0x07, META_CHANNEL_PREFIX: 0x20, META_END_OF_TRACK: 0x2F, META_TEMPO: 0x51, META_SMPTE_OFFSET: 0x54, META_TIME_SIGNATURE: 0x58, META_KEY_SIGNATURE: 0x59, META_SEQUENCER_SPECIFIC: 0x7F, }; // MIDI channels (0-based) export const MIDI_CHANNELS = { PIANO: 0, GUITAR: 1, BASS: 2, DRUMS: 9, // Channel 10 (0-based = 9) is reserved for percussion }; // General MIDI instrument program numbers (0-based) export const GM_INSTRUMENTS = { PIANO: 0, // Acoustic Grand Piano GUITAR: 24, // Acoustic Guitar (nylon) BASS: 32, // Acoustic Bass DRUMS: 0, // Not used for drums (uses channel 10) }; // MIDI velocity constants export const MIDI_VELOCITY = { MIN: 1, MAX: 127, DEFAULT: 64, }; // MIDI note constants export const MIDI_NOTES = { MIN: 0, MAX: 127, MIDDLE_C: 60, // C4 }; // Time signature constants export const MIDI_TIME_SIGNATURE = { // Denominator values for MIDI (power of 2) DENOMINATOR_MAP: { 2: 1, // 2 = 2^1 4: 2, // 4 = 2^2 8: 3, // 8 = 2^3 16: 4, // 16 = 2^4 } as const, // Default values DEFAULT_METRONOME_PULSE: 24, // MIDI clocks per quarter note DEFAULT_32ND_NOTES_PER_QUARTER: 8, }; // Key signature constants (for MIDI key signature meta event) export const MIDI_KEY_SIGNATURE = { // Sharp keys (positive values) SHARP_KEYS: { 'C major': 0, 'G major': 1, 'D major': 2, 'A major': 3, 'E major': 4, 'B major': 5, 'F# major': 6, 'C# major': 7, }, // Flat keys (negative values) FLAT_KEYS: { 'F major': -1, 'Bb major': -2, 'Eb major': -3, 'Ab major': -4, 'Db major': -5, 'Gb major': -6, 'Cb major': -7, }, // Minor keys MINOR_KEYS: { 'A minor': 0, 'E minor': 1, 'B minor': 2, 'F# minor': 3, 'C# minor': 4, 'G# minor': 5, 'D# minor': 6, 'A# minor': 7, 'D minor': -1, 'G minor': -2, 'C minor': -3, 'F minor': -4, 'Bb minor': -5, 'Eb minor': -6, 'Ab minor': -7, }, } as const; // Utility functions for MIDI data conversion export const MIDI_UTILS = { /** * Convert a 32-bit integer to big-endian byte array */ int32ToBytes: (value: number): Uint8Array => { return new Uint8Array([ (value >> 24) & 0xFF, (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, ]); }, /** * Convert a 16-bit integer to big-endian byte array */ int16ToBytes: (value: number): Uint8Array => { return new Uint8Array([ (value >> 8) & 0xFF, value & 0xFF, ]); }, /** * Convert a variable-length quantity (VLQ) to bytes * Used for MIDI delta times */ encodeVLQ: (value: number): Uint8Array => { const bytes: number[] = []; // Handle zero case if (value === 0) { return new Uint8Array([0]); } // Convert to VLQ format let temp = value & 0x7F; while ((value >>= 7) > 0) { temp <<= 8; temp |= ((value & 0x7F) | 0x80); } // Extract bytes while (true) { bytes.push(temp & 0xFF); if (temp & 0x80) { temp >>= 8; } else { break; } } return new Uint8Array(bytes); }, /** * Calculate microseconds per quarter note from BPM */ bpmToMicrosecondsPerQuarter: (bpm: number): number => { return Math.round(60000000 / bpm); }, };