KEXEL's picture
1.1
b0bfea8 verified
import { IndexedByteArray } from "../utils/indexed_array.js";
/**
* midi_message.js
* purpose: contains enums for midi events and controllers and functions to parse them
*/
export class MIDIMessage
{
/**
* Absolute number of MIDI ticks from the start of the track.
* @type {number}
*/
ticks;
/**
* The MIDI message status byte. Note that for meta events, it is the second byte. (not 0xFF)
* @type {number}
*/
messageStatusByte;
/**
* Message's binary data
* @type {IndexedByteArray}
*/
messageData;
/**
* @param ticks {number}
* @param byte {number} the message status byte
* @param data {IndexedByteArray}
*/
constructor(ticks, byte, data)
{
this.ticks = ticks;
this.messageStatusByte = byte;
this.messageData = data;
}
}
/**
* Gets the status byte's channel
* @param statusByte
* @returns {number} channel is -1 for system messages -2 for meta and -3 for sysex
*/
export function getChannel(statusByte)
{
const eventType = statusByte & 0xF0;
const channel = statusByte & 0x0F;
let resultChannel = channel;
switch (eventType)
{
// midi (and meta and sysex headers)
case 0x80:
case 0x90:
case 0xA0:
case 0xB0:
case 0xC0:
case 0xD0:
case 0xE0:
break;
case 0xF0:
switch (channel)
{
case 0x0:
resultChannel = -3;
break;
case 0x1:
case 0x2:
case 0x3:
case 0x4:
case 0x5:
case 0x6:
case 0x7:
case 0x8:
case 0x9:
case 0xA:
case 0xB:
case 0xC:
case 0xD:
case 0xE:
resultChannel = -1;
break;
case 0xF:
resultChannel = -2;
break;
}
break;
default:
resultChannel = -1;
}
return resultChannel;
}
// all the midi statuses dictionary
export const messageTypes = {
noteOff: 0x80,
noteOn: 0x90,
polyPressure: 0xA0,
controllerChange: 0xB0,
programChange: 0xC0,
channelPressure: 0xD0,
pitchBend: 0xE0,
systemExclusive: 0xF0,
timecode: 0xF1,
songPosition: 0xF2,
songSelect: 0xF3,
tuneRequest: 0xF6,
clock: 0xF8,
start: 0xFA,
continue: 0xFB,
stop: 0xFC,
activeSensing: 0xFE,
reset: 0xFF,
sequenceNumber: 0x00,
text: 0x01,
copyright: 0x02,
trackName: 0x03,
instrumentName: 0x04,
lyric: 0x05,
marker: 0x06,
cuePoint: 0x07,
programName: 0x08,
midiChannelPrefix: 0x20,
midiPort: 0x21,
endOfTrack: 0x2F,
setTempo: 0x51,
smpteOffset: 0x54,
timeSignature: 0x58,
keySignature: 0x59,
sequenceSpecific: 0x7F
};
/**
* Gets the event's status and channel from the status byte
* @param statusByte {number} the status byte
* @returns {{channel: number, status: number}} channel will be -1 for sysex and meta
*/
export function getEvent(statusByte)
{
const status = statusByte & 0xF0;
const channel = statusByte & 0x0F;
let eventChannel = -1;
let eventStatus = statusByte;
if (status >= 0x80 && status <= 0xE0)
{
eventChannel = channel;
eventStatus = status;
}
return {
status: eventStatus,
channel: eventChannel
};
}
/**
* @enum {number}
*/
export const midiControllers = {
bankSelect: 0,
modulationWheel: 1,
breathController: 2,
footController: 4,
portamentoTime: 5,
dataEntryMsb: 6,
mainVolume: 7,
balance: 8,
pan: 10,
expressionController: 11,
effectControl1: 12,
effectControl2: 13,
generalPurposeController1: 16,
generalPurposeController2: 17,
generalPurposeController3: 18,
generalPurposeController4: 19,
lsbForControl0BankSelect: 32,
lsbForControl1ModulationWheel: 33,
lsbForControl2BreathController: 34,
lsbForControl4FootController: 36,
lsbForControl5PortamentoTime: 37,
lsbForControl6DataEntry: 38,
lsbForControl7MainVolume: 39,
lsbForControl8Balance: 40,
lsbForControl10Pan: 42,
lsbForControl11ExpressionController: 43,
lsbForControl12EffectControl1: 44,
lsbForControl13EffectControl2: 45,
sustainPedal: 64,
portamentoOnOff: 65,
sostenutoPedal: 66,
softPedal: 67,
legatoFootswitch: 68,
hold2Pedal: 69,
soundVariation: 70,
filterResonance: 71,
releaseTime: 72,
attackTime: 73,
brightness: 74,
decayTime: 75,
vibratoRate: 76,
vibratoDepth: 77,
vibratoDelay: 78,
soundController10: 79,
generalPurposeController5: 80,
generalPurposeController6: 81,
generalPurposeController7: 82,
generalPurposeController8: 83,
portamentoControl: 84,
reverbDepth: 91,
tremoloDepth: 92,
chorusDepth: 93,
detuneDepth: 94,
phaserDepth: 95,
dataIncrement: 96,
dataDecrement: 97,
NRPNLsb: 98,
NRPNMsb: 99,
RPNLsb: 100,
RPNMsb: 101,
allSoundOff: 120,
resetAllControllers: 121,
localControlOnOff: 122,
allNotesOff: 123,
omniModeOff: 124,
omniModeOn: 125,
monoModeOn: 126,
polyModeOn: 127
};
/**
* @type {{"11": number, "12": number, "13": number, "14": number, "8": number, "9": number, "10": number}}
*/
export const dataBytesAmount = {
0x8: 2, // note off
0x9: 2, // note on
0xA: 2, // note at
0xB: 2, // cc change
0xC: 1, // pg change
0xD: 1, // channel after touch
0xE: 2 // pitch wheel
};