0
# Message Processing
1
2
Message processing provides comprehensive functionality for parsing and understanding MIDI messages. The Message class represents parsed MIDI messages with type-specific properties and methods for easy access to message data.
3
4
## Capabilities
5
6
### Message Construction
7
8
Create Message objects from raw MIDI data.
9
10
```javascript { .api }
11
class Message {
12
/**
13
* Create a Message object from raw MIDI data
14
* @param data - MIDI message data as Uint8Array
15
*/
16
constructor(data: Uint8Array);
17
}
18
```
19
20
**Usage Examples:**
21
22
```javascript
23
import { Message } from "webmidi";
24
25
// Create from raw MIDI data
26
const noteOnData = new Uint8Array([0x90, 60, 100]); // Note on, C4, velocity 100
27
const noteOnMessage = new Message(noteOnData);
28
29
const ccData = new Uint8Array([0xB0, 64, 127]); // Control change, sustain, max value
30
const ccMessage = new Message(ccData);
31
32
// Create from array (automatically converted to Uint8Array)
33
const pitchBendData = new Uint8Array([0xE0, 0x00, 0x40]); // Pitch bend, center position
34
const pitchBendMessage = new Message(pitchBendData);
35
```
36
37
### Message Properties
38
39
Access parsed message properties and data.
40
41
```javascript { .api }
42
/**
43
* Original raw MIDI data as Uint8Array
44
*/
45
readonly rawData: Uint8Array;
46
47
/**
48
* MIDI data as regular array of numbers
49
*/
50
readonly data: number[];
51
52
/**
53
* MIDI status byte (first byte)
54
*/
55
readonly statusByte: number;
56
57
/**
58
* Raw data bytes as Uint8Array (excluding status byte)
59
*/
60
readonly rawDataBytes: Uint8Array;
61
62
/**
63
* Data bytes as regular array (excluding status byte)
64
*/
65
readonly dataBytes: number[];
66
67
/**
68
* Whether this is a channel message (0x80-0xEF)
69
*/
70
readonly isChannelMessage: boolean;
71
72
/**
73
* Whether this is a system message (0xF0-0xFF)
74
*/
75
readonly isSystemMessage: boolean;
76
77
/**
78
* MIDI command number (upper 4 bits of status byte)
79
*/
80
readonly command: number;
81
82
/**
83
* MIDI channel (1-16) for channel messages, undefined for system messages
84
*/
85
readonly channel?: number;
86
87
/**
88
* Manufacturer ID for SysEx messages (first data byte(s))
89
*/
90
readonly manufacturerId?: number | number[];
91
92
/**
93
* Human-readable message type string
94
*/
95
readonly type: string;
96
```
97
98
**Usage Examples:**
99
100
```javascript
101
const noteOnData = new Uint8Array([0x90, 60, 100]);
102
const message = new Message(noteOnData);
103
104
console.log(message.rawData); // Uint8Array([144, 60, 100])
105
console.log(message.data); // [144, 60, 100]
106
console.log(message.statusByte); // 144 (0x90)
107
console.log(message.dataBytes); // [60, 100]
108
console.log(message.isChannelMessage); // true
109
console.log(message.isSystemMessage); // false
110
console.log(message.command); // 9 (note on)
111
console.log(message.channel); // 1
112
console.log(message.type); // "noteon"
113
```
114
115
## Message Types
116
117
### Channel Messages
118
119
Channel messages target specific MIDI channels (1-16).
120
121
```javascript
122
// Note On (0x90-0x9F)
123
const noteOn = new Message(new Uint8Array([0x90, 60, 100]));
124
console.log(noteOn.type); // "noteon"
125
console.log(noteOn.channel); // 1
126
console.log(noteOn.dataBytes); // [60, 100] (note, velocity)
127
128
// Note Off (0x80-0x8F)
129
const noteOff = new Message(new Uint8Array([0x80, 60, 64]));
130
console.log(noteOff.type); // "noteoff"
131
console.log(noteOff.channel); // 1
132
console.log(noteOff.dataBytes); // [60, 64] (note, velocity)
133
134
// Control Change (0xB0-0xBF)
135
const controlChange = new Message(new Uint8Array([0xB0, 64, 127]));
136
console.log(controlChange.type); // "controlchange"
137
console.log(controlChange.channel); // 1
138
console.log(controlChange.dataBytes); // [64, 127] (controller, value)
139
140
// Program Change (0xC0-0xCF)
141
const programChange = new Message(new Uint8Array([0xC0, 25]));
142
console.log(programChange.type); // "programchange"
143
console.log(programChange.channel); // 1
144
console.log(programChange.dataBytes); // [25] (program)
145
146
// Pitch Bend (0xE0-0xEF)
147
const pitchBend = new Message(new Uint8Array([0xE0, 0x00, 0x40]));
148
console.log(pitchBend.type); // "pitchbend"
149
console.log(pitchBend.channel); // 1
150
console.log(pitchBend.dataBytes); // [0, 64] (LSB, MSB)
151
152
// Channel Aftertouch (0xD0-0xDF)
153
const channelAftertouch = new Message(new Uint8Array([0xD0, 80]));
154
console.log(channelAftertouch.type); // "channelaftertouch"
155
console.log(channelAftertouch.channel); // 1
156
console.log(channelAftertouch.dataBytes); // [80] (pressure)
157
158
// Key Aftertouch (0xA0-0xAF)
159
const keyAftertouch = new Message(new Uint8Array([0xA0, 60, 80]));
160
console.log(keyAftertouch.type); // "keyaftertouch"
161
console.log(keyAftertouch.channel); // 1
162
console.log(keyAftertouch.dataBytes); // [60, 80] (note, pressure)
163
```
164
165
### System Messages
166
167
System messages are global and don't target specific channels.
168
169
```javascript
170
// System Exclusive (0xF0)
171
const sysexData = new Uint8Array([0xF0, 0x41, 0x10, 0x16, 0x12, 0xF7]);
172
const sysex = new Message(sysexData);
173
console.log(sysex.type); // "sysex"
174
console.log(sysex.channel); // undefined
175
console.log(sysex.manufacturerId); // 65 (0x41 = Roland)
176
console.log(sysex.isSystemMessage); // true
177
178
// MIDI Time Code Quarter Frame (0xF1)
179
const mtcQuarterFrame = new Message(new Uint8Array([0xF1, 0x20]));
180
console.log(mtcQuarterFrame.type); // "timecode"
181
182
// Song Position Pointer (0xF2)
183
const songPosition = new Message(new Uint8Array([0xF2, 0x00, 0x10]));
184
console.log(songPosition.type); // "songposition"
185
186
// Song Select (0xF3)
187
const songSelect = new Message(new Uint8Array([0xF3, 0x05]));
188
console.log(songSelect.type); // "songselect"
189
190
// Tune Request (0xF6)
191
const tuneRequest = new Message(new Uint8Array([0xF6]));
192
console.log(tuneRequest.type); // "tunerequest"
193
194
// MIDI Clock (0xF8)
195
const clock = new Message(new Uint8Array([0xF8]));
196
console.log(clock.type); // "clock"
197
198
// Start (0xFA)
199
const start = new Message(new Uint8Array([0xFA]));
200
console.log(start.type); // "start"
201
202
// Continue (0xFB)
203
const continue_ = new Message(new Uint8Array([0xFB]));
204
console.log(continue_.type); // "continue"
205
206
// Stop (0xFC)
207
const stop = new Message(new Uint8Array([0xFC]));
208
console.log(stop.type); // "stop"
209
210
// Active Sensing (0xFE)
211
const activeSensing = new Message(new Uint8Array([0xFE]));
212
console.log(activeSensing.type); // "activesensing"
213
214
// System Reset (0xFF)
215
const systemReset = new Message(new Uint8Array([0xFF]));
216
console.log(systemReset.type); // "reset"
217
```
218
219
## Message Analysis
220
221
### Channel Message Analysis
222
223
```javascript
224
function analyzeChannelMessage(message) {
225
if (!message.isChannelMessage) {
226
return "Not a channel message";
227
}
228
229
const analysis = {
230
type: message.type,
231
channel: message.channel,
232
command: message.command
233
};
234
235
switch (message.command) {
236
case 8: // Note Off
237
case 9: // Note On
238
analysis.note = message.dataBytes[0];
239
analysis.velocity = message.dataBytes[1];
240
analysis.noteName = midiNoteToName(analysis.note);
241
break;
242
243
case 11: // Control Change
244
analysis.controller = message.dataBytes[0];
245
analysis.value = message.dataBytes[1];
246
analysis.controllerName = getControllerName(analysis.controller);
247
break;
248
249
case 12: // Program Change
250
analysis.program = message.dataBytes[0];
251
break;
252
253
case 14: // Pitch Bend
254
analysis.lsb = message.dataBytes[0];
255
analysis.msb = message.dataBytes[1];
256
analysis.value = (analysis.msb << 7) | analysis.lsb;
257
analysis.normalizedValue = (analysis.value - 8192) / 8192; // -1 to 1
258
break;
259
}
260
261
return analysis;
262
}
263
264
// Helper functions
265
function midiNoteToName(noteNumber) {
266
const noteNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
267
const octave = Math.floor(noteNumber / 12) - 2;
268
const note = noteNames[noteNumber % 12];
269
return note + octave;
270
}
271
272
function getControllerName(ccNumber) {
273
const ccNames = {
274
0: "Bank Select MSB",
275
1: "Modulation",
276
7: "Volume",
277
10: "Pan",
278
11: "Expression",
279
64: "Sustain",
280
65: "Portamento",
281
91: "Reverb",
282
93: "Chorus"
283
};
284
return ccNames[ccNumber] || `CC ${ccNumber}`;
285
}
286
```
287
288
### SysEx Message Analysis
289
290
```javascript
291
function analyzeSysexMessage(message) {
292
if (message.type !== "sysex") {
293
return "Not a SysEx message";
294
}
295
296
const data = message.dataBytes;
297
const manufacturerId = data[0];
298
299
// Standard manufacturer IDs
300
const manufacturers = {
301
0x41: "Roland",
302
0x42: "Korg",
303
0x43: "Yamaha",
304
0x47: "Akai",
305
0x40: "Kawai"
306
};
307
308
return {
309
type: "sysex",
310
manufacturerId: manufacturerId,
311
manufacturer: manufacturers[manufacturerId] || "Unknown",
312
dataLength: data.length - 1, // Exclude manufacturer ID
313
data: data.slice(1) // Exclude manufacturer ID
314
};
315
}
316
```
317
318
## Working with Input Messages
319
320
### Processing Incoming Messages
321
322
```javascript
323
import { WebMidi } from "webmidi";
324
325
await WebMidi.enable();
326
const input = WebMidi.inputs[0];
327
328
// Raw message listener
329
input.addListener("midimessage", (e) => {
330
const message = e.message; // Message object
331
332
console.log("Received:", message.type);
333
console.log("Channel:", message.channel);
334
console.log("Data:", message.dataBytes);
335
336
// Process specific message types
337
switch (message.type) {
338
case "noteon":
339
handleNoteOn(message);
340
break;
341
case "controlchange":
342
handleControlChange(message);
343
break;
344
case "sysex":
345
handleSysex(message);
346
break;
347
}
348
});
349
350
function handleNoteOn(message) {
351
const [note, velocity] = message.dataBytes;
352
console.log(`Note ${note} played with velocity ${velocity} on channel ${message.channel}`);
353
}
354
355
function handleControlChange(message) {
356
const [controller, value] = message.dataBytes;
357
console.log(`CC ${controller} = ${value} on channel ${message.channel}`);
358
}
359
360
function handleSysex(message) {
361
const analysis = analyzeSysexMessage(message);
362
console.log(`SysEx from ${analysis.manufacturer}: ${analysis.dataLength} bytes`);
363
}
364
```
365
366
### Message Filtering
367
368
```javascript
369
// Filter messages by type
370
input.addListener("midimessage", (e) => {
371
const message = e.message;
372
373
// Only process note messages
374
if (message.type === "noteon" || message.type === "noteoff") {
375
processNoteMessage(message);
376
}
377
});
378
379
// Filter by channel
380
input.addListener("midimessage", (e) => {
381
const message = e.message;
382
383
// Only process messages from channel 1
384
if (message.channel === 1) {
385
processChannelOneMessage(message);
386
}
387
});
388
389
// Filter by data values
390
input.addListener("midimessage", (e) => {
391
const message = e.message;
392
393
// Only process high-velocity notes
394
if (message.type === "noteon" && message.dataBytes[1] > 100) {
395
processHighVelocityNote(message);
396
}
397
});
398
```
399
400
## Message Creation
401
402
### Creating Messages for Sending
403
404
```javascript
405
// Note: Usually you don't need to create Message objects manually
406
// The Output methods handle this automatically
407
408
// But if you need to create raw messages:
409
function createNoteOnMessage(channel, note, velocity) {
410
const statusByte = 0x90 + (channel - 1); // Note on + channel
411
const data = new Uint8Array([statusByte, note, velocity]);
412
return new Message(data);
413
}
414
415
function createControlChangeMessage(channel, controller, value) {
416
const statusByte = 0xB0 + (channel - 1); // Control change + channel
417
const data = new Uint8Array([statusByte, controller, value]);
418
return new Message(data);
419
}
420
421
// Usage
422
const noteOnMsg = createNoteOnMessage(1, 60, 100); // Channel 1, C4, velocity 100
423
const ccMsg = createControlChangeMessage(1, 64, 127); // Channel 1, sustain on
424
```
425
426
## Types
427
428
```javascript { .api }
429
type MessageType =
430
| "noteon" | "noteoff" | "keyaftertouch" | "controlchange"
431
| "programchange" | "channelaftertouch" | "pitchbend"
432
| "sysex" | "timecode" | "songposition" | "songselect"
433
| "tunerequest" | "clock" | "start" | "continue" | "stop"
434
| "activesensing" | "reset";
435
436
interface MessageAnalysis {
437
type: MessageType;
438
channel?: number;
439
command?: number;
440
note?: number;
441
velocity?: number;
442
controller?: number;
443
value?: number;
444
program?: number;
445
[key: string]: any;
446
}
447
```