CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-webmidi

JavaScript library for MIDI communication that simplifies sending and receiving MIDI messages between browsers/Node.js and MIDI instruments

Pending
Overview
Eval results
Files

midi-input.mddocs/

MIDI Input

MIDI Input handling provides comprehensive functionality for receiving and processing MIDI messages from devices. Input objects represent physical or virtual MIDI input ports, while InputChannel objects provide channel-specific message handling.

Capabilities

Input Port Management

Access and manage MIDI input ports.

class Input extends EventEmitter {
  /**
   * Array of 16 InputChannel objects (channels 1-16)
   */
  readonly channels: InputChannel[];
  
  /**
   * Input port name as reported by the system
   */
  readonly name: string;
  
  /**
   * Unique identifier for this input port
   */
  readonly id: string;
  
  /**
   * Connection state: "closed", "open", or "pending"
   */
  readonly connection: string;
  
  /**
   * Port state: "connected" or "disconnected" 
   */
  readonly state: string;
  
  /**
   * Device manufacturer name (if available)
   */
  readonly manufacturer: string;
  
  /**
   * Port type (always "input")
   */
  readonly type: string;
  
  /**
   * Octave offset for this input (-10 to 10)
   */
  readonly octaveOffset: number;
}

Event Listening

Add and remove event listeners for MIDI messages.

/**
 * Add event listener for MIDI messages
 * @param event - Event type (e.g., "noteon", "noteoff", "controlchange")
 * @param listener - Event handler function
 * @param options - Listener options
 * @param options.channels - Channel filter (number, array, or "all")
 * @param options.data1 - Filter by first data byte
 * @param options.data2 - Filter by second data byte
 * @returns Input instance for chaining
 */
addListener(event: string, listener: Function, options?: {
  channels?: number | number[] | "all";
  data1?: number | number[];
  data2?: number | number[];
}): Input;

/**
 * Add one-time event listener
 * @param event - Event type
 * @param listener - Event handler function  
 * @param options - Listener options
 * @returns Input instance for chaining
 */
addOneTimeListener(event: string, listener: Function, options?: object): Input;

/**
 * Shorthand for addListener with channel specification
 * @param event - Event type
 * @param channel - Channel number (1-16) or "all"
 * @param listener - Event handler function
 * @param options - Additional options
 * @returns Input instance for chaining
 */
on(event: string, channel: number | "all", listener: Function, options?: object): Input;

/**
 * Check if listener exists
 * @param event - Event type
 * @param listener - Event handler function
 * @param options - Listener options to match
 * @returns True if listener exists
 */
hasListener(event: string, listener: Function, options?: object): boolean;

/**
 * Remove event listener
 * @param event - Event type
 * @param listener - Event handler function
 * @param options - Listener options to match
 * @returns Input instance for chaining
 */
removeListener(event: string, listener: Function, options?: object): Input;

Usage Examples:

import { WebMidi } from "webmidi";

await WebMidi.enable();
const input = WebMidi.inputs[0];

// Listen for note on/off messages
input.addListener("noteon", (e) => {
  console.log("Note on:", e.note.name + e.note.octave, "velocity:", e.velocity);
});

input.addListener("noteoff", (e) => {
  console.log("Note off:", e.note.name + e.note.octave);
});

// Listen only on specific channel
input.addListener("controlchange", (e) => {
  console.log("CC:", e.controller.number, "value:", e.value);
}, { channels: 1 });

// Listen for pitch bend on multiple channels
input.addListener("pitchbend", (e) => {
  console.log("Pitch bend on channel", e.channel, "value:", e.value);
}, { channels: [1, 2, 3] });

// One-time listener
input.addOneTimeListener("programchange", (e) => {
  console.log("Program changed to:", e.value);
});

Message Forwarding

Forward MIDI messages to output ports.

/**
 * Add message forwarder to output port
 * @param output - Output port to forward messages to
 * @param options - Forwarding options
 * @param options.channels - Channel filter for forwarding
 * @param options.types - Message types to forward
 * @returns Forwarder instance
 */
addForwarder(output: Output, options?: {
  channels?: number | number[] | "all";
  types?: string | string[];
}): Forwarder;

/**
 * Remove message forwarder
 * @param forwarder - Forwarder instance to remove
 */
removeForwarder(forwarder: Forwarder): void;

/**
 * Check if forwarder exists
 * @param forwarder - Forwarder instance to check
 * @returns True if forwarder exists
 */
hasForwarder(forwarder: Forwarder): boolean;

