Files
KGStudio/src/constants/midiConstants.ts
T
2025-08-11 18:37:21 -07:00

220 lines
4.8 KiB
TypeScript

/**
* 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);
},
};