JavaScript library for MIDI communication that simplifies sending and receiving MIDI messages between browsers/Node.js and MIDI instruments
—
The Enumerations class provides MIDI specification constants and enumerations for all message types, control change mappings, and system messages. These constants ensure consistent and correct MIDI communication.
Constants for MIDI channel messages (0x80-0xEF).
class Enumerations {
/**
* Channel message type constants
*/
static readonly CHANNEL_MESSAGES: {
noteoff: number;
noteon: number;
keyaftertouch: number;
controlchange: number;
programchange: number;
channelaftertouch: number;
pitchbend: number;
};
/**
* MIDI channel message constants (with MIDI channel values 0-15)
*/
static readonly MIDI_CHANNEL_MESSAGES: {
noteoff: number;
noteon: number;
keyaftertouch: number;
controlchange: number;
programchange: number;
channelaftertouch: number;
pitchbend: number;
};
}Usage Examples:
import { Enumerations } from "webmidi";
// Channel message constants
console.log(Enumerations.CHANNEL_MESSAGES.noteon); // 9
console.log(Enumerations.CHANNEL_MESSAGES.noteoff); // 8
console.log(Enumerations.CHANNEL_MESSAGES.controlchange); // 11
console.log(Enumerations.CHANNEL_MESSAGES.programchange); // 12
console.log(Enumerations.CHANNEL_MESSAGES.pitchbend); // 14
// MIDI channel message constants (base values)
console.log(Enumerations.MIDI_CHANNEL_MESSAGES.noteon); // 144 (0x90)
console.log(Enumerations.MIDI_CHANNEL_MESSAGES.noteoff); // 128 (0x80)Valid channel number constants.
/**
* Valid channel numbers (1-16)
*/
static readonly CHANNEL_NUMBERS: number[];
/**
* MIDI channel numbers (0-15)
*/
static readonly MIDI_CHANNEL_NUMBERS: number[];Usage Examples:
// User-facing channel numbers (1-16)
console.log(Enumerations.CHANNEL_NUMBERS); // [1, 2, 3, ..., 16]
// MIDI protocol channel numbers (0-15)
console.log(Enumerations.MIDI_CHANNEL_NUMBERS); // [0, 1, 2, ..., 15]
// Validate channel numbers
function isValidChannel(channel) {
return Enumerations.CHANNEL_NUMBERS.includes(channel);
}
function isValidMidiChannel(channel) {
return Enumerations.MIDI_CHANNEL_NUMBERS.includes(channel);
}Comprehensive control change message mappings.
/**
* Control change message constants with descriptive names
*/
static readonly CONTROL_CHANGE_MESSAGES: {
bankselectcoarse: number;
modulationwheelcoarse: number;
breathcontrollercoarse: number;
footcontrollercoarse: number;
portamentotimecoarse: number;
dataentrycoarse: number;
volumecoarse: number;
balancecoarse: number;
pancoarse: number;
expressioncoarse: number;
effectcontrol1coarse: number;
effectcontrol2coarse: number;
generalpurposecontroller1: number;
generalpurposecontroller2: number;
generalpurposecontroller3: number;
generalpurposecontroller4: number;
bankselectfine: number;
modulationwheelfine: number;
breathcontrollerfine: number;
footcontrollerfine: number;
portamentotimefine: number;
dataentryfine: number;
volumefine: number;
balancefine: number;
panfine: number;
expressionfine: number;
effectcontrol1fine: number;
effectcontrol2fine: number;
sustain: number;
portamento: number;
sostenuto: number;
softpedal: number;
legatofootswitch: number;
hold2: number;
soundvariation: number;
resonance: number;
releasetime: number;
attacktime: number;
brightness: number;
decaytime: number;
vibratorate: number;
vibratodepth: number;
vibratodelay: number;
generalpurposecontroller5: number;
generalpurposecontroller6: number;
generalpurposecontroller7: number;
generalpurposecontroller8: number;
portamentocontrol: number;
effects1depth: number;
effects2depth: number;
effects3depth: number;
effects4depth: number;
effects5depth: number;
dataincrement: number;
datadecrement: number;
nonregisteredparametercoarse: number;
nonregisteredparameterfine: number;
registeredparametercoarse: number;
registeredparameterfine: number;
allsoundoff: number;
resetallcontrollers: number;
localcontrol: number;
allnotesoff: number;
omnimodeoff: number;
omnimodeon: number;
monomodeon: number;
polymodeon: number;
};
/**
* MIDI control change message constants (CC numbers 0-127)
*/
static readonly MIDI_CONTROL_CHANGE_MESSAGES: object;Usage Examples:
// Common control changes
console.log(Enumerations.CONTROL_CHANGE_MESSAGES.volumecoarse); // 7
console.log(Enumerations.CONTROL_CHANGE_MESSAGES.pancoarse); // 10
console.log(Enumerations.CONTROL_CHANGE_MESSAGES.sustain); // 64
console.log(Enumerations.CONTROL_CHANGE_MESSAGES.effects1depth); // 91 (reverb)
console.log(Enumerations.CONTROL_CHANGE_MESSAGES.effects3depth); // 93 (chorus)
// Channel mode messages
console.log(Enumerations.CONTROL_CHANGE_MESSAGES.allsoundoff); // 120
console.log(Enumerations.CONTROL_CHANGE_MESSAGES.allnotesoff); // 123
console.log(Enumerations.CONTROL_CHANGE_MESSAGES.resetallcontrollers); // 121
// Use in code
function sendVolume(output, channel, volume) {
const ccNumber = Enumerations.CONTROL_CHANGE_MESSAGES.volumecoarse;
output.sendControlChange(ccNumber, volume, { channels: channel });
}
function enableSustain(output, channel) {
const ccNumber = Enumerations.CONTROL_CHANGE_MESSAGES.sustain;
output.sendControlChange(ccNumber, 127, { channels: channel });
}Channel mode message constants for voice and mode control.
/**
* Channel mode message constants
*/
static readonly CHANNEL_MODE_MESSAGES: {
allsoundoff: number;
resetallcontrollers: number;
localcontrol: number;
allnotesoff: number;
omnimodeoff: number;
omnimodeon: number;
monomodeon: number;
polymodeon: number;
};
/**
* MIDI channel mode message constants
*/
static readonly MIDI_CHANNEL_MODE_MESSAGES: object;Usage Examples:
// Channel mode constants
console.log(Enumerations.CHANNEL_MODE_MESSAGES.allsoundoff); // 120
console.log(Enumerations.CHANNEL_MODE_MESSAGES.allnotesoff); // 123
console.log(Enumerations.CHANNEL_MODE_MESSAGES.resetallcontrollers); // 121
console.log(Enumerations.CHANNEL_MODE_MESSAGES.localcontrol); // 122
// Omni mode constants
console.log(Enumerations.CHANNEL_MODE_MESSAGES.omnimodeoff); // 124
console.log(Enumerations.CHANNEL_MODE_MESSAGES.omnimodeon); // 125
// Mono/poly mode constants
console.log(Enumerations.CHANNEL_MODE_MESSAGES.monomodeon); // 126
console.log(Enumerations.CHANNEL_MODE_MESSAGES.polymodeon); // 127
// Use in emergency stop function
function panicStop(output) {
const allSoundOff = Enumerations.CHANNEL_MODE_MESSAGES.allsoundoff;
const allNotesOff = Enumerations.CHANNEL_MODE_MESSAGES.allnotesoff;
// Send to all channels
for (let channel = 1; channel <= 16; channel++) {
output.sendControlChange(allSoundOff, 0, { channels: channel });
output.sendControlChange(allNotesOff, 0, { channels: channel });
}
}Registered Parameter Number (RPN) constants for standard parameters.
/**
* Registered parameter constants
*/
static readonly REGISTERED_PARAMETERS: {
pitchbendrange: number[];
channelfinetuning: number[];
channelcoarsetuning: number[];
tuningprogram: number[];
tuningbank: number[];
modulationrange: number[];
azimuthangle: number[];
elevationangle: number[];
gain: number[];
distanceratio: number[];
maximumdistance: number[];
gainincrease: number[];
referenceazimuthangle: number[];
referenceelvationangle: number[];
referencegain: number[];
referencedistanceratio: number[];
};
/**
* MIDI registered parameter constants
*/
static readonly MIDI_REGISTERED_PARAMETERS: object;Usage Examples:
// RPN constants
console.log(Enumerations.REGISTERED_PARAMETERS.pitchbendrange); // [0, 0]
console.log(Enumerations.REGISTERED_PARAMETERS.channelfinetuning); // [0, 1]
console.log(Enumerations.REGISTERED_PARAMETERS.channelcoarsetuning); // [0, 2]
console.log(Enumerations.REGISTERED_PARAMETERS.tuningprogram); // [0, 3]
// Use with RPN messages
function setPitchBendRange(output, channel, semitones, cents = 0) {
const rpn = Enumerations.REGISTERED_PARAMETERS.pitchbendrange;
output.sendRpnValue(rpn, [semitones, cents], { channels: channel });
}
function setFineTuning(output, channel, cents) {
const rpn = Enumerations.REGISTERED_PARAMETERS.channelfinetuning;
const msbLsb = Utilities.fromFloatToMsbLsb((cents + 100) / 200); // -100 to +100 cents
output.sendRpnValue(rpn, [msbLsb.msb, msbLsb.lsb], { channels: channel });
}System message constants for real-time and common messages.
/**
* System message constants
*/
static readonly SYSTEM_MESSAGES: {
sysex: number;
timecode: number;
songposition: number;
songselect: number;
tunerequest: number;
sysexend: number;
clock: number;
start: number;
continue: number;
stop: number;
activesensing: number;
reset: number;
};
/**
* MIDI system message constants
*/
static readonly MIDI_SYSTEM_MESSAGES: object;Usage Examples:
// System message constants
console.log(Enumerations.SYSTEM_MESSAGES.sysex); // 240 (0xF0)
console.log(Enumerations.SYSTEM_MESSAGES.clock); // 248 (0xF8)
console.log(Enumerations.SYSTEM_MESSAGES.start); // 250 (0xFA)
console.log(Enumerations.SYSTEM_MESSAGES.stop); // 252 (0xFC)
console.log(Enumerations.SYSTEM_MESSAGES.reset); // 255 (0xFF)
// System common messages
console.log(Enumerations.SYSTEM_MESSAGES.timecode); // 241 (0xF1)
console.log(Enumerations.SYSTEM_MESSAGES.songposition); // 242 (0xF2)
console.log(Enumerations.SYSTEM_MESSAGES.songselect); // 243 (0xF3)
console.log(Enumerations.SYSTEM_MESSAGES.tunerequest); // 246 (0xF6)
// Use for message filtering
function isClockMessage(message) {
return message.statusByte === Enumerations.SYSTEM_MESSAGES.clock;
}
function isTransportMessage(message) {
const transportMessages = [
Enumerations.SYSTEM_MESSAGES.start,
Enumerations.SYSTEM_MESSAGES.continue,
Enumerations.SYSTEM_MESSAGES.stop
];
return transportMessages.includes(message.statusByte);
}Array of channel event type names.
/**
* Channel event type names
*/
static readonly CHANNEL_EVENTS: string[];Usage Examples:
// Channel event names
console.log(Enumerations.CHANNEL_EVENTS);
// ["noteoff", "noteon", "keyaftertouch", "controlchange",
// "programchange", "channelaftertouch", "pitchbend"]
// Use for event handling
function setupChannelListeners(input) {
Enumerations.CHANNEL_EVENTS.forEach(eventType => {
input.addListener(eventType, (e) => {
console.log(`Received ${eventType} on channel ${e.channel}`);
});
});
}
// Check if event is a channel event
function isChannelEvent(eventType) {
return Enumerations.CHANNEL_EVENTS.includes(eventType);
}import { Enumerations, Message } from "webmidi";
function analyzeMessage(message) {
const statusByte = message.statusByte;
// Check channel messages
if (message.isChannelMessage) {
const command = message.command;
if (command === Enumerations.CHANNEL_MESSAGES.noteon) {
return "Note On";
} else if (command === Enumerations.CHANNEL_MESSAGES.controlchange) {
const ccNumber = message.dataBytes[0];
// Check for channel mode messages
if (ccNumber >= 120) {
for (const [name, value] of Object.entries(Enumerations.CHANNEL_MODE_MESSAGES)) {
if (value === ccNumber) {
return `Channel Mode: ${name}`;
}
}
}
// Regular control change
return `Control Change: CC${ccNumber}`;
}
}
// Check system messages
for (const [name, value] of Object.entries(Enumerations.SYSTEM_MESSAGES)) {
if (value === statusByte) {
return `System: ${name}`;
}
}
return "Unknown";
}function getControllerName(ccNumber) {
// Find name in control change messages
for (const [name, value] of Object.entries(Enumerations.CONTROL_CHANGE_MESSAGES)) {
if (value === ccNumber) {
return name;
}
}
return `CC${ccNumber}`;
}
function getControllerNumber(ccName) {
const normalizedName = ccName.toLowerCase().replace(/[^a-z0-9]/g, '');
for (const [name, value] of Object.entries(Enumerations.CONTROL_CHANGE_MESSAGES)) {
if (name === normalizedName) {
return value;
}
}
return undefined;
}
// Usage
console.log(getControllerName(64)); // "sustain"
console.log(getControllerName(91)); // "effects1depth" (reverb)
console.log(getControllerNumber("volume")); // undefined (need "volumecoarse")
console.log(getControllerNumber("volumecoarse")); // 7// Create a standard MIDI implementation using constants
class StandardMidiDevice {
constructor(output) {
this.output = output;
}
// Standard program change
selectProgram(channel, program) {
this.output.sendProgramChange(program, { channels: channel });
}
// Volume control
setVolume(channel, volume) {
const cc = Enumerations.CONTROL_CHANGE_MESSAGES.volumecoarse;
this.output.sendControlChange(cc, volume, { channels: channel });
}
// Pan control
setPan(channel, pan) {
const cc = Enumerations.CONTROL_CHANGE_MESSAGES.pancoarse;
this.output.sendControlChange(cc, pan, { channels: channel });
}
// Sustain pedal
setSustain(channel, enabled) {
const cc = Enumerations.CONTROL_CHANGE_MESSAGES.sustain;
const value = enabled ? 127 : 0;
this.output.sendControlChange(cc, value, { channels: channel });
}
// Emergency stop
panic() {
const allSoundOff = Enumerations.CHANNEL_MODE_MESSAGES.allsoundoff;
const allNotesOff = Enumerations.CHANNEL_MODE_MESSAGES.allnotesoff;
Enumerations.CHANNEL_NUMBERS.forEach(channel => {
this.output.sendControlChange(allSoundOff, 0, { channels: channel });
this.output.sendControlChange(allNotesOff, 0, { channels: channel });
});
}
// Reset all controllers
resetControllers(channel = "all") {
const reset = Enumerations.CHANNEL_MODE_MESSAGES.resetallcontrollers;
this.output.sendControlChange(reset, 0, { channels: channel });
}
}type ChannelMessageType = "noteoff" | "noteon" | "keyaftertouch" | "controlchange" | "programchange" | "channelaftertouch" | "pitchbend";
type SystemMessageType = "sysex" | "timecode" | "songposition" | "songselect" | "tunerequest" | "clock" | "start" | "continue" | "stop" | "activesensing" | "reset";
type ChannelModeMessageType = "allsoundoff" | "resetallcontrollers" | "localcontrol" | "allnotesoff" | "omnimodeoff" | "omnimodeon" | "monomodeon" | "polymodeon";
interface ControlChangeMessages {
[key: string]: number;
}
interface RegisteredParameters {
[key: string]: number[];
}Install with Tessl CLI
npx tessl i tessl/npm-webmidi