Usage Examples:

const input = WebMidi.getInputByName("My Keyboard");
const output = WebMidi.getOutputByName("My Synth");

// Forward all messages
const forwarder = input.addForwarder(output);

// Forward only note messages from channels 1-4
const noteForwarder = input.addForwarder(output, {
  channels: [1, 2, 3, 4],
  types: ["noteon", "noteoff"]
});

// Remove forwarder
input.removeForwarder(forwarder);

InputChannel

Channel-specific input handling for targeted MIDI communication.

class InputChannel extends EventEmitter {
  /**
   * Channel number (1-16)
   */
  readonly number: number;
  
  /**
   * Parent Input object
   */
  readonly input: Input;
  
  /**
   * Octave offset for this channel
   */
  readonly octaveOffset: number;
  
  /**
   * Whether NRPN events are enabled for this channel
   */
  readonly nrpnEventsEnabled: boolean;
}

Channel-Specific Methods

/**
 * Get current state of a specific note
 * @param note - Note to check (name, number, or Note object)
 * @returns Note state object with velocity and timestamp
 */
getNoteState(note: string | number | Note): {
  velocity: number;
  timestamp: number;
  rawVelocity: number;
} | false;

/**
 * Get control change name by number
 * @param number - CC number (0-127)
 * @returns Control change name
 */
getCcNameByNumber(number: number): string;

/**
 * Get channel mode name by number
 * @param number - Channel mode number
 * @returns Channel mode name
 */
getChannelModeByNumber(number: number): string;

/**
 * Destroy the input channel (cleanup)
 */
destroy(): void;

Usage Examples:

const input = WebMidi.inputs[0];
const channel1 = input.channels[0]; // Channel 1 (index 0)

// Check if a note is currently pressed
const noteState = channel1.getNoteState("C4");
if (noteState) {
  console.log("C4 is pressed with velocity:", noteState.velocity);
}

// Listen to channel-specific events
channel1.addListener("noteon", (e) => {
  console.log("Note on channel 1:", e.note.identifier);
});

// Get control change name
const ccName = channel1.getCcNameByNumber(64); // "sustain"

Common MIDI Events

Note Events

// Note on event
input.addListener("noteon", (e) => {
  console.log("Note:", e.note.name);
  console.log("Octave:", e.note.octave);
  console.log("Velocity:", e.velocity); // 0-1
  console.log("Raw velocity:", e.rawVelocity); // 0-127
  console.log("Channel:", e.channel); // 1-16
});

// Note off event  
input.addListener("noteoff", (e) => {
  console.log("Note off:", e.note.identifier);
  console.log("Release velocity:", e.velocity);
});

Control Change Events

// Control change
input.addListener("controlchange", (e) => {
  console.log("Controller:", e.controller.name);
  console.log("Number:", e.controller.number);
  console.log("Value:", e.value); // 0-1
  console.log("Raw value:", e.rawValue); // 0-127
});

// Specific control changes
input.addListener("controlchange-sustain", (e) => {
  console.log("Sustain pedal:", e.value > 0.5 ? "pressed" : "released");
});

Other Common Events

// Pitch bend
input.addListener("pitchbend", (e) => {
  console.log("Pitch bend:", e.value); // -1 to 1
});

// Program change
input.addListener("programchange", (e) => {
  console.log("Program:", e.value); // 1-128
});

// Channel aftertouch
input.addListener("channelaftertouch", (e) => {
  console.log("Channel pressure:", e.value);
});

// Key aftertouch (polyphonic)
input.addListener("keyaftertouch", (e) => {
  console.log("Key pressure:", e.note.identifier, e.value);
});

Types

interface NoteEvent {
  note: Note;
  velocity: number;
  rawVelocity: number;
  channel: number;
  timestamp: number;
  target: Input | InputChannel;
}

interface ControlChangeEvent {
  controller: {
    name: string;
    number: number;
  };
  value: number;
  rawValue: number;
  channel: number;
  timestamp: number;
  target: Input | InputChannel;
}

interface PitchBendEvent {
  value: number;
  rawValue: number;
  channel: number;
  timestamp: number;
  target: Input | InputChannel;
}

interface ListenerOptions {
  channels?: number | number[] | "all";
  data1?: number | number[];
  data2?: number | number[];
}

Install with Tessl CLI

npx tessl i tessl/npm-webmidi

docs

constants.md

index.md

message-forwarding.md

message-processing.md

midi-input.md

midi-output.md

note-processing.md

utilities.md

webmidi-interface.md

tile.json