or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

constants.mdindex.mdmessage-forwarding.mdmessage-processing.mdmidi-input.mdmidi-output.mdnote-processing.mdutilities.mdwebmidi-interface.md

message-processing.mddocs/

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

```