1/** 2 * WEBMIDI.js v3.1.16 3 * A JavaScript library to kickstart your MIDI projects 4 * https://webmidijs.org 5 * Build generated on March 31st, 2026. 6 * 7 * © Copyright 2015-2026, Jean-Philippe Côté. 8 * 9 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 10 * in compliance with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software distributed under the License 15 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 16 * or implied. See the License for the specific language governing permissions and limitations under 17 * the License. 18 */ 19 20/* Version: 3.1.16 - March 31, 2026 09:40:58
vendor: 201,121 bytes, lines 20-4850
20 */ 21(function (exports) { 22 'use strict'; 23 24 /** 25 * The `EventEmitter` class provides methods to implement the _observable_ design pattern. This 26 * pattern allows one to _register_ a function to execute when a specific event is _emitted_ by the 27 * emitter. 28 * 29 * It is intended to be an abstract class meant to be extended by (or mixed into) other objects. 30 */ 31 class EventEmitter { 32 /** 33 * Creates a new `EventEmitter`object. 34 * 35 * @param {boolean} [eventsSuspended=false] Whether the `EventEmitter` is initially in a suspended 36 * state (i.e. not executing callbacks). 37 */ 38 constructor(eventsSuspended = false) { 39 /** 40 * An object containing a property for each event with at least one registered listener. Each 41 * event property contains an array of all the [`Listener`]{@link Listener} objects registered 42 * for the event. 43 * 44 * @type {Object} 45 * @readonly 46 */ 47 this.eventMap = {}; 48 49 /** 50 * Whether or not the execution of callbacks is currently suspended for this emitter. 51 * 52 * @type {boolean} 53 */ 54 this.eventsSuspended = eventsSuspended == true ? true : false; 55 } 56 57 /** 58 * The callback function is executed when the associated event is triggered via [`emit()`](#emit). 59 * The [`emit()`](#emit) method relays all additional arguments it received to the callback 60 * functions. Since [`emit()`](#emit) can be passed a variable number of arguments, it is up to 61 * the developer to make sure the arguments match those of the associated callback. In addition, 62 * the callback also separately receives all the arguments present in the listener's 63 * [`arguments`](Listener#arguments) property. This makes it easy to pass data from where the 64 * listener is added to where the listener is executed. 65 * 66 * @callback EventEmitter~callback 67 * @param {...*} [args] A variable number of arguments matching the ones (if any) that were passed 68 * to the [`emit()`](#emit) method (except, the first one) followed by the arguments found in the 69 * listener's [`arguments`](Listener#arguments) array. 70 */ 71 72 /** 73 * Adds a listener for the specified event. It returns the [`Listener`]{@link Listener} object 74 * that was created and attached to the event. 75 * 76 * To attach a global listener that will be triggered for any events, use 77 * [`EventEmitter.ANY_EVENT`]{@link #ANY_EVENT} as the first parameter. Note that a global 78 * listener will also be triggered by non-registered events. 79 * 80 * @param {string|Symbol} event The event to listen to. 81 * @param {EventEmitter~callback} callback The callback function to execute when the event occurs. 82 * @param {Object} [options={}] 83 * @param {Object} [options.context=this] The value of `this` in the callback function. 84 * @param {boolean} [options.prepend=false] Whether the listener should be added at the beginning 85 * of the listeners array and thus executed first. 86 * @param {number} [options.duration=Infinity] The number of milliseconds before the listener 87 * automatically expires. 88 * @param {number} [options.remaining=Infinity] The number of times after which the callback 89 * should automatically be removed. 90 * @param {array} [options.arguments] An array of arguments which will be passed separately to the 91 * callback function. This array is stored in the [`arguments`]{@link Listener#arguments} 92 * property of the [`Listener`]{@link Listener} object and can be retrieved or modified as 93 * desired. 94 * 95 * @returns {Listener} The newly created [`Listener`]{@link Listener} object. 96 * 97 * @throws {TypeError} The `event` parameter must be a string or 98 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT}. 99 * @throws {TypeError} The `callback` parameter must be a function. 100 */ 101 addListener(event, callback, options = {}) { 102 if (typeof event === "string" && event.length < 1 || event instanceof String && event.length < 1 || typeof event !== "string" && !(event instanceof String) && event !== EventEmitter.ANY_EVENT) { 103 throw new TypeError("The 'event' parameter must be a string or EventEmitter.ANY_EVENT."); 104 } 105 if (typeof callback !== "function") throw new TypeError("The callback must be a function."); 106 const listener = new Listener(event, this, callback, options); 107 if (!this.eventMap[event]) this.eventMap[event] = []; 108 if (options.prepend) { 109 this.eventMap[event].unshift(listener); 110 } else { 111 this.eventMap[event].push(listener); 112 } 113 return listener; 114 } 115 116 /** 117 * Adds a one-time listener for the specified event. The listener will be executed once and then 118 * destroyed. It returns the [`Listener`]{@link Listener} object that was created and attached 119 * to the event. 120 * 121 * To attach a global listener that will be triggered for any events, use 122 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT} as the first parameter. Note that a 123 * global listener will also be triggered by non-registered events. 124 * 125 * @param {string|Symbol} event The event to listen to 126 * @param {EventEmitter~callback} callback The callback function to execute when the event occurs 127 * @param {Object} [options={}] 128 * @param {Object} [options.context=this] The context to invoke the callback function in. 129 * @param {boolean} [options.prepend=false] Whether the listener should be added at the beginning 130 * of the listeners array and thus executed first. 131 * @param {number} [options.duration=Infinity] The number of milliseconds before the listener 132 * automatically expires. 133 * @param {array} [options.arguments] An array of arguments which will be passed separately to the 134 * callback function. This array is stored in the [`arguments`]{@link Listener#arguments} 135 * property of the [`Listener`]{@link Listener} object and can be retrieved or modified as 136 * desired. 137 * 138 * @returns {Listener} The newly created [`Listener`]{@link Listener} object. 139 * 140 * @throws {TypeError} The `event` parameter must be a string or 141 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT}. 142 * @throws {TypeError} The `callback` parameter must be a function. 143 */ 144 addOneTimeListener(event, callback, options = {}) { 145 options.remaining = 1; 146 this.addListener(event, callback, options); 147 } 148 149 /** 150 * Identifier to use when adding or removing a listener that should be triggered when any events 151 * occur. 152 * 153 * @type {Symbol} 154 */ 155 static get ANY_EVENT() { 156 return Symbol.for("Any event"); 157 } 158 159 /** 160 * Returns `true` if the specified event has at least one registered listener. If no event is 161 * specified, the method returns `true` if any event has at least one listener registered (this 162 * includes global listeners registered to 163 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT}). 164 * 165 * Note: to specifically check for global listeners added with 166 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT}, use 167 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT} as the parameter. 168 * 169 * @param {string|Symbol} [event=(any event)] The event to check 170 * @param {function|Listener} [callback=(any callback)] The actual function that was added to the 171 * event or the {@link Listener} object returned by `addListener()`. 172 * @returns {boolean} 173 */ 174 hasListener(event, callback) { 175 if (event === undefined) { 176 // Check for ANY_EVENT 177 if (this.eventMap[EventEmitter.ANY_EVENT] && this.eventMap[EventEmitter.ANY_EVENT].length > 0) { 178 return true; 179 } 180 181 // Check for any regular events 182 return Object.entries(this.eventMap).some(([, value]) => { 183 return value.length > 0; 184 }); 185 } else { 186 if (this.eventMap[event] && this.eventMap[event].length > 0) { 187 if (callback instanceof Listener) { 188 let result = this.eventMap[event].filter(listener => listener === callback); 189 return result.length > 0; 190 } else if (typeof callback === "function") { 191 let result = this.eventMap[event].filter(listener => listener.callback === callback); 192 return result.length > 0; 193 } else if (callback != undefined) { 194 return false; 195 } 196 return true; 197 } else { 198 return false; 199 } 200 } 201 } 202 203 /** 204 * An array of all the unique event names for which the emitter has at least one registered 205 * listener. 206 * 207 * Note: this excludes global events registered with 208 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT} because they are not tied to a 209 * specific event. 210 * 211 * @type {string[]} 212 * @readonly 213 */ 214 get eventNames() { 215 return Object.keys(this.eventMap); 216 } 217 218 /** 219 * Returns an array of all the [`Listener`]{@link Listener} objects that have been registered for 220 * a specific event. 221 * 222 * Please note that global events (those added with 223 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT}) are not returned for "regular" 224 * events. To get the list of global listeners, specifically use 225 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT} as the parameter. 226 * 227 * @param {string|Symbol} event The event to get listeners for. 228 * @returns {Listener[]} An array of [`Listener`]{@link Listener} objects. 229 */ 230 getListeners(event) { 231 return this.eventMap[event] || []; 232 } 233 234 /** 235 * Suspends execution of all callbacks functions registered for the specified event type. 236 * 237 * You can suspend execution of callbacks registered with 238 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT} by passing 239 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT} to `suspendEvent()`. Beware that this 240 * will not suspend all callbacks but only those registered with 241 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT}. While this may seem counter-intuitive 242 * at first glance, it allows the selective suspension of global listeners while leaving other 243 * listeners alone. If you truly want to suspends all callbacks for a specific 244 * [`EventEmitter`]{@link EventEmitter}, simply set its `eventsSuspended` property to `true`. 245 * 246 * @param {string|Symbol} event The event name (or `EventEmitter.ANY_EVENT`) for which to suspend 247 * execution of all callback functions. 248 */ 249 suspendEvent(event) { 250 this.getListeners(event).forEach(listener => { 251 listener.suspended = true; 252 }); 253 } 254 255 /** 256 * Resumes execution of all suspended callback functions registered for the specified event type. 257 * 258 * You can resume execution of callbacks registered with 259 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT} by passing 260 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT} to `unsuspendEvent()`. Beware that 261 * this will not resume all callbacks but only those registered with 262 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT}. While this may seem 263 * counter-intuitive, it allows the selective unsuspension of global listeners while leaving other 264 * callbacks alone. 265 * 266 * @param {string|Symbol} event The event name (or `EventEmitter.ANY_EVENT`) for which to resume 267 * execution of all callback functions. 268 */ 269 unsuspendEvent(event) { 270 this.getListeners(event).forEach(listener => { 271 listener.suspended = false; 272 }); 273 } 274 275 /** 276 * Returns the number of listeners registered for a specific event. 277 * 278 * Please note that global events (those added with 279 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT}) do not count towards the remaining 280 * number for a "regular" event. To get the number of global listeners, specifically use 281 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT} as the parameter. 282 * 283 * @param {string|Symbol} event The event which is usually a string but can also be the special 284 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT} symbol. 285 * @returns {number} An integer representing the number of listeners registered for the specified 286 * event. 287 */ 288 getListenerCount(event) { 289 return this.getListeners(event).length; 290 } 291 292 /** 293 * Executes the callback function of all the [`Listener`]{@link Listener} objects registered for 294 * a given event. The callback functions are passed the additional arguments passed to `emit()` 295 * (if any) followed by the arguments present in the [`arguments`](Listener#arguments) property of 296 * the [`Listener`](Listener) object (if any). 297 * 298 * If the [`eventsSuspended`]{@link #eventsSuspended} property is `true` or the 299 * [`Listener.suspended`]{@link Listener#suspended} property is `true`, the callback functions 300 * will not be executed. 301 * 302 * This function returns an array containing the return values of each of the callbacks. 303 * 304 * It should be noted that the regular listeners are triggered first followed by the global 305 * listeners (those added with [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT}). 306 * 307 * @param {string} event The event 308 * @param {...*} args Arbitrary number of arguments to pass along to the callback functions 309 * 310 * @returns {Array} An array containing the return value of each of the executed listener 311 * functions. 312 * 313 * @throws {TypeError} The `event` parameter must be a string. 314 */ 315 emit(event, ...args) { 316 if (typeof event !== "string" && !(event instanceof String)) { 317 throw new TypeError("The 'event' parameter must be a string."); 318 } 319 if (this.eventsSuspended) return; 320 321 // We collect return values from all listeners here 322 let results = []; 323 324 // We must make sure that we do not have undefined otherwise concat() will add an undefined 325 // entry in the array. 326 let listeners = this.eventMap[EventEmitter.ANY_EVENT] || []; 327 if (this.eventMap[event]) listeners = listeners.concat(this.eventMap[event]); 328 listeners.forEach(listener => { 329 // This is the per-listener suspension check 330 if (listener.suspended) return; 331 let params = [...args]; 332 if (Array.isArray(listener.arguments)) params = params.concat(listener.arguments); 333 if (listener.remaining > 0) { 334 results.push(listener.callback.apply(listener.context, params)); 335 listener.count++; 336 } 337 if (--listener.remaining < 1) listener.remove(); 338 }); 339 return results; 340 } 341 342 /** 343 * Removes all the listeners that were added to the object upon which the method is called and 344 * that match the specified criterias. If no parameters are passed, all listeners added to this 345 * object will be removed. If only the `event` parameter is passed, all listeners for that event 346 * will be removed from that object. You can remove global listeners by using 347 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT} as the first parameter. 348 * 349 * To use more granular options, you must at least define the `event`. Then, you can specify the 350 * callback to match or one or more of the additional options. 351 * 352 * @param {string} [event] The event name. 353 * @param {EventEmitter~callback} [callback] Only remove the listeners that match this exact 354 * callback function. 355 * @param {Object} [options] 356 * @param {*} [options.context] Only remove the listeners that have this exact context. 357 * @param {number} [options.remaining] Only remove the listener if it has exactly that many 358 * remaining times to be executed. 359 */ 360 removeListener(event, callback, options = {}) { 361 if (event === undefined) { 362 this.eventMap = {}; 363 return; 364 } else if (!this.eventMap[event]) { 365 return; 366 } 367 368 // Find listeners that do not match the criterias (those are the ones we will keep) 369 let listeners = this.eventMap[event].filter(listener => { 370 return callback && listener.callback !== callback || options.remaining && options.remaining !== listener.remaining || options.context && options.context !== listener.context; 371 }); 372 if (listeners.length) { 373 this.eventMap[event] = listeners; 374 } else { 375 delete this.eventMap[event]; 376 } 377 } 378 379 /** 380 * The `waitFor()` method is an async function which returns a promise. The promise is fulfilled 381 * when the specified event occurs. The event can be a regular event or 382 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT} (if you want to resolve as soon as any 383 * event is emitted). 384 * 385 * If the `duration` option is set, the promise will only be fulfilled if the event is emitted 386 * within the specified duration. If the event has not been fulfilled after the specified 387 * duration, the promise is rejected. This makes it super easy to wait for an event and timeout 388 * after a certain time if the event is not triggered. 389 * 390 * @param {string|Symbol} event The event to wait for 391 * @param {Object} [options={}] 392 * @param {number} [options.duration=Infinity] The number of milliseconds to wait before the 393 * promise is automatically rejected. 394 */ 395 async waitFor(event, options = {}) { 396 options.duration = parseInt(options.duration); 397 if (isNaN(options.duration) || options.duration <= 0) options.duration = Infinity; 398 return new Promise((resolve, reject) => { 399 let timeout; 400 let listener = this.addListener(event, () => { 401 clearTimeout(timeout); 402 resolve(); 403 }, { 404 remaining: 1 405 }); 406 if (options.duration !== Infinity) { 407 timeout = setTimeout(() => { 408 listener.remove(); 409 reject("The duration expired before the event was emitted."); 410 }, options.duration); 411 } 412 }); 413 } 414 415 /** 416 * The number of unique events that have registered listeners. 417 * 418 * Note: this excludes global events registered with 419 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT} because they are not tied to a 420 * specific event. 421 * 422 * @type {number} 423 * @readonly 424 */ 425 get eventCount() { 426 return Object.keys(this.eventMap).length; 427 } 428 } 429 430 /** 431 * The `Listener` class represents a single event listener object. Such objects keep all relevant 432 * contextual information such as the event being listened to, the object the listener was attached 433 * to, the callback function and so on. 434 * 435 */ 436 class Listener { 437 /** 438 * Creates a new `Listener` object 439 * 440 * @param {string|Symbol} event The event being listened to 441 * @param {EventEmitter} target The [`EventEmitter`]{@link EventEmitter} object that the listener 442 * is attached to. 443 * @param {EventEmitter~callback} callback The function to call when the listener is triggered 444 * @param {Object} [options={}] 445 * @param {Object} [options.context=target] The context to invoke the listener in (a.k.a. the 446 * value of `this` inside the callback function). 447 * @param {number} [options.remaining=Infinity] The remaining number of times after which the 448 * callback should automatically be removed. 449 * @param {array} [options.arguments] An array of arguments that will be passed separately to the 450 * callback function upon execution. The array is stored in the [`arguments`]{@link #arguments} 451 * property and can be retrieved or modified as desired. 452 * 453 * @throws {TypeError} The `event` parameter must be a string or 454 * [`EventEmitter.ANY_EVENT`]{@link EventEmitter#ANY_EVENT}. 455 * @throws {ReferenceError} The `target` parameter is mandatory. 456 * @throws {TypeError} The `callback` must be a function. 457 */ 458 constructor(event, target, callback, options = {}) { 459 if (typeof event !== "string" && !(event instanceof String) && event !== EventEmitter.ANY_EVENT) { 460 throw new TypeError("The 'event' parameter must be a string or EventEmitter.ANY_EVENT."); 461 } 462 if (!target) { 463 throw new ReferenceError("The 'target' parameter is mandatory."); 464 } 465 if (typeof callback !== "function") { 466 throw new TypeError("The 'callback' must be a function."); 467 } 468 469 // Convert single value argument to array 470 if (options.arguments !== undefined && !Array.isArray(options.arguments)) { 471 options.arguments = [options.arguments]; 472 } 473 474 // Define default options and merge declared options into them, 475 options = Object.assign({ 476 context: target, 477 remaining: Infinity, 478 arguments: undefined, 479 duration: Infinity 480 }, options); 481 482 // Make sure it is eventually deleted if a duration is supplied 483 if (options.duration !== Infinity) { 484 setTimeout(() => this.remove(), options.duration); 485 } 486 487 /** 488 * An array of arguments to pass to the callback function upon execution. 489 * @type {array} 490 */ 491 this.arguments = options.arguments; 492 493 /** 494 * The callback function to execute. 495 * @type {Function} 496 */ 497 this.callback = callback; 498 499 /** 500 * The context to execute the callback function in (a.k.a. the value of `this` inside the 501 * callback function) 502 * @type {Object} 503 */ 504 this.context = options.context; 505 506 /** 507 * The number of times the listener function was executed. 508 * @type {number} 509 */ 510 this.count = 0; 511 512 /** 513 * The event name. 514 * @type {string} 515 */ 516 this.event = event; 517 518 /** 519 * The remaining number of times after which the callback should automatically be removed. 520 * @type {number} 521 */ 522 this.remaining = parseInt(options.remaining) >= 1 ? parseInt(options.remaining) : Infinity; 523 524 /** 525 * Whether this listener is currently suspended or not. 526 * @type {boolean} 527 */ 528 this.suspended = false; 529 530 /** 531 * The object that the event is attached to (or that emitted the event). 532 * @type {EventEmitter} 533 */ 534 this.target = target; 535 } 536 537 /** 538 * Removes the listener from its target. 539 */ 540 remove() { 541 this.target.removeListener(this.event, this.callback, { 542 context: this.context, 543 remaining: this.remaining 544 }); 545 } 546 } 547 548 /** 549 * The `Enumerations` class contains enumerations and arrays of elements used throughout the 550 * library. All its properties are static and should be referenced using the class name. For 551 * example: `Enumerations.CHANNEL_MESSAGES`. 552 * 553 * @license Apache-2.0 554 * @since 3.0.0 555 */ 556 class Enumerations { 557 /** 558 * @enum {Object.<string, number>} 559 * @readonly 560 * @deprecated since 3.1 (use Enumerations.CHANNEL_MESSAGES instead) 561 * @private 562 * @static 563 */ 564 static get MIDI_CHANNEL_MESSAGES() { 565 if (this.validation) { 566 console.warn("The MIDI_CHANNEL_MESSAGES enum has been deprecated. Use the " + "Enumerations.CHANNEL_MESSAGES enum instead."); 567 } 568 return Enumerations.CHANNEL_MESSAGES; 569 } 570 571 /** 572 * Enumeration of all MIDI channel message names and their associated 4-bit numerical value: 573 * 574 * | Message Name | Hexadecimal | Decimal | 575 * |---------------------|-------------|---------| 576 * | `noteoff` | 0x8 | 8 | 577 * | `noteon` | 0x9 | 9 | 578 * | `keyaftertouch` | 0xA | 10 | 579 * | `controlchange` | 0xB | 11 | 580 * | `programchange` | 0xC | 12 | 581 * | `channelaftertouch` | 0xD | 13 | 582 * | `pitchbend` | 0xE | 14 | 583 * 584 * @enum {Object.<string, number>} 585 * @readonly 586 * @since 3.1 587 * @static 588 */ 589 static get CHANNEL_MESSAGES() { 590 return { 591 noteoff: 0x8, 592 // 8 593 noteon: 0x9, 594 // 9 595 keyaftertouch: 0xA, 596 // 10 597 controlchange: 0xB, 598 // 11 599 programchange: 0xC, 600 // 12 601 channelaftertouch: 0xD, 602 // 13 603 pitchbend: 0xE // 14 604 }; 605 } 606 607 /** 608 * A simple array of the 16 valid MIDI channel numbers (`1` to `16`): 609 * 610 * @type {number[]} 611 * @readonly 612 * @since 3.1 613 * @static 614 */ 615 static get CHANNEL_NUMBERS() { 616 return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; 617 } 618 619 /** 620 * @type {number[]} 621 * @readonly 622 * @deprecated since 3.1 (use Enumerations.CHANNEL_NUMBERS instead) 623 * @private 624 * @static 625 */ 626 static get MIDI_CHANNEL_NUMBERS() { 627 if (this.validation) { 628 console.warn("The MIDI_CHANNEL_NUMBERS array has been deprecated. Use the " + "Enumerations.CHANNEL_NUMBERS array instead."); 629 } 630 return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; 631 } 632 633 /** 634 * Enumeration of all MIDI channel mode message names and their associated numerical value: 635 * 636 * 637 * | Message Name | Hexadecimal | Decimal | 638 * |-----------------------|-------------|---------| 639 * | `allsoundoff` | 0x78 | 120 | 640 * | `resetallcontrollers` | 0x79 | 121 | 641 * | `localcontrol` | 0x7A | 122 | 642 * | `allnotesoff` | 0x7B | 123 | 643 * | `omnimodeoff` | 0x7C | 124 | 644 * | `omnimodeon` | 0x7D | 125 | 645 * | `monomodeon` | 0x7E | 126 | 646 * | `polymodeon` | 0x7F | 127 | 647 * 648 * @enum {Object.<string, number>} 649 * @readonly 650 * @since 3.1 651 * @static 652 */ 653 static get CHANNEL_MODE_MESSAGES() { 654 return { 655 allsoundoff: 120, 656 resetallcontrollers: 121, 657 localcontrol: 122, 658 allnotesoff: 123, 659 omnimodeoff: 124, 660 omnimodeon: 125, 661 monomodeon: 126, 662 polymodeon: 127 663 }; 664 } 665 666 /** 667 * @enum {Object.<string, number>} 668 * @deprecated since 3.1 (use Enumerations.CHANNEL_MODE_MESSAGES instead) 669 * @private 670 * @readonly 671 * @static 672 */ 673 static get MIDI_CHANNEL_MODE_MESSAGES() { 674 if (this.validation) { 675 console.warn("The MIDI_CHANNEL_MODE_MESSAGES enum has been deprecated. Use the " + "Enumerations.CHANNEL_MODE_MESSAGES enum instead."); 676 } 677 return Enumerations.CHANNEL_MODE_MESSAGES; 678 } 679 680 /** 681 * @enum {Object.<string, number>} 682 * @readonly 683 * @static 684 * @private 685 * @deprecated since version 3.0.26 (use `CONTROL_CHANGE_MESSAGES` instead) 686 */ 687 static get MIDI_CONTROL_CHANGE_MESSAGES() { 688 if (this.validation) { 689 console.warn("The MIDI_CONTROL_CHANGE_MESSAGES enum has been deprecated. Use the " + "Enumerations.CONTROL_CHANGE_MESSAGES array instead."); 690 } 691 return { 692 bankselectcoarse: 0, 693 modulationwheelcoarse: 1, 694 breathcontrollercoarse: 2, 695 controller3: 3, 696 footcontrollercoarse: 4, 697 portamentotimecoarse: 5, 698 dataentrycoarse: 6, 699 volumecoarse: 7, 700 balancecoarse: 8, 701 controller9: 9, 702 pancoarse: 10, 703 expressioncoarse: 11, 704 effectcontrol1coarse: 12, 705 effectcontrol2coarse: 13, 706 controller14: 14, 707 controller15: 15, 708 generalpurposeslider1: 16, 709 generalpurposeslider2: 17, 710 generalpurposeslider3: 18, 711 generalpurposeslider4: 19, 712 controller20: 20, 713 controller21: 21, 714 controller22: 22, 715 controller23: 23, 716 controller24: 24, 717 controller25: 25, 718 controller26: 26, 719 controller27: 27, 720 controller28: 28, 721 controller29: 29, 722 controller30: 30, 723 controller31: 31, 724 bankselectfine: 32, 725 modulationwheelfine: 33, 726 breathcontrollerfine: 34, 727 controller35: 35, 728 footcontrollerfine: 36, 729 portamentotimefine: 37, 730 dataentryfine: 38, 731 volumefine: 39, 732 balancefine: 40, 733 controller41: 41, 734 panfine: 42, 735 expressionfine: 43, 736 effectcontrol1fine: 44, 737 effectcontrol2fine: 45, 738 controller46: 46, 739 controller47: 47, 740 controller48: 48, 741 controller49: 49, 742 controller50: 50, 743 controller51: 51, 744 controller52: 52, 745 controller53: 53, 746 controller54: 54, 747 controller55: 55, 748 controller56: 56, 749 controller57: 57, 750 controller58: 58, 751 controller59: 59, 752 controller60: 60, 753 controller61: 61, 754 controller62: 62, 755 controller63: 63, 756 holdpedal: 64, 757 portamento: 65, 758 sustenutopedal: 66, 759 softpedal: 67, 760 legatopedal: 68, 761 hold2pedal: 69, 762 soundvariation: 70, 763 resonance: 71, 764 soundreleasetime: 72, 765 soundattacktime: 73, 766 brightness: 74, 767 soundcontrol6: 75, 768 soundcontrol7: 76, 769 soundcontrol8: 77, 770 soundcontrol9: 78, 771 soundcontrol10: 79, 772 generalpurposebutton1: 80, 773 generalpurposebutton2: 81, 774 generalpurposebutton3: 82, 775 generalpurposebutton4: 83, 776 controller84: 84, 777 controller85: 85, 778 controller86: 86, 779 controller87: 87, 780 controller88: 88, 781 controller89: 89, 782 controller90: 90, 783 reverblevel: 91, 784 tremololevel: 92, 785 choruslevel: 93, 786 celestelevel: 94, 787 phaserlevel: 95, 788 databuttonincrement: 96, 789 databuttondecrement: 97, 790 nonregisteredparametercoarse: 98, 791 nonregisteredparameterfine: 99, 792 registeredparametercoarse: 100, 793 registeredparameterfine: 101, 794 controller102: 102, 795 controller103: 103, 796 controller104: 104, 797 controller105: 105, 798 controller106: 106, 799 controller107: 107, 800 controller108: 108, 801 controller109: 109, 802 controller110: 110, 803 controller111: 111, 804 controller112: 112, 805 controller113: 113, 806 controller114: 114, 807 controller115: 115, 808 controller116: 116, 809 controller117: 117, 810 controller118: 118, 811 controller119: 119, 812 allsoundoff: 120, 813 resetallcontrollers: 121, 814 localcontrol: 122, 815 allnotesoff: 123, 816 omnimodeoff: 124, 817 omnimodeon: 125, 818 monomodeon: 126, 819 polymodeon: 127 820 }; 821 } 822 823 /** 824 * An array of objects, ordered by control number, describing control change messages. Each object 825 * in the array has 3 properties with some objects having a fourth one (`position`) : 826 * 827 * * `number`: MIDI control number (0-127); 828 * * `name`: name of emitted event (eg: `bankselectcoarse`, `choruslevel`, etc) that can be 829 * listened to; 830 * * `description`: user-friendly description of the controller's purpose; 831 * * `position` (optional): whether this controller's value should be considered an `msb` or 832 * `lsb` 833 * 834 * Not all controllers have a predefined function. For those that don't, `name` is the word 835 * "controller" followed by the number (e.g. `controller112`). 836 * 837 * | Event name | Control Number | 838 * |--------------------------------|----------------| 839 * | `bankselectcoarse` | 0 | 840 * | `modulationwheelcoarse` | 1 | 841 * | `breathcontrollercoarse` | 2 | 842 * | `controller3` | 3 | 843 * | `footcontrollercoarse` | 4 | 844 * | `portamentotimecoarse` | 5 | 845 * | `dataentrycoarse` | 6 | 846 * | `volumecoarse` | 7 | 847 * | `balancecoarse` | 8 | 848 * | `controller9` | 9 | 849 * | `pancoarse` | 10 | 850 * | `expressioncoarse` | 11 | 851 * | `effectcontrol1coarse` | 12 | 852 * | `effectcontrol2coarse` | 13 | 853 * | `controller14` | 14 | 854 * | `controller15` | 15 | 855 * | `generalpurposecontroller1` | 16 | 856 * | `generalpurposecontroller2` | 17 | 857 * | `generalpurposecontroller3` | 18 | 858 * | `generalpurposecontroller4` | 19 | 859 * | `controller20` | 20 | 860 * | `controller21` | 21 | 861 * | `controller22` | 22 | 862 * | `controller23` | 23 | 863 * | `controller24` | 24 | 864 * | `controller25` | 25 | 865 * | `controller26` | 26 | 866 * | `controller27` | 27 | 867 * | `controller28` | 28 | 868 * | `controller29` | 29 | 869 * | `controller30` | 30 | 870 * | `controller31` | 31 | 871 * | `bankselectfine` | 32 | 872 * | `modulationwheelfine` | 33 | 873 * | `breathcontrollerfine` | 34 | 874 * | `controller35` | 35 | 875 * | `footcontrollerfine` | 36 | 876 * | `portamentotimefine` | 37 | 877 * | `dataentryfine` | 38 | 878 * | `channelvolumefine` | 39 | 879 * | `balancefine` | 40 | 880 * | `controller41` | 41 | 881 * | `panfine` | 42 | 882 * | `expressionfine` | 43 | 883 * | `effectcontrol1fine` | 44 | 884 * | `effectcontrol2fine` | 45 | 885 * | `controller46` | 46 | 886 * | `controller47` | 47 | 887 * | `controller48` | 48 | 888 * | `controller49` | 49 | 889 * | `controller50` | 50 | 890 * | `controller51` | 51 | 891 * | `controller52` | 52 | 892 * | `controller53` | 53 | 893 * | `controller54` | 54 | 894 * | `controller55` | 55 | 895 * | `controller56` | 56 | 896 * | `controller57` | 57 | 897 * | `controller58` | 58 | 898 * | `controller59` | 59 | 899 * | `controller60` | 60 | 900 * | `controller61` | 61 | 901 * | `controller62` | 62 | 902 * | `controller63` | 63 | 903 * | `damperpedal` | 64 | 904 * | `portamento` | 65 | 905 * | `sostenuto` | 66 | 906 * | `softpedal` | 67 | 907 * | `legatopedal` | 68 | 908 * | `hold2` | 69 | 909 * | `soundvariation` | 70 | 910 * | `resonance` | 71 | 911 * | `releasetime` | 72 | 912 * | `attacktime` | 73 | 913 * | `brightness` | 74 | 914 * | `decaytime` | 75 | 915 * | `vibratorate` | 76 | 916 * | `vibratodepth` | 77 | 917 * | `vibratodelay` | 78 | 918 * | `controller79` | 79 | 919 * | `generalpurposecontroller5` | 80 | 920 * | `generalpurposecontroller6` | 81 | 921 * | `generalpurposecontroller7` | 82 | 922 * | `generalpurposecontroller8` | 83 | 923 * | `portamentocontrol` | 84 | 924 * | `controller85` | 85 | 925 * | `controller86` | 86 | 926 * | `controller87` | 87 | 927 * | `highresolutionvelocityprefix` | 88 | 928 * | `controller89` | 89 | 929 * | `controller90` | 90 | 930 * | `effect1depth` | 91 | 931 * | `effect2depth` | 92 | 932 * | `effect3depth` | 93 | 933 * | `effect4depth` | 94 | 934 * | `effect5depth` | 95 | 935 * | `dataincrement` | 96 | 936 * | `datadecrement` | 97 | 937 * | `nonregisteredparameterfine` | 98 | 938 * | `nonregisteredparametercoarse` | 99 | 939 * | `nonregisteredparameterfine` | 100 | 940 * | `registeredparametercoarse` | 101 | 941 * | `controller102` | 102 | 942 * | `controller103` | 103 | 943 * | `controller104` | 104 | 944 * | `controller105` | 105 | 945 * | `controller106` | 106 | 946 * | `controller107` | 107 | 947 * | `controller108` | 108 | 948 * | `controller109` | 109 | 949 * | `controller110` | 110 | 950 * | `controller111` | 111 | 951 * | `controller112` | 112 | 952 * | `controller113` | 113 | 953 * | `controller114` | 114 | 954 * | `controller115` | 115 | 955 * | `controller116` | 116 | 956 * | `controller117` | 117 | 957 * | `controller118` | 118 | 958 * | `controller119` | 119 | 959 * | `allsoundoff` | 120 | 960 * | `resetallcontrollers` | 121 | 961 * | `localcontrol` | 122 | 962 * | `allnotesoff` | 123 | 963 * | `omnimodeoff` | 124 | 964 * | `omnimodeon` | 125 | 965 * | `monomodeon` | 126 | 966 * | `polymodeon` | 127 | 967 * 968 * @type {object[]} 969 * @readonly 970 * @static 971 * @since 3.1 972 */ 973 static get CONTROL_CHANGE_MESSAGES() { 974 return [{ 975 number: 0, 976 name: "bankselectcoarse", 977 description: "Bank Select (Coarse)", 978 position: "msb" 979 }, { 980 number: 1, 981 name: "modulationwheelcoarse", 982 description: "Modulation Wheel (Coarse)", 983 position: "msb" 984 }, { 985 number: 2, 986 name: "breathcontrollercoarse", 987 description: "Breath Controller (Coarse)", 988 position: "msb" 989 }, { 990 number: 3, 991 name: "controller3", 992 description: "Undefined", 993 position: "msb" 994 }, { 995 number: 4, 996 name: "footcontrollercoarse", 997 description: "Foot Controller (Coarse)", 998 position: "msb" 999 }, { 1000 number: 5, 1001 name: "portamentotimecoarse", 1002 description: "Portamento Time (Coarse)", 1003 position: "msb" 1004 }, { 1005 number: 6, 1006 name: "dataentrycoarse", 1007 description: "Data Entry (Coarse)", 1008 position: "msb" 1009 }, { 1010 number: 7, 1011 name: "volumecoarse", 1012 description: "Channel Volume (Coarse)", 1013 position: "msb" 1014 }, { 1015 number: 8, 1016 name: "balancecoarse", 1017 description: "Balance (Coarse)", 1018 position: "msb" 1019 }, { 1020 number: 9, 1021 name: "controller9", 1022 description: "Controller 9 (Coarse)", 1023 position: "msb" 1024 }, { 1025 number: 10, 1026 name: "pancoarse", 1027 description: "Pan (Coarse)", 1028 position: "msb" 1029 }, { 1030 number: 11, 1031 name: "expressioncoarse", 1032 description: "Expression Controller (Coarse)", 1033 position: "msb" 1034 }, { 1035 number: 12, 1036 name: "effectcontrol1coarse", 1037 description: "Effect Control 1 (Coarse)", 1038 position: "msb" 1039 }, { 1040 number: 13, 1041 name: "effectcontrol2coarse", 1042 description: "Effect Control 2 (Coarse)", 1043 position: "msb" 1044 }, { 1045 number: 14, 1046 name: "controller14", 1047 description: "Undefined", 1048 position: "msb" 1049 }, { 1050 number: 15, 1051 name: "controller15", 1052 description: "Undefined", 1053 position: "msb" 1054 }, { 1055 number: 16, 1056 name: "generalpurposecontroller1", 1057 description: "General Purpose Controller 1 (Coarse)", 1058 position: "msb" 1059 }, { 1060 number: 17, 1061 name: "generalpurposecontroller2", 1062 description: "General Purpose Controller 2 (Coarse)", 1063 position: "msb" 1064 }, { 1065 number: 18, 1066 name: "generalpurposecontroller3", 1067 description: "General Purpose Controller 3 (Coarse)", 1068 position: "msb" 1069 }, { 1070 number: 19, 1071 name: "generalpurposecontroller4", 1072 description: "General Purpose Controller 4 (Coarse)", 1073 position: "msb" 1074 }, { 1075 number: 20, 1076 name: "controller20", 1077 description: "Undefined", 1078 position: "msb" 1079 }, { 1080 number: 21, 1081 name: "controller21", 1082 description: "Undefined", 1083 position: "msb" 1084 }, { 1085 number: 22, 1086 name: "controller22", 1087 description: "Undefined", 1088 position: "msb" 1089 }, { 1090 number: 23, 1091 name: "controller23", 1092 description: "Undefined", 1093 position: "msb" 1094 }, { 1095 number: 24, 1096 name: "controller24", 1097 description: "Undefined", 1098 position: "msb" 1099 }, { 1100 number: 25, 1101 name: "controller25", 1102 description: "Undefined", 1103 position: "msb" 1104 }, { 1105 number: 26, 1106 name: "controller26", 1107 description: "Undefined", 1108 position: "msb" 1109 }, { 1110 number: 27, 1111 name: "controller27", 1112 description: "Undefined", 1113 position: "msb" 1114 }, { 1115 number: 28, 1116 name: "controller28", 1117 description: "Undefined", 1118 position: "msb" 1119 }, { 1120 number: 29, 1121 name: "controller29", 1122 description: "Undefined", 1123 position: "msb" 1124 }, { 1125 number: 30, 1126 name: "controller30", 1127 description: "Undefined", 1128 position: "msb" 1129 }, { 1130 number: 31, 1131 name: "controller31", 1132 description: "Undefined", 1133 position: "msb" 1134 }, { 1135 number: 32, 1136 name: "bankselectfine", 1137 description: "Bank Select (Fine)", 1138 position: "lsb" 1139 }, { 1140 number: 33, 1141 name: "modulationwheelfine", 1142 description: "Modulation Wheel (Fine)", 1143 position: "lsb" 1144 }, { 1145 number: 34, 1146 name: "breathcontrollerfine", 1147 description: "Breath Controller (Fine)", 1148 position: "lsb" 1149 }, { 1150 number: 35, 1151 name: "controller35", 1152 description: "Undefined", 1153 position: "lsb" 1154 }, { 1155 number: 36, 1156 name: "footcontrollerfine", 1157 description: "Foot Controller (Fine)", 1158 position: "lsb" 1159 }, { 1160 number: 37, 1161 name: "portamentotimefine", 1162 description: "Portamento Time (Fine)", 1163 position: "lsb" 1164 }, { 1165 number: 38, 1166 name: "dataentryfine", 1167 description: "Data Entry (Fine)", 1168 position: "lsb" 1169 }, { 1170 number: 39, 1171 name: "channelvolumefine", 1172 description: "Channel Volume (Fine)", 1173 position: "lsb" 1174 }, { 1175 number: 40, 1176 name: "balancefine", 1177 description: "Balance (Fine)", 1178 position: "lsb" 1179 }, { 1180 number: 41, 1181 name: "controller41", 1182 description: "Undefined", 1183 position: "lsb" 1184 }, { 1185 number: 42, 1186 name: "panfine", 1187 description: "Pan (Fine)", 1188 position: "lsb" 1189 }, { 1190 number: 43, 1191 name: "expressionfine", 1192 description: "Expression Controller (Fine)", 1193 position: "lsb" 1194 }, { 1195 number: 44, 1196 name: "effectcontrol1fine", 1197 description: "Effect control 1 (Fine)", 1198 position: "lsb" 1199 }, { 1200 number: 45, 1201 name: "effectcontrol2fine", 1202 description: "Effect control 2 (Fine)", 1203 position: "lsb" 1204 }, { 1205 number: 46, 1206 name: "controller46", 1207 description: "Undefined", 1208 position: "lsb" 1209 }, { 1210 number: 47, 1211 name: "controller47", 1212 description: "Undefined", 1213 position: "lsb" 1214 }, { 1215 number: 48, 1216 name: "controller48", 1217 description: "General Purpose Controller 1 (Fine)", 1218 position: "lsb" 1219 }, { 1220 number: 49, 1221 name: "controller49", 1222 description: "General Purpose Controller 2 (Fine)", 1223 position: "lsb" 1224 }, { 1225 number: 50, 1226 name: "controller50", 1227 description: "General Purpose Controller 3 (Fine)", 1228 position: "lsb" 1229 }, { 1230 number: 51, 1231 name: "controller51", 1232 description: "General Purpose Controller 4 (Fine)", 1233 position: "lsb" 1234 }, { 1235 number: 52, 1236 name: "controller52", 1237 description: "Undefined", 1238 position: "lsb" 1239 }, { 1240 number: 53, 1241 name: "controller53", 1242 description: "Undefined", 1243 position: "lsb" 1244 }, { 1245 number: 54, 1246 name: "controller54", 1247 description: "Undefined", 1248 position: "lsb" 1249 }, { 1250 number: 55, 1251 name: "controller55", 1252 description: "Undefined", 1253 position: "lsb" 1254 }, { 1255 number: 56, 1256 name: "controller56", 1257 description: "Undefined", 1258 position: "lsb" 1259 }, { 1260 number: 57, 1261 name: "controller57", 1262 description: "Undefined", 1263 position: "lsb" 1264 }, { 1265 number: 58, 1266 name: "controller58", 1267 description: "Undefined", 1268 position: "lsb" 1269 }, { 1270 number: 59, 1271 name: "controller59", 1272 description: "Undefined", 1273 position: "lsb" 1274 }, { 1275 number: 60, 1276 name: "controller60", 1277 description: "Undefined", 1278 position: "lsb" 1279 }, { 1280 number: 61, 1281 name: "controller61", 1282 description: "Undefined", 1283 position: "lsb" 1284 }, { 1285 number: 62, 1286 name: "controller62", 1287 description: "Undefined", 1288 position: "lsb" 1289 }, { 1290 number: 63, 1291 name: "controller63", 1292 description: "Undefined", 1293 position: "lsb" 1294 }, { 1295 number: 64, 1296 name: "damperpedal", 1297 description: "Damper Pedal On/Off" 1298 }, { 1299 number: 65, 1300 name: "portamento", 1301 description: "Portamento On/Off" 1302 }, { 1303 number: 66, 1304 name: "sostenuto", 1305 description: "Sostenuto On/Off" 1306 }, { 1307 number: 67, 1308 name: "softpedal", 1309 description: "Soft Pedal On/Off" 1310 }, { 1311 number: 68, 1312 name: "legatopedal", 1313 description: "Legato Pedal On/Off" 1314 }, { 1315 number: 69, 1316 name: "hold2", 1317 description: "Hold 2 On/Off" 1318 }, { 1319 number: 70, 1320 name: "soundvariation", 1321 description: "Sound Variation", 1322 position: "lsb" 1323 }, { 1324 number: 71, 1325 name: "resonance", 1326 description: "Resonance", 1327 position: "lsb" 1328 }, { 1329 number: 72, 1330 name: "releasetime", 1331 description: "Release Time", 1332 position: "lsb" 1333 }, { 1334 number: 73, 1335 name: "attacktime", 1336 description: "Attack Time", 1337 position: "lsb" 1338 }, { 1339 number: 74, 1340 name: "brightness", 1341 description: "Brightness", 1342 position: "lsb" 1343 }, { 1344 number: 75, 1345 name: "decaytime", 1346 description: "Decay Time", 1347 position: "lsb" 1348 }, { 1349 number: 76, 1350 name: "vibratorate", 1351 description: "Vibrato Rate", 1352 position: "lsb" 1353 }, { 1354 number: 77, 1355 name: "vibratodepth", 1356 description: "Vibrato Depth", 1357 position: "lsb" 1358 }, { 1359 number: 78, 1360 name: "vibratodelay", 1361 description: "Vibrato Delay", 1362 position: "lsb" 1363 }, { 1364 number: 79, 1365 name: "controller79", 1366 description: "Undefined", 1367 position: "lsb" 1368 }, { 1369 number: 80, 1370 name: "generalpurposecontroller5", 1371 description: "General Purpose Controller 5", 1372 position: "lsb" 1373 }, { 1374 number: 81, 1375 name: "generalpurposecontroller6", 1376 description: "General Purpose Controller 6", 1377 position: "lsb" 1378 }, { 1379 number: 82, 1380 name: "generalpurposecontroller7", 1381 description: "General Purpose Controller 7", 1382 position: "lsb" 1383 }, { 1384 number: 83, 1385 name: "generalpurposecontroller8", 1386 description: "General Purpose Controller 8", 1387 position: "lsb" 1388 }, { 1389 number: 84, 1390 name: "portamentocontrol", 1391 description: "Portamento Control", 1392 position: "lsb" 1393 }, { 1394 number: 85, 1395 name: "controller85", 1396 description: "Undefined" 1397 }, { 1398 number: 86, 1399 name: "controller86", 1400 description: "Undefined" 1401 }, { 1402 number: 87, 1403 name: "controller87", 1404 description: "Undefined" 1405 }, { 1406 number: 88, 1407 name: "highresolutionvelocityprefix", 1408 description: "High Resolution Velocity Prefix", 1409 position: "lsb" 1410 }, { 1411 number: 89, 1412 name: "controller89", 1413 description: "Undefined" 1414 }, { 1415 number: 90, 1416 name: "controller90", 1417 description: "Undefined" 1418 }, { 1419 number: 91, 1420 name: "effect1depth", 1421 description: "Effects 1 Depth (Reverb Send Level)" 1422 }, { 1423 number: 92, 1424 name: "effect2depth", 1425 description: "Effects 2 Depth" 1426 }, { 1427 number: 93, 1428 name: "effect3depth", 1429 description: "Effects 3 Depth (Chorus Send Level)" 1430 }, { 1431 number: 94, 1432 name: "effect4depth", 1433 description: "Effects 4 Depth" 1434 }, { 1435 number: 95, 1436 name: "effect5depth", 1437 description: "Effects 5 Depth" 1438 }, { 1439 number: 96, 1440 name: "dataincrement", 1441 description: "Data Increment" 1442 }, { 1443 number: 97, 1444 name: "datadecrement", 1445 description: "Data Decrement" 1446 }, { 1447 number: 98, 1448 name: "nonregisteredparameterfine", 1449 description: "Non-Registered Parameter Number (Fine)", 1450 position: "lsb" 1451 }, { 1452 number: 99, 1453 name: "nonregisteredparametercoarse", 1454 description: "Non-Registered Parameter Number (Coarse)", 1455 position: "msb" 1456 }, { 1457 number: 100, 1458 name: "registeredparameterfine", 1459 description: "Registered Parameter Number (Fine)", 1460 position: "lsb" 1461 }, { 1462 number: 101, 1463 name: "registeredparametercoarse", 1464 description: "Registered Parameter Number (Coarse)", 1465 position: "msb" 1466 }, { 1467 number: 102, 1468 name: "controller102", 1469 description: "Undefined" 1470 }, { 1471 number: 103, 1472 name: "controller103", 1473 description: "Undefined" 1474 }, { 1475 number: 104, 1476 name: "controller104", 1477 description: "Undefined" 1478 }, { 1479 number: 105, 1480 name: "controller105", 1481 description: "Undefined" 1482 }, { 1483 number: 106, 1484 name: "controller106", 1485 description: "Undefined" 1486 }, { 1487 number: 107, 1488 name: "controller107", 1489 description: "Undefined" 1490 }, { 1491 number: 108, 1492 name: "controller108", 1493 description: "Undefined" 1494 }, { 1495 number: 109, 1496 name: "controller109", 1497 description: "Undefined" 1498 }, { 1499 number: 110, 1500 name: "controller110", 1501 description: "Undefined" 1502 }, { 1503 number: 111, 1504 name: "controller111", 1505 description: "Undefined" 1506 }, { 1507 number: 112, 1508 name: "controller112", 1509 description: "Undefined" 1510 }, { 1511 number: 113, 1512 name: "controller113", 1513 description: "Undefined" 1514 }, { 1515 number: 114, 1516 name: "controller114", 1517 description: "Undefined" 1518 }, { 1519 number: 115, 1520 name: "controller115", 1521 description: "Undefined" 1522 }, { 1523 number: 116, 1524 name: "controller116", 1525 description: "Undefined" 1526 }, { 1527 number: 117, 1528 name: "controller117", 1529 description: "Undefined" 1530 }, { 1531 number: 118, 1532 name: "controller118", 1533 description: "Undefined" 1534 }, { 1535 number: 119, 1536 name: "controller119", 1537 description: "Undefined" 1538 }, { 1539 number: 120, 1540 name: "allsoundoff", 1541 description: "All Sound Off" 1542 }, { 1543 number: 121, 1544 name: "resetallcontrollers", 1545 description: "Reset All Controllers" 1546 }, { 1547 number: 122, 1548 name: "localcontrol", 1549 description: "Local Control On/Off" 1550 }, { 1551 number: 123, 1552 name: "allnotesoff", 1553 description: "All Notes Off" 1554 }, { 1555 number: 124, 1556 name: "omnimodeoff", 1557 description: "Omni Mode Off" 1558 }, { 1559 number: 125, 1560 name: "omnimodeon", 1561 description: "Omni Mode On" 1562 }, { 1563 number: 126, 1564 name: "monomodeon", 1565 description: "Mono Mode On" 1566 }, { 1567 number: 127, 1568 name: "polymodeon", 1569 description: "Poly Mode On" 1570 }]; 1571 } 1572 1573 /** 1574 * Enumeration of all MIDI registered parameters and their associated pair of numerical values. 1575 * MIDI registered parameters extend the original list of control change messages. Currently, 1576 * there are only a limited number of them: 1577 * 1578 * 1579 * | Control Function | [LSB, MSB] | 1580 * |------------------------------|--------------| 1581 * | `pitchbendrange` | [0x00, 0x00] | 1582 * | `channelfinetuning` | [0x00, 0x01] | 1583 * | `channelcoarsetuning` | [0x00, 0x02] | 1584 * | `tuningprogram` | [0x00, 0x03] | 1585 * | `tuningbank` | [0x00, 0x04] | 1586 * | `modulationrange` | [0x00, 0x05] | 1587 * | `azimuthangle` | [0x3D, 0x00] | 1588 * | `elevationangle` | [0x3D, 0x01] | 1589 * | `gain` | [0x3D, 0x02] | 1590 * | `distanceratio` | [0x3D, 0x03] | 1591 * | `maximumdistance` | [0x3D, 0x04] | 1592 * | `maximumdistancegain` | [0x3D, 0x05] | 1593 * | `referencedistanceratio` | [0x3D, 0x06] | 1594 * | `panspreadangle` | [0x3D, 0x07] | 1595 * | `rollangle` | [0x3D, 0x08] | 1596 * 1597 * @enum {Object.<string, number[]>} 1598 * @readonly 1599 * @since 3.1 1600 * @static 1601 */ 1602 static get REGISTERED_PARAMETERS() { 1603 return { 1604 pitchbendrange: [0x00, 0x00], 1605 channelfinetuning: [0x00, 0x01], 1606 channelcoarsetuning: [0x00, 0x02], 1607 tuningprogram: [0x00, 0x03], 1608 tuningbank: [0x00, 0x04], 1609 modulationrange: [0x00, 0x05], 1610 azimuthangle: [0x3D, 0x00], 1611 elevationangle: [0x3D, 0x01], 1612 gain: [0x3D, 0x02], 1613 distanceratio: [0x3D, 0x03], 1614 maximumdistance: [0x3D, 0x04], 1615 maximumdistancegain: [0x3D, 0x05], 1616 referencedistanceratio: [0x3D, 0x06], 1617 panspreadangle: [0x3D, 0x07], 1618 rollangle: [0x3D, 0x08] 1619 }; 1620 } 1621 1622 /** 1623 * @enum {Object.<string, number[]>} 1624 * @readonly 1625 * @deprecated since 3.1 (use Enumerations.REGISTERED_PARAMETERS instead) 1626 * @private 1627 * @static 1628 */ 1629 static get MIDI_REGISTERED_PARAMETERS() { 1630 if (this.validation) { 1631 console.warn("The MIDI_REGISTERED_PARAMETERS enum has been deprecated. Use the " + "Enumerations.REGISTERED_PARAMETERS enum instead."); 1632 } 1633 return Enumerations.MIDI_REGISTERED_PARAMETERS; 1634 } 1635 1636 /** 1637 * Enumeration of all valid MIDI system messages and matching numerical values. This library also 1638 * uses two additional custom messages. 1639 * 1640 * **System Common Messages** 1641 * 1642 * | Function | Hexadecimal | Decimal | 1643 * |------------------------|-------------|---------| 1644 * | `sysex` | 0xF0 | 240 | 1645 * | `timecode` | 0xF1 | 241 | 1646 * | `songposition` | 0xF2 | 242 | 1647 * | `songselect` | 0xF3 | 243 | 1648 * | `tunerequest` | 0xF6 | 246 | 1649 * | `sysexend` | 0xF7 | 247 | 1650 * 1651 * The `sysexend` message is never actually received. It simply ends a sysex stream. 1652 * 1653 * **System Real-Time Messages** 1654 * 1655 * | Function | Hexadecimal | Decimal | 1656 * |------------------------|-------------|---------| 1657 * | `clock` | 0xF8 | 248 | 1658 * | `start` | 0xFA | 250 | 1659 * | `continue` | 0xFB | 251 | 1660 * | `stop` | 0xFC | 252 | 1661 * | `activesensing` | 0xFE | 254 | 1662 * | `reset` | 0xFF | 255 | 1663 * 1664 * Values 249 and 253 are relayed by the 1665 * [Web MIDI API](https://developer.mozilla.org/en-US/docs/Web/API/Web_MIDI_API) but they do not 1666 * serve any specific purpose. The 1667 * [MIDI 1.0 spec](https://www.midi.org/specifications/item/table-1-summary-of-midi-message) 1668 * simply states that they are undefined/reserved. 1669 * 1670 * **Custom Messages** 1671 * 1672 * These two messages are mostly for internal use. They are not MIDI messages and cannot be sent 1673 * or forwarded. 1674 * 1675 * | Function | Hexadecimal | Decimal | 1676 * |------------------------|-------------|---------| 1677 * | `midimessage` | | 0 | 1678 * | `unknownsystemmessage` | | -1 | 1679 * 1680 * @enum {Object.<string, number>} 1681 * @readonly 1682 * @since 3.1 1683 * @static 1684 */ 1685 static get SYSTEM_MESSAGES() { 1686 return { 1687 // System common messages 1688 sysex: 0xF0, 1689 // 240 1690 timecode: 0xF1, 1691 // 241 1692 songposition: 0xF2, 1693 // 242 1694 songselect: 0xF3, 1695 // 243 1696 tunerequest: 0xF6, 1697 // 246 1698 tuningrequest: 0xF6, 1699 // for backwards-compatibility (deprecated in version 3.0) 1700 sysexend: 0xF7, 1701 // 247 (never actually received - simply ends a sysex) 1702 1703 // System real-time messages 1704 clock: 0xF8, 1705 // 248 1706 start: 0xFA, 1707 // 250 1708 continue: 0xFB, 1709 // 251 1710 stop: 0xFC, 1711 // 252 1712 activesensing: 0xFE, 1713 // 254 1714 reset: 0xFF, 1715 // 255 1716 1717 // Custom WebMidi.js messages 1718 midimessage: 0, 1719 unknownsystemmessage: -1 1720 }; 1721 } 1722 1723 /** 1724 * @enum {Object.<string, number>} 1725 * @readonly 1726 * @deprecated since 3.1 (use Enumerations.SYSTEM_MESSAGES instead) 1727 * @private 1728 * @static 1729 */ 1730 static get MIDI_SYSTEM_MESSAGES() { 1731 if (this.validation) { 1732 console.warn("The MIDI_SYSTEM_MESSAGES enum has been deprecated. Use the " + "Enumerations.SYSTEM_MESSAGES enum instead."); 1733 } 1734 return Enumerations.SYSTEM_MESSAGES; 1735 } 1736 1737 /** 1738 * Array of channel-specific event names that can be listened for. This includes channel mode 1739 * events and RPN/NRPN events. 1740 * 1741 * @type {string[]} 1742 * @readonly 1743 */ 1744 static get CHANNEL_EVENTS() { 1745 return [ 1746 // MIDI channel message events 1747 "noteoff", "controlchange", "noteon", "keyaftertouch", "programchange", "channelaftertouch", "pitchbend", 1748 // MIDI channel mode events 1749 "allnotesoff", "allsoundoff", "localcontrol", "monomode", "omnimode", "resetallcontrollers", 1750 // RPN/NRPN events 1751 "nrpn", "nrpn-dataentrycoarse", "nrpn-dataentryfine", "nrpn-dataincrement", "nrpn-datadecrement", "rpn", "rpn-dataentrycoarse", "rpn-dataentryfine", "rpn-dataincrement", "rpn-datadecrement", 1752 // Legacy (remove in v4) 1753 "nrpn-databuttonincrement", "nrpn-databuttondecrement", "rpn-databuttonincrement", "rpn-databuttondecrement"]; 1754 } 1755 } 1756 1757 /** 1758 * The `Note` class represents a single musical note such as `"D3"`, `"G#4"`, `"F-1"`, `"Gb7"`, etc. 1759 * 1760 * `Note` objects can be played back on a single channel by calling 1761 * [`OutputChannel.playNote()`]{@link OutputChannel#playNote} or, on multiple channels of the same 1762 * output, by calling [`Output.playNote()`]{@link Output#playNote}. 1763 * 1764 * The note has [`attack`](#attack) and [`release`](#release) velocities set at `0.5` by default. 1765 * These can be changed by passing in the appropriate option. It is also possible to set a 1766 * system-wide default for attack and release velocities by using the 1767 * [`WebMidi.defaults`](WebMidi#defaults) property. 1768 * 1769 * If you prefer to work with raw MIDI values (`0` to `127`), you can use [`rawAttack`](#rawAttack) and 1770 * [`rawRelease`](#rawRelease) to both get and set the values. 1771 * 1772 * The note may have a [`duration`](#duration). If it does, playback will be automatically stopped 1773 * when the duration has elapsed by sending a `"noteoff"` event. By default, the duration is set to 1774 * `Infinity`. In this case, it will never stop playing unless explicitly stopped by calling a 1775 * method such as [`OutputChannel.stopNote()`]{@link OutputChannel#stopNote}, 1776 * [`Output.stopNote()`]{@link Output#stopNote} or similar. 1777 * 1778 * @license Apache-2.0 1779 * @since 3.0.0 1780 */ 1781 class Note { 1782 /** 1783 * Creates a `Note` object. 1784 * 1785 * @param value {string|number} The value used to create the note. If an identifier string is used, 1786 * it must start with the note letter, optionally followed by an accidental and followed by the 1787 * octave number (`"C3"`, `"G#4"`, `"F-1"`, `"Db7"`, etc.). If a number is used, it must be an 1788 * integer between 0 and 127. In this case, middle C is considered to be C4 (note number 60). 1789 * 1790 * @param {object} [options={}] 1791 * 1792 * @param {number} [options.duration=Infinity] The number of milliseconds before the note should be 1793 * explicitly stopped. 1794 * 1795 * @param {number} [options.attack=0.5] The note's attack velocity as a float between 0 and 1. If 1796 * you wish to use an integer between 0 and 127, use the `rawAttack` option instead. If both 1797 * `attack` and `rawAttack` are specified, the latter has precedence. 1798 * 1799 * @param {number} [options.release=0.5] The note's release velocity as a float between 0 and 1. If 1800 * you wish to use an integer between 0 and 127, use the `rawRelease` option instead. If both 1801 * `release` and `rawRelease` are specified, the latter has precedence. 1802 * 1803 * @param {number} [options.rawAttack=64] The note's attack velocity as an integer between 0 and 1804 * 127. If you wish to use a float between 0 and 1, use the `release` option instead. If both 1805 * `attack` and `rawAttack` are specified, the latter has precedence. 1806 * 1807 * @param {number} [options.rawRelease=64] The note's release velocity as an integer between 0 and 1808 * 127. If you wish to use a float between 0 and 1, use the `release` option instead. If both 1809 * `release` and `rawRelease` are specified, the latter has precedence. 1810 * 1811 * @throws {Error} Invalid note identifier 1812 * @throws {RangeError} Invalid name value 1813 * @throws {RangeError} Invalid accidental value 1814 * @throws {RangeError} Invalid octave value 1815 * @throws {RangeError} Invalid duration value 1816 * @throws {RangeError} Invalid attack value 1817 * @throws {RangeError} Invalid release value 1818 */ 1819 constructor(value, options = {}) { 1820 // Assign property defaults 1821 this.duration = wm.defaults.note.duration; 1822 this.attack = wm.defaults.note.attack; 1823 this.release = wm.defaults.note.release; 1824 1825 // Assign property values from options (validation occurs in setter) 1826 if (options.duration != undefined) this.duration = options.duration; 1827 if (options.attack != undefined) this.attack = options.attack; 1828 if (options.rawAttack != undefined) this.attack = Utilities.from7bitToFloat(options.rawAttack); 1829 if (options.release != undefined) this.release = options.release; 1830 if (options.rawRelease != undefined) { 1831 this.release = Utilities.from7bitToFloat(options.rawRelease); 1832 } 1833 1834 // Assign note depending on the way it was specified (name or number) 1835 if (Number.isInteger(value)) { 1836 this.identifier = Utilities.toNoteIdentifier(value); 1837 } else { 1838 this.identifier = value; 1839 } 1840 } 1841 1842 /** 1843 * The name, optional accidental and octave of the note, as a string. 1844 * @type {string} 1845 * @since 3.0.0 1846 */ 1847 get identifier() { 1848 return this._name + (this._accidental || "") + this._octave; 1849 } 1850 set identifier(value) { 1851 const fragments = Utilities.getNoteDetails(value); 1852 if (wm.validation) { 1853 if (!value) throw new Error("Invalid note identifier"); 1854 } 1855 this._name = fragments.name; 1856 this._accidental = fragments.accidental; 1857 this._octave = fragments.octave; 1858 } 1859 1860 /** 1861 * The name (letter) of the note. If you need the full name with octave and accidental, you can 1862 * use the [`identifier`]{@link Note#identifier} property instead. 1863 * @type {string} 1864 * @since 3.0.0 1865 */ 1866 get name() { 1867 return this._name; 1868 } 1869 set name(value) { 1870 if (wm.validation) { 1871 value = value.toUpperCase(); 1872 if (!["C", "D", "E", "F", "G", "A", "B"].includes(value)) { 1873 throw new Error("Invalid name value"); 1874 } 1875 } 1876 this._name = value; 1877 } 1878 1879 /** 1880 * The accidental (#, ##, b or bb) of the note. 1881 * @type {string} 1882 * @since 3.0.0 1883 */ 1884 get accidental() { 1885 return this._accidental; 1886 } 1887 set accidental(value) { 1888 if (wm.validation) { 1889 value = value.toLowerCase(); 1890 if (!["#", "##", "b", "bb"].includes(value)) throw new Error("Invalid accidental value"); 1891 } 1892 this._accidental = value; 1893 } 1894 1895 /** 1896 * The octave of the note. 1897 * @type {number} 1898 * @since 3.0.0 1899 */ 1900 get octave() { 1901 return this._octave; 1902 } 1903 set octave(value) { 1904 if (wm.validation) { 1905 value = parseInt(value); 1906 if (isNaN(value)) throw new Error("Invalid octave value"); 1907 } 1908 this._octave = value; 1909 } 1910 1911 /** 1912 * The duration of the note as a positive decimal number representing the number of milliseconds 1913 * that the note should play for. 1914 * 1915 * @type {number} 1916 * @since 3.0.0 1917 */ 1918 get duration() { 1919 return this._duration; 1920 } 1921 set duration(value) { 1922 if (wm.validation) { 1923 value = parseFloat(value); 1924 if (isNaN(value) || value === null || value < 0) { 1925 throw new RangeError("Invalid duration value."); 1926 } 1927 } 1928 this._duration = value; 1929 } 1930 1931 /** 1932 * The attack velocity of the note as a float between 0 and 1. 1933 * @type {number} 1934 * @since 3.0.0 1935 */ 1936 get attack() { 1937 return this._attack; 1938 } 1939 set attack(value) { 1940 if (wm.validation) { 1941 value = parseFloat(value); 1942 if (isNaN(value) || !(value >= 0 && value <= 1)) { 1943 throw new RangeError("Invalid attack value."); 1944 } 1945 } 1946 this._attack = value; 1947 } 1948 1949 /** 1950 * The release velocity of the note as an integer between 0 and 1. 1951 * @type {number} 1952 * @since 3.0.0 1953 */ 1954 get release() { 1955 return this._release; 1956 } 1957 set release(value) { 1958 if (wm.validation) { 1959 value = parseFloat(value); 1960 if (isNaN(value) || !(value >= 0 && value <= 1)) { 1961 throw new RangeError("Invalid release value."); 1962 } 1963 } 1964 this._release = value; 1965 } 1966 1967 /** 1968 * The attack velocity of the note as a positive integer between 0 and 127. 1969 * @type {number} 1970 * @since 3.0.0 1971 */ 1972 get rawAttack() { 1973 return Utilities.fromFloatTo7Bit(this._attack); 1974 } 1975 set rawAttack(value) { 1976 this._attack = Utilities.from7bitToFloat(value); 1977 } 1978 1979 /** 1980 * The release velocity of the note as a positive integer between 0 and 127. 1981 * @type {number} 1982 * @since 3.0.0 1983 */ 1984 get rawRelease() { 1985 return Utilities.fromFloatTo7Bit(this._release); 1986 } 1987 set rawRelease(value) { 1988 this._release = Utilities.from7bitToFloat(value); 1989 } 1990 1991 /** 1992 * The MIDI number of the note (`0` - `127`). This number is derived from the note identifier 1993 * using C4 as a reference for middle C. 1994 * 1995 * @type {number} 1996 * @readonly 1997 * @since 3.0.0 1998 */ 1999 get number() { 2000 return Utilities.toNoteNumber(this.identifier); 2001 } 2002 2003 /** 2004 * Returns a MIDI note number offset by octave and/or semitone. If the calculated value is less 2005 * than 0, 0 will be returned. If the calculated value is more than 127, 127 will be returned. If 2006 * an invalid value is supplied, 0 will be used. 2007 * 2008 * @param [octaveOffset] {number} An integer to offset the note number by octave. 2009 * @param [semitoneOffset] {number} An integer to offset the note number by semitone. 2010 * @returns {number} An integer between 0 and 127 2011 */ 2012 getOffsetNumber(octaveOffset = 0, semitoneOffset = 0) { 2013 if (wm.validation) { 2014 octaveOffset = parseInt(octaveOffset) || 0; 2015 semitoneOffset = parseInt(semitoneOffset) || 0; 2016 } 2017 return Math.min(Math.max(this.number + octaveOffset * 12 + semitoneOffset, 0), 127); 2018 } 2019 } 2020 2021 /** 2022 * The `Utilities` class contains general-purpose utility methods. All methods are static and 2023 * should be called using the class name. For example: `Utilities.getNoteDetails("C4")`. 2024 * 2025 * @license Apache-2.0 2026 * @since 3.0.0 2027 */ 2028 class Utilities { 2029 /** 2030 * Returns a MIDI note number matching the identifier passed in the form of a string. The 2031 * identifier must include the octave number. The identifier also optionally include a sharp (#), 2032 * a double sharp (##), a flat (b) or a double flat (bb) symbol. For example, these are all valid 2033 * identifiers: C5, G4, D#-1, F0, Gb7, Eb-1, Abb4, B##6, etc. 2034 * 2035 * When converting note identifiers to numbers, C4 is considered to be middle C (MIDI note number 2036 * 60) as per the scientific pitch notation standard. 2037 * 2038 * The resulting note number can be offset by using the `octaveOffset` parameter. 2039 * 2040 * @param identifier {string} The identifier in the form of a letter, followed by an optional "#", 2041 * "##", "b" or "bb" followed by the octave number. For exemple: C5, G4, D#-1, F0, Gb7, Eb-1, 2042 * Abb4, B##6, etc. 2043 * 2044 * @param {number} [octaveOffset=0] A integer to offset the octave by. 2045 * 2046 * @returns {number} The MIDI note number (an integer between 0 and 127). 2047 * 2048 * @throws RangeError Invalid 'octaveOffset' value 2049 * 2050 * @throws TypeError Invalid note identifier 2051 * 2052 * @license Apache-2.0 2053 * @since 3.0.0 2054 * @static 2055 */ 2056 static toNoteNumber(identifier, octaveOffset = 0) { 2057 // Validation 2058 octaveOffset = octaveOffset == undefined ? 0 : parseInt(octaveOffset); 2059 if (isNaN(octaveOffset)) throw new RangeError("Invalid 'octaveOffset' value"); 2060 if (typeof identifier !== "string") identifier = ""; 2061 const fragments = this.getNoteDetails(identifier); 2062 if (!fragments) throw new TypeError("Invalid note identifier"); 2063 const notes = { 2064 C: 0, 2065 D: 2, 2066 E: 4, 2067 F: 5, 2068 G: 7, 2069 A: 9, 2070 B: 11 2071 }; 2072 let result = (fragments.octave + 1 + octaveOffset) * 12; 2073 result += notes[fragments.name]; 2074 if (fragments.accidental) { 2075 if (fragments.accidental.startsWith("b")) { 2076 result -= fragments.accidental.length; 2077 } else { 2078 result += fragments.accidental.length; 2079 } 2080 } 2081 if (result < 0 || result > 127) throw new RangeError("Invalid octaveOffset value"); 2082 return result; 2083 } 2084 2085 /** 2086 * Given a proper note identifier (`C#4`, `Gb-1`, etc.) or a valid MIDI note number (0-127), this 2087 * method returns an object containing broken down details about the specified note (uppercase 2088 * letter, accidental and octave). 2089 * 2090 * When a number is specified, the translation to note is done using a value of 60 for middle C 2091 * (C4 = middle C). 2092 * 2093 * @param value {string|number} A note identifier A atring ("C#4", "Gb-1", etc.) or a MIDI note 2094 * number (0-127). 2095 * 2096 * @returns {{accidental: string, identifier: string, name: string, octave: number }} 2097 * 2098 * @throws TypeError Invalid note identifier 2099 * 2100 * @since 3.0.0 2101 * @static 2102 */ 2103 static getNoteDetails(value) { 2104 if (Number.isInteger(value)) value = this.toNoteIdentifier(value); 2105 const matches = value.match(/^([CDEFGAB])(#{0,2}|b{0,2})(-?\d+)$/i); 2106 if (!matches) throw new TypeError("Invalid note identifier"); 2107 const name = matches[1].toUpperCase(); 2108 const octave = parseInt(matches[3]); 2109 let accidental = matches[2].toLowerCase(); 2110 accidental = accidental === "" ? undefined : accidental; 2111 const fragments = { 2112 accidental: accidental, 2113 identifier: name + (accidental || "") + octave, 2114 name: name, 2115 octave: octave 2116 }; 2117 return fragments; 2118 } 2119 2120 /** 2121 * Returns a sanitized array of valid MIDI channel numbers (1-16). The parameter should be a 2122 * single integer or an array of integers. 2123 * 2124 * For backwards-compatibility, passing `undefined` as a parameter to this method results in all 2125 * channels being returned (1-16). Otherwise, parameters that cannot successfully be parsed to 2126 * integers between 1 and 16 are silently ignored. 2127 * 2128 * @param [channel] {number|number[]} An integer or an array of integers to parse as channel 2129 * numbers. 2130 * 2131 * @returns {number[]} An array of 0 or more valid MIDI channel numbers. 2132 * 2133 * @since 3.0.0 2134 * @static 2135 */ 2136 static sanitizeChannels(channel) { 2137 let channels; 2138 if (wm.validation) { 2139 if (channel === "all") { 2140 // backwards-compatibility 2141 channels = ["all"]; 2142 } else if (channel === "none") { 2143 // backwards-compatibility 2144 return []; 2145 } 2146 } 2147 if (!Array.isArray(channel)) { 2148 channels = [channel]; 2149 } else { 2150 channels = channel; 2151 } 2152 2153 // In order to preserve backwards-compatibility, we let this assignment as it is. 2154 if (channels.indexOf("all") > -1) { 2155 channels = Enumerations.MIDI_CHANNEL_NUMBERS; 2156 } 2157 return channels.map(function (ch) { 2158 return parseInt(ch); 2159 }).filter(function (ch) { 2160 return ch >= 1 && ch <= 16; 2161 }); 2162 } 2163 2164 /** 2165 * Returns a valid timestamp, relative to the navigation start of the document, derived from the 2166 * `time` parameter. If the parameter is a string starting with the "+" sign and followed by a 2167 * number, the resulting timestamp will be the sum of the current timestamp plus that number. If 2168 * the parameter is a positive number, it will be returned as is. Otherwise, false will be 2169 * returned. 2170 * 2171 * @param [time] {number|string} The time string (e.g. `"+2000"`) or number to parse 2172 * @return {number|false} A positive number or `false` (if the time cannot be converted) 2173 * 2174 * @since 3.0.0 2175 * @static 2176 */ 2177 static toTimestamp(time) { 2178 let value = false; 2179 const parsed = parseFloat(time); 2180 if (isNaN(parsed)) return false; 2181 if (typeof time === "string" && time.substring(0, 1) === "+") { 2182 if (parsed >= 0) value = wm.time + parsed; 2183 } else { 2184 if (parsed >= 0) value = parsed; 2185 } 2186 return value; 2187 } 2188 2189 /** 2190 * Returns a valid MIDI note number (0-127) given the specified input. The input usually is a 2191 * string containing a note identifier (`"C3"`, `"F#4"`, `"D-2"`, `"G8"`, etc.). If an integer 2192 * between 0 and 127 is passed, it will simply be returned as is (for convenience). Other strings 2193 * will be parsed for integer value, if possible. 2194 * 2195 * If the input is an identifier, the resulting note number is offset by the `octaveOffset` 2196 * parameter. For example, if you pass in "C4" (note number 60) and the `octaveOffset` value is 2197 * -2, the resulting MIDI note number will be 36. 2198 * 2199 * @param input {string|number} A string or number to extract the MIDI note number from. 2200 * @param octaveOffset {number} An integer to offset the octave by 2201 * 2202 * @returns {number|false} A valid MIDI note number (0-127) or `false` if the input could not 2203 * successfully be parsed to a note number. 2204 * 2205 * @since 3.0.0 2206 * @static 2207 */ 2208 static guessNoteNumber(input, octaveOffset) { 2209 // Validate and, if necessary, assign default 2210 octaveOffset = parseInt(octaveOffset) || 0; 2211 let output = false; 2212 2213 // Check input type 2214 if (Number.isInteger(input) && input >= 0 && input <= 127) { 2215 // uint 2216 output = parseInt(input); 2217 } else if (parseInt(input) >= 0 && parseInt(input) <= 127) { 2218 // float or uint as string 2219 output = parseInt(input); 2220 } else if (typeof input === "string" || input instanceof String) { 2221 // string 2222 try { 2223 output = this.toNoteNumber(input.trim(), octaveOffset); 2224 } catch (e) { 2225 return false; 2226 } 2227 } 2228 return output; 2229 } 2230 2231 /** 2232 * Returns an identifier string representing a note name (with optional accidental) followed by an 2233 * octave number. The octave can be offset by using the `octaveOffset` parameter. 2234 * 2235 * @param {number} number The MIDI note number to convert to a note identifier 2236 * @param {number} octaveOffset An offset to apply to the resulting octave 2237 * 2238 * @returns {string} 2239 * 2240 * @throws RangeError Invalid note number 2241 * @throws RangeError Invalid octaveOffset value 2242 * 2243 * @since 3.0.0 2244 * @static 2245 */ 2246 static toNoteIdentifier(number, octaveOffset) { 2247 number = parseInt(number); 2248 if (isNaN(number) || number < 0 || number > 127) throw new RangeError("Invalid note number"); 2249 octaveOffset = octaveOffset == undefined ? 0 : parseInt(octaveOffset); 2250 if (isNaN(octaveOffset)) throw new RangeError("Invalid octaveOffset value"); 2251 const notes = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; 2252 const octave = Math.floor(number / 12 - 1) + octaveOffset; 2253 return notes[number % 12] + octave.toString(); 2254 } 2255 2256 /** 2257 * Converts the `input` parameter to a valid [`Note`]{@link Note} object. The input usually is an 2258 * unsigned integer (0-127) or a note identifier (`"C4"`, `"G#5"`, etc.). If the input is a 2259 * [`Note`]{@link Note} object, it will be returned as is. 2260 * 2261 * If the input is a note number or identifier, it is possible to specify options by providing the 2262 * `options` parameter. 2263 * 2264 * @param [input] {number|string|Note} 2265 * 2266 * @param {object} [options={}] 2267 * 2268 * @param {number} [options.duration=Infinity] The number of milliseconds before the note should 2269 * be explicitly stopped. 2270 * 2271 * @param {number} [options.attack=0.5] The note's attack velocity as a float between 0 and 1. If 2272 * you wish to use an integer between 0 and 127, use the `rawAttack` option instead. If both 2273 * `attack` and `rawAttack` are specified, the latter has precedence. 2274 * 2275 * @param {number} [options.release=0.5] The note's release velocity as a float between 0 and 1. If 2276 * you wish to use an integer between 0 and 127, use the `rawRelease` option instead. If both 2277 * `release` and `rawRelease` are specified, the latter has precedence. 2278 * 2279 * @param {number} [options.rawAttack=64] The note's attack velocity as an integer between 0 and 2280 * 127. If you wish to use a float between 0 and 1, use the `release` option instead. If both 2281 * `attack` and `rawAttack` are specified, the latter has precedence. 2282 * 2283 * @param {number} [options.rawRelease=64] The note's release velocity as an integer between 0 and 2284 * 127. If you wish to use a float between 0 and 1, use the `release` option instead. If both 2285 * `release` and `rawRelease` are specified, the latter has precedence. 2286 * 2287 * @param {number} [options.octaveOffset=0] An integer to offset the octave by. **This is only 2288 * used when the input value is a note identifier.** 2289 * 2290 * @returns {Note} 2291 * 2292 * @throws TypeError The input could not be parsed to a note 2293 * 2294 * @since version 3.0.0 2295 * @static 2296 */ 2297 static buildNote(input, options = {}) { 2298 options.octaveOffset = parseInt(options.octaveOffset) || 0; 2299 2300 // If it's already a Note, we're done 2301 if (input instanceof Note) return input; 2302 let number = this.guessNoteNumber(input, options.octaveOffset); 2303 if (number === false) { 2304 // We use a comparison b/c the note can be 0 (which equates to false) 2305 throw new TypeError(`The input could not be parsed as a note (${input})`); 2306 } 2307 2308 // If we got here, we have a proper note number. Before creating the new note, we strip out 2309 // 'octaveOffset' because it has already been factored in when calling guessNoteNumber(). 2310 options.octaveOffset = undefined; 2311 return new Note(number, options); 2312 } 2313 2314 /** 2315 * Converts an input value, which can be an unsigned integer (0-127), a note identifier, a 2316 * [`Note`]{@link Note} object or an array of the previous types, to an array of 2317 * [`Note`]{@link Note} objects. 2318 * 2319 * [`Note`]{@link Note} objects are returned as is. For note numbers and identifiers, a 2320 * [`Note`]{@link Note} object is created with the options specified. An error will be thrown when 2321 * encountering invalid input. 2322 * 2323 * Note: if both the `attack` and `rawAttack` options are specified, the later has priority. The 2324 * same goes for `release` and `rawRelease`. 2325 * 2326 * @param [notes] {number|string|Note|number[]|string[]|Note[]} 2327 * 2328 * @param {object} [options={}] 2329 * 2330 * @param {number} [options.duration=Infinity] The number of milliseconds before the note should 2331 * be explicitly stopped. 2332 * 2333 * @param {number} [options.attack=0.5] The note's attack velocity as a float between 0 and 1. If 2334 * you wish to use an integer between 0 and 127, use the `rawAttack` option instead. If both 2335 * `attack` and `rawAttack` are specified, the latter has precedence. 2336 * 2337 * @param {number} [options.release=0.5] The note's release velocity as a float between 0 and 1. If 2338 * you wish to use an integer between 0 and 127, use the `rawRelease` option instead. If both 2339 * `release` and `rawRelease` are specified, the latter has precedence. 2340 * 2341 * @param {number} [options.rawAttack=64] The note's attack velocity as an integer between 0 and 2342 * 127. If you wish to use a float between 0 and 1, use the `release` option instead. If both 2343 * `attack` and `rawAttack` are specified, the latter has precedence. 2344 * 2345 * @param {number} [options.rawRelease=64] The note's release velocity as an integer between 0 and 2346 * 127. If you wish to use a float between 0 and 1, use the `release` option instead. If both 2347 * `release` and `rawRelease` are specified, the latter has precedence. 2348 * 2349 * @param {number} [options.octaveOffset=0] An integer to offset the octave by. **This is only 2350 * used when the input value is a note identifier.** 2351 * 2352 * @returns {Note[]} 2353 * 2354 * @throws TypeError An element could not be parsed as a note. 2355 * 2356 * @since 3.0.0 2357 * @static 2358 */ 2359 static buildNoteArray(notes, options = {}) { 2360 let result = []; 2361 if (!Array.isArray(notes)) notes = [notes]; 2362 notes.forEach(note => { 2363 result.push(this.buildNote(note, options)); 2364 }); 2365 return result; 2366 } 2367 2368 /** 2369 * Returns a number between 0 and 1 representing the ratio of the input value divided by 127 (7 2370 * bit). The returned value is restricted between 0 and 1 even if the input is greater than 127 or 2371 * smaller than 0. 2372 * 2373 * Passing `Infinity` will return `1` and passing `-Infinity` will return `0`. Otherwise, when the 2374 * input value cannot be converted to an integer, the method returns 0. 2375 * 2376 * @param value {number} A positive integer between 0 and 127 (inclusive) 2377 * @returns {number} A number between 0 and 1 (inclusive) 2378 * @static 2379 */ 2380 static from7bitToFloat(value) { 2381 if (value === Infinity) value = 127; 2382 value = parseInt(value) || 0; 2383 return Math.min(Math.max(value / 127, 0), 1); 2384 } 2385 2386 /** 2387 * Returns an integer between 0 and 127 which is the result of multiplying the input value by 2388 * 127. The input value should be a number between 0 and 1 (inclusively). The returned value is 2389 * restricted between 0 and 127 even if the input is greater than 1 or smaller than 0. 2390 * 2391 * Passing `Infinity` will return `127` and passing `-Infinity` will return `0`. Otherwise, when 2392 * the input value cannot be converted to a number, the method returns 0. 2393 * 2394 * @param value {number} A positive float between 0 and 1 (inclusive) 2395 * @returns {number} A number between 0 and 127 (inclusive) 2396 * @static 2397 */ 2398 static fromFloatTo7Bit(value) { 2399 if (value === Infinity) value = 1; 2400 value = parseFloat(value) || 0; 2401 return Math.min(Math.max(Math.round(value * 127), 0), 127); 2402 } 2403 2404 /** 2405 * Combines and converts MSB and LSB values (0-127) to a float between 0 and 1. The returned value 2406 * is within between 0 and 1 even if the result is greater than 1 or smaller than 0. 2407 * 2408 * @param msb {number} The most significant byte as a integer between 0 and 127. 2409 * @param [lsb=0] {number} The least significant byte as a integer between 0 and 127. 2410 * @returns {number} A float between 0 and 1. 2411 */ 2412 static fromMsbLsbToFloat(msb, lsb = 0) { 2413 if (wm.validation) { 2414 msb = Math.min(Math.max(parseInt(msb) || 0, 0), 127); 2415 lsb = Math.min(Math.max(parseInt(lsb) || 0, 0), 127); 2416 } 2417 const value = ((msb << 7) + lsb) / 16383; 2418 return Math.min(Math.max(value, 0), 1); 2419 } 2420 2421 /** 2422 * Extracts 7bit MSB and LSB values from the supplied float. 2423 * 2424 * @param value {number} A float between 0 and 1 2425 * @returns {{lsb: number, msb: number}} 2426 */ 2427 static fromFloatToMsbLsb(value) { 2428 if (wm.validation) { 2429 value = Math.min(Math.max(parseFloat(value) || 0, 0), 1); 2430 } 2431 const multiplied = Math.round(value * 16383); 2432 return { 2433 msb: multiplied >> 7, 2434 lsb: multiplied & 0x7F 2435 }; 2436 } 2437 2438 /** 2439 * Returns the supplied MIDI note number offset by the requested octave and semitone values. If 2440 * the calculated value is less than 0, 0 will be returned. If the calculated value is more than 2441 * 127, 127 will be returned. If an invalid offset value is supplied, 0 will be used. 2442 * 2443 * @param number {number} The MIDI note to offset as an integer between 0 and 127. 2444 * @param octaveOffset {number} An integer to offset the note by (in octave) 2445 * @param octaveOffset {number} An integer to offset the note by (in semitones) 2446 * @returns {number} An integer between 0 and 127 2447 * 2448 * @throws {Error} Invalid note number 2449 * @static 2450 */ 2451 static offsetNumber(number, octaveOffset = 0, semitoneOffset = 0) { 2452 if (wm.validation) { 2453 number = parseInt(number); 2454 if (isNaN(number)) throw new Error("Invalid note number"); 2455 octaveOffset = parseInt(octaveOffset) || 0; 2456 semitoneOffset = parseInt(semitoneOffset) || 0; 2457 } 2458 return Math.min(Math.max(number + octaveOffset * 12 + semitoneOffset, 0), 127); 2459 } 2460 2461 /** 2462 * Returns the name of the first property of the supplied object whose value is equal to the one 2463 * supplied. If nothing is found, `undefined` is returned. 2464 * 2465 * @param object {object} The object to look for the property in. 2466 * @param value {*} Any value that can be expected to be found in the object's properties. 2467 * @returns {string|undefined} The name of the matching property or `undefined` if nothing is 2468 * found. 2469 * @static 2470 */ 2471 static getPropertyByValue(object, value) { 2472 return Object.keys(object).find(key => object[key] === value); 2473 } 2474 2475 /** 2476 * Returns the name of a control change message matching the specified number (0-127). Some valid 2477 * control change numbers do not have a specific name or purpose assigned in the MIDI 2478 * [spec](https://midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2). 2479 * In these cases, the method returns `controllerXXX` (where XXX is the number). 2480 * 2481 * @param {number} number An integer (0-127) representing the control change message 2482 * @returns {string|undefined} The matching control change name or `undefined` if no match was 2483 * found. 2484 * 2485 * @static 2486 */ 2487 static getCcNameByNumber(number) { 2488 if (wm.validation) { 2489 number = parseInt(number); 2490 if (!(number >= 0 && number <= 127)) return undefined; 2491 } 2492 return Enumerations.CONTROL_CHANGE_MESSAGES[number].name; 2493 } 2494 2495 /** 2496 * Returns the number of a control change message matching the specified name. 2497 * 2498 * @param {string} name A string representing the control change message 2499 * @returns {string|undefined} The matching control change number or `undefined` if no match was 2500 * found. 2501 * 2502 * @since 3.1 2503 * @static 2504 */ 2505 static getCcNumberByName(name) { 2506 let message = Enumerations.CONTROL_CHANGE_MESSAGES.find(element => element.name === name); 2507 if (message) { 2508 return message.number; 2509 } else { 2510 // Legacy (remove in v4) 2511 return Enumerations.MIDI_CONTROL_CHANGE_MESSAGES[name]; 2512 } 2513 } 2514 2515 /** 2516 * Returns the channel mode name matching the specified number. If no match is found, the function 2517 * returns `false`. 2518 * 2519 * @param {number} number An integer representing the channel mode message (120-127) 2520 * @returns {string|false} The name of the matching channel mode or `false` if no match could be 2521 * found. 2522 * 2523 * @since 2.0.0 2524 */ 2525 static getChannelModeByNumber(number) { 2526 if (!(number >= 120 && number <= 127)) return false; 2527 for (let cm in Enumerations.CHANNEL_MODE_MESSAGES) { 2528 if (Enumerations.CHANNEL_MODE_MESSAGES.hasOwnProperty(cm) && number === Enumerations.CHANNEL_MODE_MESSAGES[cm]) { 2529 return cm; 2530 } 2531 } 2532 return false; 2533 } 2534 2535 /** 2536 * Indicates whether the execution environment is Node.js (`true`) or not (`false`) 2537 * @type {boolean} 2538 */ 2539 static get isNode() { 2540 return typeof process !== "undefined" && process.versions != null && process.versions.node != null; 2541 } 2542 2543 /** 2544 * Indicates whether the execution environment is a browser (`true`) or not (`false`) 2545 * @type {boolean} 2546 */ 2547 static get isBrowser() { 2548 return typeof window !== "undefined" && typeof window.document !== "undefined"; 2549 } 2550 } 2551 2552 /** 2553 * The `OutputChannel` class represents a single output MIDI channel. `OutputChannel` objects are 2554 * provided by an [`Output`](Output) port which, itself, is made available by a device. The 2555 * `OutputChannel` object is derived from the host's MIDI subsystem and should not be instantiated 2556 * directly. 2557 * 2558 * All 16 `OutputChannel` objects can be found inside the parent output's 2559 * [`channels`]{@link Output#channels} property. 2560 * 2561 * @param {Output} output The [`Output`](Output) this channel belongs to. 2562 * @param {number} number The MIDI channel number (`1` - `16`). 2563 * 2564 * @extends EventEmitter 2565 * @license Apache-2.0 2566 * @since 3.0.0 2567 */ 2568 class OutputChannel extends EventEmitter { 2569 /** 2570 * Creates an `OutputChannel` object. 2571 * 2572 * @param {Output} output The [`Output`](Output) this channel belongs to. 2573 * @param {number} number The MIDI channel number (`1` - `16`). 2574 */ 2575 constructor(output, number) { 2576 super(); 2577 2578 /** 2579 * @type {Output} 2580 * @private 2581 */ 2582 this._output = output; 2583 2584 /** 2585 * @type {number} 2586 * @private 2587 */ 2588 this._number = number; 2589 2590 /** 2591 * @type {number} 2592 * @private 2593 */ 2594 this._octaveOffset = 0; 2595 } 2596 2597 /** 2598 * Unlinks the MIDI subsystem, removes all listeners attached to the channel and nulls the channel 2599 * number. This method is mostly for internal use. It has not been prefixed with an underscore 2600 * since it is called by other objects such as the `Output` object. 2601 * 2602 * @private 2603 */ 2604 destroy() { 2605 this._output = null; 2606 this._number = null; 2607 this._octaveOffset = 0; 2608 this.removeListener(); 2609 } 2610 2611 /** 2612 * Sends a MIDI message on the MIDI output port. If no time is specified, the message will be 2613 * sent immediately. The message should be an array of 8-bit unsigned integers (`0` - `225`), 2614 * a 2615 * [`Uint8Array`]{@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array} 2616 * object or a [`Message`](Message) object. 2617 * 2618 * It is usually not necessary to use this method directly as you can use one of the simpler 2619 * helper methods such as [`playNote()`](#playNote), [`stopNote()`](#stopNote), 2620 * [`sendControlChange()`](#sendControlChange), etc. 2621 * 2622 * Details on the format of MIDI messages are available in the summary of 2623 * [MIDI messages]{@link https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message} 2624 * from the MIDI Manufacturers Association. 2625 * 2626 * @param message {number[]|Uint8Array|Message} A `Message` object, an array of 8-bit unsigned 2627 * integers or a `Uint8Array` object (not available in Node.js) containing the message bytes. 2628 * 2629 * @param {object} [options={}] 2630 * 2631 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 2632 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 2633 * positive number 2634 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 2635 * the operation will be scheduled for that time. The current time can be retrieved with 2636 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 2637 * operation will be carried out as soon as possible. 2638 * 2639 * @throws {RangeError} The first byte (status) must be an integer between 128 and 255. 2640 * 2641 * @throws {RangeError} Data bytes must be integers between 0 and 255. 2642 * 2643 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 2644 */ 2645 send(message, options = { 2646 time: 0 2647 }) { 2648 this.output.send(message, options); 2649 return this; 2650 } 2651 2652 /** 2653 * Sends a MIDI **key aftertouch** message at the scheduled time. This is a key-specific 2654 * aftertouch. For a channel-wide aftertouch message, use 2655 * [`sendChannelAftertouch()`]{@link #sendChannelAftertouch}. 2656 * 2657 * @param target {number|Note|string|number[]|Note[]|string[]} The note(s) for which you are sending 2658 * an aftertouch value. The notes can be specified by using a MIDI note number (`0` - `127`), a 2659 * [`Note`](Note) object, a note identifier (e.g. `C3`, `G#4`, `F-1`, `Db7`) or an array of the 2660 * previous types. When using a note identifier, octave range must be between `-1` and `9`. The 2661 * lowest note is `C-1` (MIDI note number `0`) and the highest note is `G9` (MIDI note number 2662 * `127`). 2663 * 2664 * When using a note identifier, the octave value will be offset by the local 2665 * [`octaveOffset`](#octaveOffset) and by 2666 * [`Output.octaveOffset`](Output#octaveOffset) and [`WebMidi.octaveOffset`](WebMidi#octaveOffset) 2667 * (if those values are not `0`). When using a key number, `octaveOffset` values are ignored. 2668 * 2669 * @param [pressure=0.5] {number} The pressure level (between `0` and `1`). An invalid pressure 2670 * value will silently trigger the default behaviour. If the `rawValue` option is set to `true`, 2671 * the pressure is defined by using an integer between `0` and `127`. 2672 * 2673 * @param {object} [options={}] 2674 * 2675 * @param {boolean} [options.rawValue=false] A boolean indicating whether the value should be 2676 * considered a float between `0` and `1.0` (default) or a raw integer between `0` and `127`. 2677 * 2678 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 2679 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 2680 * positive number 2681 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 2682 * the operation will be scheduled for that time. The current time can be retrieved with 2683 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 2684 * operation will be carried out as soon as possible. 2685 * 2686 * @return {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 2687 * 2688 * @throws RangeError Invalid key aftertouch value. 2689 */ 2690 sendKeyAftertouch(target, pressure, options = {}) { 2691 if (wm.validation) { 2692 // Legacy support 2693 if (options.useRawValue) options.rawValue = options.useRawValue; 2694 if (isNaN(parseFloat(pressure))) { 2695 throw new RangeError("Invalid key aftertouch value."); 2696 } 2697 if (options.rawValue) { 2698 if (!(pressure >= 0 && pressure <= 127 && Number.isInteger(pressure))) { 2699 throw new RangeError("Key aftertouch raw value must be an integer between 0 and 127."); 2700 } 2701 } else { 2702 if (!(pressure >= 0 && pressure <= 1)) { 2703 throw new RangeError("Key aftertouch value must be a float between 0 and 1."); 2704 } 2705 } 2706 } 2707 2708 // Normalize pressure to integer 2709 if (!options.rawValue) pressure = Utilities.fromFloatTo7Bit(pressure); 2710 2711 // Plot total offset 2712 const offset = wm.octaveOffset + this.output.octaveOffset + this.octaveOffset; 2713 2714 // Make sure we are dealing with an array 2715 if (!Array.isArray(target)) target = [target]; 2716 Utilities.buildNoteArray(target).forEach(n => { 2717 this.send([(Enumerations.CHANNEL_MESSAGES.keyaftertouch << 4) + (this.number - 1), n.getOffsetNumber(offset), pressure], { 2718 time: Utilities.toTimestamp(options.time) 2719 }); 2720 }); 2721 return this; 2722 } 2723 2724 /** 2725 * Sends a MIDI **control change** message to the channel at the scheduled time. The control 2726 * change message to send can be specified numerically (`0` to `127`) or by using one of the 2727 * following common names: 2728 * 2729 * | Number | Name | 2730 * |--------|-------------------------------| 2731 * | 0 |`bankselectcoarse` | 2732 * | 1 |`modulationwheelcoarse` | 2733 * | 2 |`breathcontrollercoarse` | 2734 * | 4 |`footcontrollercoarse` | 2735 * | 5 |`portamentotimecoarse` | 2736 * | 6 |`dataentrycoarse` | 2737 * | 7 |`volumecoarse` | 2738 * | 8 |`balancecoarse` | 2739 * | 10 |`pancoarse` | 2740 * | 11 |`expressioncoarse` | 2741 * | 12 |`effectcontrol1coarse` | 2742 * | 13 |`effectcontrol2coarse` | 2743 * | 18 |`generalpurposeslider3` | 2744 * | 19 |`generalpurposeslider4` | 2745 * | 32 |`bankselectfine` | 2746 * | 33 |`modulationwheelfine` | 2747 * | 34 |`breathcontrollerfine` | 2748 * | 36 |`footcontrollerfine` | 2749 * | 37 |`portamentotimefine` | 2750 * | 38 |`dataentryfine` | 2751 * | 39 |`volumefine` | 2752 * | 40 |`balancefine` | 2753 * | 42 |`panfine` | 2754 * | 43 |`expressionfine` | 2755 * | 44 |`effectcontrol1fine` | 2756 * | 45 |`effectcontrol2fine` | 2757 * | 64 |`holdpedal` | 2758 * | 65 |`portamento` | 2759 * | 66 |`sustenutopedal` | 2760 * | 67 |`softpedal` | 2761 * | 68 |`legatopedal` | 2762 * | 69 |`hold2pedal` | 2763 * | 70 |`soundvariation` | 2764 * | 71 |`resonance` | 2765 * | 72 |`soundreleasetime` | 2766 * | 73 |`soundattacktime` | 2767 * | 74 |`brightness` | 2768 * | 75 |`soundcontrol6` | 2769 * | 76 |`soundcontrol7` | 2770 * | 77 |`soundcontrol8` | 2771 * | 78 |`soundcontrol9` | 2772 * | 79 |`soundcontrol10` | 2773 * | 80 |`generalpurposebutton1` | 2774 * | 81 |`generalpurposebutton2` | 2775 * | 82 |`generalpurposebutton3` | 2776 * | 83 |`generalpurposebutton4` | 2777 * | 91 |`reverblevel` | 2778 * | 92 |`tremololevel` | 2779 * | 93 |`choruslevel` | 2780 * | 94 |`celestelevel` | 2781 * | 95 |`phaserlevel` | 2782 * | 96 |`dataincrement` | 2783 * | 97 |`datadecrement` | 2784 * | 98 |`nonregisteredparametercoarse` | 2785 * | 99 |`nonregisteredparameterfine` | 2786 * | 100 |`registeredparametercoarse` | 2787 * | 101 |`registeredparameterfine` | 2788 * | 120 |`allsoundoff` | 2789 * | 121 |`resetallcontrollers` | 2790 * | 122 |`localcontrol` | 2791 * | 123 |`allnotesoff` | 2792 * | 124 |`omnimodeoff` | 2793 * | 125 |`omnimodeon` | 2794 * | 126 |`monomodeon` | 2795 * | 127 |`polymodeon` | 2796 * 2797 * As you can see above, not all control change message have a matching name. This does not mean 2798 * you cannot use the others. It simply means you will need to use their number 2799 * (`0` to `127`) instead of their name. While you can still use them, numbers `120` to `127` are 2800 * usually reserved for *channel mode* messages. See 2801 * [`sendChannelMode()`]{@link OutputChannel#sendChannelMode} method for more info. 2802 * 2803 * To view a detailed list of all available **control change** messages, please consult "Table 3 - 2804 * Control Change Messages" from the [MIDI Messages]( 2805 * https://www.midi.org/specifications/item/table-3-control-change-messages-data-bytes-2) 2806 * specification. 2807 * 2808 * **Note**: messages #0-31 (MSB) are paired with messages #32-63 (LSB). For example, message #1 2809 * (`modulationwheelcoarse`) can be accompanied by a second control change message for 2810 * `modulationwheelfine` to achieve a greater level of precision. if you want to specify both MSB 2811 * and LSB for messages between `0` and `31`, you can do so by passing a 2-value array as the 2812 * second parameter. 2813 * 2814 * @param {number|string} controller The MIDI controller name or number (`0` - `127`). 2815 * 2816 * @param {number|number[]} value The value to send (0-127). You can also use a two-position array 2817 * for controllers 0 to 31. In this scenario, the first value will be sent as usual and the second 2818 * value will be sent to the matching LSB controller (which is obtained by adding 32 to the first 2819 * controller) 2820 * 2821 * @param {object} [options={}] 2822 * 2823 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 2824 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 2825 * positive number 2826 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 2827 * the operation will be scheduled for that time. The current time can be retrieved with 2828 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 2829 * operation will be carried out as soon as possible. 2830 * 2831 * @throws {RangeError} Controller numbers must be between 0 and 127. 2832 * @throws {RangeError} Invalid controller name. 2833 * @throws {TypeError} The value array must have a length of 2. 2834 * 2835 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 2836 * 2837 * @license Apache-2.0 2838 * @since 3.0.0 2839 */ 2840 sendControlChange(controller, value, options = {}) { 2841 if (typeof controller === "string") { 2842 controller = Utilities.getCcNumberByName(controller); 2843 } 2844 if (!Array.isArray(value)) value = [value]; 2845 if (wm.validation) { 2846 if (controller === undefined) { 2847 throw new TypeError("Control change must be identified with a valid name or an integer between 0 and 127."); 2848 } 2849 if (!Number.isInteger(controller) || !(controller >= 0 && controller <= 127)) { 2850 throw new TypeError("Control change number must be an integer between 0 and 127."); 2851 } 2852 value = value.map(item => { 2853 const output = Math.min(Math.max(parseInt(item), 0), 127); 2854 if (isNaN(output)) throw new TypeError("Values must be integers between 0 and 127"); 2855 return output; 2856 }); 2857 if (value.length === 2 && controller >= 32) { 2858 throw new TypeError("To use a value array, the controller must be between 0 and 31"); 2859 } 2860 } 2861 value.forEach((item, index) => { 2862 this.send([(Enumerations.CHANNEL_MESSAGES.controlchange << 4) + (this.number - 1), controller + index * 32, value[index]], { 2863 time: Utilities.toTimestamp(options.time) 2864 }); 2865 }); 2866 return this; 2867 } 2868 2869 /** 2870 * Selects a MIDI non-registered parameter so it is affected by upcoming data entry, data 2871 * increment and data decrement messages. 2872 * 2873 * @param parameter {number[]} A two-position array specifying the two control bytes that identify 2874 * the registered parameter. The NRPN MSB (99 or 0x63) is a position 0. The NRPN LSB (98 or 0x62) 2875 * is at position 1. 2876 * 2877 * @private 2878 * 2879 * @param {object} [options={}] 2880 * 2881 * @param {number|string} [options.time] If `time` is a string prefixed with `"+"` and followed by 2882 * a number, the message will be delayed by that many milliseconds. If the value is a number, the 2883 * operation will be scheduled for that time. The current time can be retrieved with 2884 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 2885 * operation will be carried out as soon as possible. 2886 * 2887 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 2888 */ 2889 _selectNonRegisteredParameter(parameter, options = {}) { 2890 // parameter[0] = Math.floor(parameter[0]); 2891 // if (!(parameter[0] >= 0 && parameter[0] <= 127)) { 2892 // throw new RangeError("The control63 value must be between 0 and 127."); 2893 // } 2894 // 2895 // parameter[1] = Math.floor(parameter[1]); 2896 // if (!(parameter[1] >= 0 && parameter[1] <= 127)) { 2897 // throw new RangeError("The control62 value must be between 0 and 127."); 2898 // } 2899 2900 this.sendControlChange(0x63, parameter[0], options); 2901 this.sendControlChange(0x62, parameter[1], options); 2902 return this; 2903 } 2904 2905 /** 2906 * Deselects the currently active MIDI registered parameter so it is no longer affected by data 2907 * entry, data increment and data decrement messages. 2908 * 2909 * Current best practice recommends doing that after each call to 2910 * [_setCurrentParameter()]{@link #_setCurrentParameter}. 2911 * 2912 * @private 2913 * 2914 * @param {object} [options={}] 2915 * 2916 * @param {number|string} [options.time] If `time` is a string prefixed with `"+"` and followed by 2917 * a number, the message will be delayed by that many milliseconds. If the value is a number, the 2918 * operation will be scheduled for that time. The current time can be retrieved with 2919 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 2920 * operation will be carried out as soon as possible. 2921 * 2922 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 2923 */ 2924 _deselectRegisteredParameter(options = {}) { 2925 this.sendControlChange(0x65, 0x7F, options); 2926 this.sendControlChange(0x64, 0x7F, options); 2927 return this; 2928 } 2929 2930 /** 2931 * Deselects the currently active MIDI non-registered parameter so it is no longer affected by 2932 * data entry, data increment and data decrement messages. 2933 * 2934 * @private 2935 * 2936 * @param {object} [options={}] 2937 * 2938 * @param {number|string} [options.time] If `time` is a string prefixed with `"+"` and followed by 2939 * a number, the message will be delayed by that many milliseconds. If the value is a number, the 2940 * operation will be scheduled for that time. The current time can be retrieved with 2941 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 2942 * operation will be carried out as soon as possible. 2943 * 2944 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 2945 */ 2946 _deselectNonRegisteredParameter(options = {}) { 2947 this.sendControlChange(0x65, 0x7F, options); 2948 this.sendControlChange(0x64, 0x7F, options); 2949 return this; 2950 } 2951 2952 /** 2953 * Selects a MIDI registered parameter so it is affected by upcoming data entry, data increment 2954 * and data decrement messages. 2955 * 2956 * @private 2957 * 2958 * @param parameter {number[]} A two-position array of integers specifying the two control bytes 2959 * (0x65, 0x64) that identify the registered parameter. The integers must be between 0 and 127. 2960 * 2961 * @param {object} [options={}] 2962 * 2963 * @param {number|string} [options.time] If `time` is a string prefixed with `"+"` and followed by 2964 * a number, the message will be delayed by that many milliseconds. If the value is a number, the 2965 * operation will be scheduled for that time. The current time can be retrieved with 2966 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 2967 * operation will be carried out as soon as possible. 2968 * 2969 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 2970 */ 2971 _selectRegisteredParameter(parameter, options = {}) { 2972 this.sendControlChange(0x65, parameter[0], options); 2973 this.sendControlChange(0x64, parameter[1], options); 2974 return this; 2975 } 2976 2977 /** 2978 * Sets the value of the currently selected MIDI registered parameter. 2979 * 2980 * @private 2981 * 2982 * @param data {number|number[]} 2983 * 2984 * @param {object} [options={}] 2985 * 2986 * @param {number|string} [options.time] If `time` is a string prefixed with `"+"` and followed by 2987 * a number, the message will be delayed by that many milliseconds. If the value is a number, the 2988 * operation will be scheduled for that time. The current time can be retrieved with 2989 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 2990 * operation will be carried out as soon as possible. 2991 * 2992 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 2993 */ 2994 _setCurrentParameter(data, options = {}) { 2995 data = [].concat(data); 2996 2997 // MSB 2998 // data[0] = parseInt(data[0]); 2999 // if (!isNaN(data[0]) && data[0] >= 0 && data[0] <= 127) { 3000 this.sendControlChange(0x06, data[0], options); 3001 // } else { 3002 // throw new RangeError("The msb value must be between 0 and 127."); 3003 // } 3004 3005 if (data.length < 2) return this; 3006 3007 // LSB 3008 // data[1] = parseInt(data[1]); 3009 3010 // if (!isNaN(data[1]) && data[1] >= 0 && data[1] <= 127) { 3011 this.sendControlChange(0x26, data[1], options); 3012 // } else { 3013 // throw new RangeError("The lsb value must be between 0 and 127."); 3014 // } 3015 3016 return this; 3017 } 3018 3019 /** 3020 * Decrements the specified MIDI registered parameter by 1. Here is the full list of parameter 3021 * names that can be used with this function: 3022 * 3023 * * Pitchbend Range (0x00, 0x00): `"pitchbendrange"` 3024 * * Channel Fine Tuning (0x00, 0x01): `"channelfinetuning"` 3025 * * Channel Coarse Tuning (0x00, 0x02): `"channelcoarsetuning"` 3026 * * Tuning Program (0x00, 0x03): `"tuningprogram"` 3027 * * Tuning Bank (0x00, 0x04): `"tuningbank"` 3028 * * Modulation Range (0x00, 0x05): `"modulationrange"` 3029 * * Azimuth Angle (0x3D, 0x00): `"azimuthangle"` 3030 * * Elevation Angle (0x3D, 0x01): `"elevationangle"` 3031 * * Gain (0x3D, 0x02): `"gain"` 3032 * * Distance Ratio (0x3D, 0x03): `"distanceratio"` 3033 * * Maximum Distance (0x3D, 0x04): `"maximumdistance"` 3034 * * Maximum Distance Gain (0x3D, 0x05): `"maximumdistancegain"` 3035 * * Reference Distance Ratio (0x3D, 0x06): `"referencedistanceratio"` 3036 * * Pan Spread Angle (0x3D, 0x07): `"panspreadangle"` 3037 * * Roll Angle (0x3D, 0x08): `"rollangle"` 3038 * 3039 * @param parameter {String|number[]} A string identifying the parameter's name (see above) or a 3040 * two-position array specifying the two control bytes (0x65, 0x64) that identify the registered 3041 * parameter. 3042 * 3043 * @param {object} [options={}] 3044 * 3045 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3046 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3047 * positive number 3048 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3049 * the operation will be scheduled for that time. The current time can be retrieved with 3050 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3051 * operation will be carried out as soon as possible. 3052 * 3053 * @throws TypeError The specified registered parameter is invalid. 3054 * 3055 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 3056 */ 3057 sendRpnDecrement(parameter, options = {}) { 3058 if (!Array.isArray(parameter)) parameter = Enumerations.REGISTERED_PARAMETERS[parameter]; 3059 if (wm.validation) { 3060 if (parameter === undefined) { 3061 throw new TypeError("The specified registered parameter is invalid."); 3062 } 3063 let valid = false; 3064 Object.getOwnPropertyNames(Enumerations.REGISTERED_PARAMETERS).forEach(p => { 3065 if (Enumerations.REGISTERED_PARAMETERS[p][0] === parameter[0] && Enumerations.REGISTERED_PARAMETERS[p][1] === parameter[1]) { 3066 valid = true; 3067 } 3068 }); 3069 if (!valid) throw new TypeError("The specified registered parameter is invalid."); 3070 } 3071 this._selectRegisteredParameter(parameter, options); 3072 this.sendControlChange(0x61, 0, options); 3073 this._deselectRegisteredParameter(options); 3074 return this; 3075 } 3076 3077 /** 3078 * Increments the specified MIDI registered parameter by 1. Here is the full list of parameter 3079 * names that can be used with this function: 3080 * 3081 * * Pitchbend Range (0x00, 0x00): `"pitchbendrange"` 3082 * * Channel Fine Tuning (0x00, 0x01): `"channelfinetuning"` 3083 * * Channel Coarse Tuning (0x00, 0x02): `"channelcoarsetuning"` 3084 * * Tuning Program (0x00, 0x03): `"tuningprogram"` 3085 * * Tuning Bank (0x00, 0x04): `"tuningbank"` 3086 * * Modulation Range (0x00, 0x05): `"modulationrange"` 3087 * * Azimuth Angle (0x3D, 0x00): `"azimuthangle"` 3088 * * Elevation Angle (0x3D, 0x01): `"elevationangle"` 3089 * * Gain (0x3D, 0x02): `"gain"` 3090 * * Distance Ratio (0x3D, 0x03): `"distanceratio"` 3091 * * Maximum Distance (0x3D, 0x04): `"maximumdistance"` 3092 * * Maximum Distance Gain (0x3D, 0x05): `"maximumdistancegain"` 3093 * * Reference Distance Ratio (0x3D, 0x06): `"referencedistanceratio"` 3094 * * Pan Spread Angle (0x3D, 0x07): `"panspreadangle"` 3095 * * Roll Angle (0x3D, 0x08): `"rollangle"` 3096 * 3097 * @param parameter {String|number[]} A string identifying the parameter's name (see above) or a 3098 * two-position array specifying the two control bytes (0x65, 0x64) that identify the registered 3099 * parameter. 3100 * 3101 * @param {object} [options={}] 3102 * 3103 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3104 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3105 * positive number 3106 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3107 * the operation will be scheduled for that time. The current time can be retrieved with 3108 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3109 * operation will be carried out as soon as possible. 3110 * 3111 * @throws TypeError The specified registered parameter is invalid. 3112 * 3113 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 3114 */ 3115 sendRpnIncrement(parameter, options = {}) { 3116 if (!Array.isArray(parameter)) parameter = Enumerations.REGISTERED_PARAMETERS[parameter]; 3117 if (wm.validation) { 3118 if (parameter === undefined) { 3119 throw new TypeError("The specified registered parameter is invalid."); 3120 } 3121 let valid = false; 3122 Object.getOwnPropertyNames(Enumerations.REGISTERED_PARAMETERS).forEach(p => { 3123 if (Enumerations.REGISTERED_PARAMETERS[p][0] === parameter[0] && Enumerations.REGISTERED_PARAMETERS[p][1] === parameter[1]) { 3124 valid = true; 3125 } 3126 }); 3127 if (!valid) throw new TypeError("The specified registered parameter is invalid."); 3128 } 3129 this._selectRegisteredParameter(parameter, options); 3130 this.sendControlChange(0x60, 0, options); 3131 this._deselectRegisteredParameter(options); 3132 return this; 3133 } 3134 3135 /** 3136 * Plays a note or an array of notes on the channel. The first parameter is the note to play. It 3137 * can be a single value or an array of the following valid values: 3138 * 3139 * - A [`Note`]{@link Note} object 3140 * - A MIDI note number (integer between `0` and `127`) 3141 * - A note name, followed by the octave (e.g. `"C3"`, `"G#4"`, `"F-1"`, `"Db7"`) 3142 * 3143 * The `playNote()` method sends a **note on** MIDI message for all specified notes. If a 3144 * `duration` is set in the `options` parameter or in the [`Note`]{@link Note} object's 3145 * [`duration`]{@link Note#duration} property, it will also schedule a **note off** message 3146 * to end the note after said duration. If no `duration` is set, the note will simply play until 3147 * a matching **note off** message is sent with [`stopNote()`]{@link OutputChannel#stopNote} or 3148 * [`sendNoteOff()`]{@link OutputChannel#sendNoteOff}. 3149 * 3150 * The execution of the **note on** command can be delayed by using the `time` property of the 3151 * `options` parameter. 3152 * 3153 * When using [`Note`]{@link Note} objects, the durations and velocities defined in the 3154 * [`Note`]{@link Note} objects have precedence over the ones specified via the method's `options` 3155 * parameter. 3156 * 3157 * **Note**: per the MIDI standard, a **note on** message with an attack velocity of `0` is 3158 * functionally equivalent to a **note off** message. 3159 * 3160 * @param note {number|string|Note|number[]|string[]|Note[]} The note(s) to play. The notes can be 3161 * specified by using a MIDI note number (`0` - `127`), a note identifier (e.g. `C3`, `G#4`, 3162 * `F-1`, `Db7`), a [`Note`]{@link Note} object or an array of the previous types. When using a 3163 * note identifier, the octave range must be between `-1` and `9`. The lowest note is `C-1` (MIDI 3164 * note number `0`) and the highest note is `G9` (MIDI note number `127`). 3165 * 3166 * @param {object} [options={}] 3167 * 3168 * @param {number} [options.duration] A positive decimal number larger than `0` representing the 3169 * number of milliseconds to wait before sending a **note off** message. If invalid or left 3170 * undefined, only a **note on** message will be sent. 3171 * 3172 * @param {number} [options.attack=0.5] The velocity at which to play the note (between `0` and 3173 * `1`). If the `rawAttack` option is also defined, it will have priority. An invalid velocity 3174 * value will silently trigger the default of `0.5`. 3175 * 3176 * @param {number} [options.rawAttack=64] The attack velocity at which to play the note (between 3177 * `0` and `127`). This has priority over the `attack` property. An invalid velocity value will 3178 * silently trigger the default of 64. 3179 * 3180 * @param {number} [options.release=0.5] The velocity at which to release the note (between `0` 3181 * and `1`). If the `rawRelease` option is also defined, it will have priority. An invalid 3182 * velocity value will silently trigger the default of `0.5`. This is only used with the 3183 * **note off** event triggered when `options.duration` is set. 3184 * 3185 * @param {number} [options.rawRelease=64] The velocity at which to release the note (between `0` 3186 * and `127`). This has priority over the `release` property. An invalid velocity value will 3187 * silently trigger the default of 64. This is only used with the **note off** event triggered 3188 * when `options.duration` is set. 3189 * 3190 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3191 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3192 * positive number 3193 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3194 * the operation will be scheduled for that time. The current time can be retrieved with 3195 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3196 * operation will be carried out as soon as possible. 3197 * 3198 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 3199 */ 3200 playNote(note, options = {}) { 3201 // Send note on and, optionally, note off message (if duration is a positive number) 3202 this.sendNoteOn(note, options); 3203 const notes = Array.isArray(note) ? note : [note]; 3204 for (let note of notes) { 3205 if (parseInt(note.duration) > 0) { 3206 const noteOffOptions = { 3207 time: (Utilities.toTimestamp(options.time) || wm.time) + parseInt(note.duration), 3208 release: note.release, 3209 rawRelease: note.rawRelease 3210 }; 3211 this.sendNoteOff(note, noteOffOptions); 3212 } else if (parseInt(options.duration) > 0) { 3213 const noteOffOptions = { 3214 time: (Utilities.toTimestamp(options.time) || wm.time) + parseInt(options.duration), 3215 release: options.release, 3216 rawRelease: options.rawRelease 3217 }; 3218 this.sendNoteOff(note, noteOffOptions); 3219 } 3220 } 3221 return this; 3222 } 3223 3224 /** 3225 * Sends a **note off** message for the specified notes on the channel. The first parameter is the 3226 * note. It can be a single value or an array of the following valid values: 3227 * 3228 * - A MIDI note number (integer between `0` and `127`) 3229 * - A note name, followed by the octave (e.g. `"C3"`, `"G#4"`, `"F-1"`, `"Db7"`) 3230 * - A [`Note`]{@link Note} object 3231 * 3232 * The execution of the **note off** command can be delayed by using the `time` property of the 3233 * `options` parameter. 3234 * 3235 * When using [`Note`]{@link Note} objects, the release velocity defined in the 3236 * [`Note`]{@link Note} objects has precedence over the one specified via the method's `options` 3237 * parameter. 3238 * 3239 * @param note {number|string|Note|number[]|string[]|Note[]} The note(s) to stop. The notes can be 3240 * specified by using a MIDI note number (0-127), a note identifier (e.g. C3, G#4, F-1, Db7), a 3241 * [`Note`]{@link Note} object or an array of the previous types. When using a note name, octave 3242 * range must be between -1 and 9. The lowest note is C-1 (MIDI note number 0) and the highest 3243 * note is G9 (MIDI note number 127). 3244 * 3245 * @param {object} [options={}] 3246 * 3247 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3248 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3249 * positive number 3250 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3251 * the operation will be scheduled for that time. The current time can be retrieved with 3252 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3253 * operation will be carried out as soon as possible. 3254 * 3255 * @param {number} [options.release=0.5] The velocity at which to release the note 3256 * (between `0` and `1`). If the `rawRelease` option is also defined, `rawRelease` will have 3257 * priority. An invalid velocity value will silently trigger the default of `0.5`. 3258 * 3259 * @param {number} [options.rawRelease=64] The velocity at which to release the note 3260 * (between `0` and `127`). If the `release` option is also defined, `rawRelease` will have 3261 * priority. An invalid velocity value will silently trigger the default of `64`. 3262 * 3263 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 3264 */ 3265 sendNoteOff(note, options = {}) { 3266 if (wm.validation) { 3267 if (options.rawRelease != undefined && !(options.rawRelease >= 0 && options.rawRelease <= 127)) { 3268 throw new RangeError("The 'rawRelease' option must be an integer between 0 and 127"); 3269 } 3270 if (options.release != undefined && !(options.release >= 0 && options.release <= 1)) { 3271 throw new RangeError("The 'release' option must be an number between 0 and 1"); 3272 } 3273 3274 // Legacy compatibility warnings 3275 if (options.rawVelocity) { 3276 options.rawRelease = options.velocity; 3277 console.warn("The 'rawVelocity' option is deprecated. Use 'rawRelease' instead."); 3278 } 3279 if (options.velocity) { 3280 options.release = options.velocity; 3281 console.warn("The 'velocity' option is deprecated. Use 'attack' instead."); 3282 } 3283 } 3284 let nVelocity = 64; 3285 if (options.rawRelease != undefined) { 3286 nVelocity = options.rawRelease; 3287 } else { 3288 if (!isNaN(options.release)) nVelocity = Math.round(options.release * 127); 3289 } 3290 3291 // Plot total octave offset 3292 const offset = wm.octaveOffset + this.output.octaveOffset + this.octaveOffset; 3293 Utilities.buildNoteArray(note, { 3294 rawRelease: parseInt(nVelocity) 3295 }).forEach(n => { 3296 this.send([(Enumerations.CHANNEL_MESSAGES.noteoff << 4) + (this.number - 1), n.getOffsetNumber(offset), n.rawRelease], { 3297 time: Utilities.toTimestamp(options.time) 3298 }); 3299 }); 3300 return this; 3301 } 3302 3303 /** 3304 * Sends a **note off** message for the specified MIDI note number. The first parameter is the 3305 * note to stop. It can be a single value or an array of the following valid values: 3306 * 3307 * - A MIDI note number (integer between `0` and `127`) 3308 * - A note identifier (e.g. `"C3"`, `"G#4"`, `"F-1"`, `"Db7"`) 3309 * - A [`Note`](Note) object 3310 * 3311 * The execution of the **note off** command can be delayed by using the `time` property of the 3312 * `options` parameter. 3313 * 3314 * @param note {number|Note|string|number[]|Note[]|string[]} The note(s) to stop. The notes can be 3315 * specified by using a MIDI note number (`0` - `127`), a note identifier (e.g. `C3`, `G#4`, `F-1`, 3316 * `Db7`) or an array of the previous types. When using a note identifier, octave range must be 3317 * between `-1` and `9`. The lowest note is `C-1` (MIDI note number `0`) and the highest note is 3318 * `G9` (MIDI note number `127`). 3319 * 3320 * @param {Object} [options={}] 3321 * 3322 * @param {number} [options.release=0.5] The velocity at which to release the note 3323 * (between `0` and `1`). If the `rawRelease` option is also defined, `rawRelease` will have 3324 * priority. An invalid velocity value will silently trigger the default of `0.5`. 3325 * 3326 * @param {number} [options.rawRelease=64] The velocity at which to release the note 3327 * (between `0` and `127`). If the `release` option is also defined, `rawRelease` will have 3328 * priority. An invalid velocity value will silently trigger the default of `64`. 3329 * 3330 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3331 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3332 * positive number 3333 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3334 * the operation will be scheduled for that time. The current time can be retrieved with 3335 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3336 * operation will be carried out as soon as possible. 3337 * 3338 * @returns {Output} Returns the `Output` object so methods can be chained. 3339 */ 3340 stopNote(note, options = {}) { 3341 return this.sendNoteOff(note, options); 3342 } 3343 3344 /** 3345 * Sends a **note on** message for the specified note(s) on the channel. The first parameter is 3346 * the note. It can be a single value or an array of the following valid values: 3347 * 3348 * - A [`Note`]{@link Note} object 3349 * - A MIDI note number (integer between `0` and `127`) 3350 * - A note identifier (e.g. `"C3"`, `"G#4"`, `"F-1"`, `"Db7"`) 3351 * 3352 * When passing a [`Note`]{@link Note}object or a note name, the `octaveOffset` will be applied. 3353 * This is not the case when using a note number. In this case, we assume you know exactly which 3354 * MIDI note number should be sent out. 3355 * 3356 * The execution of the **note on** command can be delayed by using the `time` property of the 3357 * `options` parameter. 3358 * 3359 * When using [`Note`]{@link Note} objects, the attack velocity defined in the 3360 * [`Note`]{@link Note} objects has precedence over the one specified via the method's `options` 3361 * parameter. Also, the `duration` is ignored. If you want to also send a **note off** message, 3362 * use the [`playNote()`]{@link #playNote} method instead. 3363 * 3364 * **Note**: As per the MIDI standard, a **note on** message with an attack velocity of `0` is 3365 * functionally equivalent to a **note off** message. 3366 * 3367 * @param note {number|string|Note|number[]|string[]|Note[]} The note(s) to play. The notes can be 3368 * specified by using a MIDI note number (0-127), a note identifier (e.g. C3, G#4, F-1, Db7), a 3369 * [`Note`]{@link Note} object or an array of the previous types. 3370 * 3371 * @param {object} [options={}] 3372 * 3373 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3374 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3375 * positive number 3376 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3377 * the operation will be scheduled for that time. The current time can be retrieved with 3378 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3379 * operation will be carried out as soon as possible. 3380 * 3381 * @param {number} [options.attack=0.5] The velocity at which to play the note (between `0` and 3382 * `1`). If the `rawAttack` option is also defined, `rawAttack` will have priority. An invalid 3383 * velocity value will silently trigger the default of `0.5`. 3384 * 3385 * @param {number} [options.rawAttack=64] The velocity at which to release the note (between `0` 3386 * and `127`). If the `attack` option is also defined, `rawAttack` will have priority. An invalid 3387 * velocity value will silently trigger the default of `64`. 3388 * 3389 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 3390 */ 3391 sendNoteOn(note, options = {}) { 3392 if (wm.validation) { 3393 if (options.rawAttack != undefined && !(options.rawAttack >= 0 && options.rawAttack <= 127)) { 3394 throw new RangeError("The 'rawAttack' option must be an integer between 0 and 127"); 3395 } 3396 if (options.attack != undefined && !(options.attack >= 0 && options.attack <= 1)) { 3397 throw new RangeError("The 'attack' option must be an number between 0 and 1"); 3398 } 3399 3400 // Legacy compatibility warnings 3401 if (options.rawVelocity) { 3402 options.rawAttack = options.velocity; 3403 options.rawRelease = options.release; 3404 console.warn("The 'rawVelocity' option is deprecated. Use 'rawAttack' or 'rawRelease'."); 3405 } 3406 if (options.velocity) { 3407 options.attack = options.velocity; 3408 console.warn("The 'velocity' option is deprecated. Use 'attack' instead."); 3409 } 3410 } 3411 let nVelocity = 64; 3412 if (options.rawAttack != undefined) { 3413 nVelocity = options.rawAttack; 3414 } else { 3415 if (!isNaN(options.attack)) nVelocity = Math.round(options.attack * 127); 3416 } 3417 3418 // Plot total octave offset 3419 const offset = wm.octaveOffset + this.output.octaveOffset + this.octaveOffset; 3420 Utilities.buildNoteArray(note, { 3421 rawAttack: nVelocity 3422 }).forEach(n => { 3423 this.send([(Enumerations.CHANNEL_MESSAGES.noteon << 4) + (this.number - 1), n.getOffsetNumber(offset), n.rawAttack], { 3424 time: Utilities.toTimestamp(options.time) 3425 }); 3426 }); 3427 return this; 3428 } 3429 3430 /** 3431 * Sends a MIDI **channel mode** message. The channel mode message to send can be specified 3432 * numerically or by using one of the following common names: 3433 * 3434 * | Type |Number| Shortcut Method | 3435 * | ---------------------|------|-------------------------------------------------------------- | 3436 * | `allsoundoff` | 120 | [`sendAllSoundOff()`]{@link #sendAllSoundOff} | 3437 * | `resetallcontrollers`| 121 | [`sendResetAllControllers()`]{@link #sendResetAllControllers} | 3438 * | `localcontrol` | 122 | [`sendLocalControl()`]{@link #sendLocalControl} | 3439 * | `allnotesoff` | 123 | [`sendAllNotesOff()`]{@link #sendAllNotesOff} | 3440 * | `omnimodeoff` | 124 | [`sendOmniMode(false)`]{@link #sendOmniMode} | 3441 * | `omnimodeon` | 125 | [`sendOmniMode(true)`]{@link #sendOmniMode} | 3442 * | `monomodeon` | 126 | [`sendPolyphonicMode("mono")`]{@link #sendPolyphonicMode} | 3443 * | `polymodeon` | 127 | [`sendPolyphonicMode("poly")`]{@link #sendPolyphonicMode} | 3444 * 3445 * **Note**: as you can see above, to make it easier, all channel mode messages also have a matching 3446 * helper method. 3447 * 3448 * It should be noted that, per the MIDI specification, only `localcontrol` and `monomodeon` may 3449 * require a value that's not zero. For that reason, the `value` parameter is optional and 3450 * defaults to 0. 3451 * 3452 * @param {number|string} command The numerical identifier of the channel mode message (integer 3453 * between `120` and `127`) or its name as a string. 3454 * 3455 * @param {number} [value=0] The value to send (integer between `0` - `127`). 3456 * 3457 * @param {object} [options={}] 3458 * 3459 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3460 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3461 * positive number 3462 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3463 * the operation will be scheduled for that time. The current time can be retrieved with 3464 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3465 * operation will be carried out as soon as possible. 3466 * 3467 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 3468 */ 3469 sendChannelMode(command, value = 0, options = {}) { 3470 // Normalize command to integer 3471 if (typeof command === "string") command = Enumerations.CHANNEL_MODE_MESSAGES[command]; 3472 if (wm.validation) { 3473 if (command === undefined) { 3474 throw new TypeError("Invalid channel mode message name or number."); 3475 } 3476 if (isNaN(command) || !(command >= 120 && command <= 127)) { 3477 throw new TypeError("Invalid channel mode message number."); 3478 } 3479 if (isNaN(parseInt(value)) || value < 0 || value > 127) { 3480 throw new RangeError("Value must be an integer between 0 and 127."); 3481 } 3482 } 3483 this.send([(Enumerations.CHANNEL_MESSAGES.controlchange << 4) + (this.number - 1), command, value], { 3484 time: Utilities.toTimestamp(options.time) 3485 }); 3486 return this; 3487 } 3488 3489 /** 3490 * Sets OMNI mode to `"on"` or `"off"`. MIDI's OMNI mode causes the instrument to respond to 3491 * messages from all channels. 3492 * 3493 * It should be noted that support for OMNI mode is not as common as it used to be. 3494 * 3495 * @param [state=true] {boolean} Whether to activate OMNI mode (`true`) or not (`false`). 3496 * 3497 * @param {object} [options={}] 3498 * 3499 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3500 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3501 * positive number 3502 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3503 * the operation will be scheduled for that time. The current time can be retrieved with 3504 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3505 * operation will be carried out as soon as possible. 3506 * 3507 * @throws {TypeError} Invalid channel mode message name. 3508 * @throws {RangeError} Channel mode controller numbers must be between 120 and 127. 3509 * @throws {RangeError} Value must be an integer between 0 and 127. 3510 * 3511 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 3512 */ 3513 sendOmniMode(state, options = {}) { 3514 if (state === undefined || state) { 3515 this.sendChannelMode("omnimodeon", 0, options); 3516 } else { 3517 this.sendChannelMode("omnimodeoff", 0, options); 3518 } 3519 return this; 3520 } 3521 3522 /** 3523 * Sends a MIDI **channel aftertouch** message. For key-specific aftertouch, you should instead 3524 * use [`sendKeyAftertouch()`]{@link #sendKeyAftertouch}. 3525 * 3526 * @param [pressure] {number} The pressure level (between `0` and `1`). If the `rawValue` option 3527 * is set to `true`, the pressure can be defined by using an integer between `0` and `127`. 3528 * 3529 * @param {object} [options={}] 3530 * 3531 * @param {boolean} [options.rawValue=false] A boolean indicating whether the value should be 3532 * considered a float between `0` and `1.0` (default) or a raw integer between `0` and `127`. 3533 * 3534 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3535 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3536 * positive number 3537 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3538 * the operation will be scheduled for that time. The current time can be retrieved with 3539 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3540 * operation will be carried out as soon as possible. 3541 * 3542 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 3543 * 3544 * @throws RangeError Invalid channel aftertouch value. 3545 */ 3546 sendChannelAftertouch(pressure, options = {}) { 3547 if (wm.validation) { 3548 if (isNaN(parseFloat(pressure))) { 3549 throw new RangeError("Invalid channel aftertouch value."); 3550 } 3551 if (options.rawValue) { 3552 if (!(pressure >= 0 && pressure <= 127 && Number.isInteger(pressure))) { 3553 throw new RangeError("Channel aftertouch raw value must be an integer between 0 and 127."); 3554 } 3555 } else { 3556 if (!(pressure >= 0 && pressure <= 1)) { 3557 throw new RangeError("Channel aftertouch value must be a float between 0 and 1."); 3558 } 3559 } 3560 } 3561 3562 // Normalize pressure to integer 3563 if (!options.rawValue) pressure = Utilities.fromFloatTo7Bit(pressure); 3564 this.send([(Enumerations.CHANNEL_MESSAGES.channelaftertouch << 4) + (this.number - 1), Math.round(pressure)], { 3565 time: Utilities.toTimestamp(options.time) 3566 }); 3567 return this; 3568 } 3569 3570 /** 3571 * Sends a **master tuning** message. The value is decimal and must be larger than -65 semitones 3572 * and smaller than 64 semitones. 3573 * 3574 * Because of the way the MIDI specification works, the decimal portion of the value will be 3575 * encoded with a resolution of 14bit. The integer portion must be between -64 and 63 3576 * inclusively. This function actually generates two MIDI messages: a **Master Coarse Tuning** and 3577 * a **Master Fine Tuning** RPN messages. 3578 * 3579 * @param [value=0.0] {number} The desired decimal adjustment value in semitones (-65 < x < 64) 3580 * 3581 * @param {object} [options={}] 3582 * 3583 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3584 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3585 * positive number 3586 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3587 * the operation will be scheduled for that time. The current time can be retrieved with 3588 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3589 * operation will be carried out as soon as possible. 3590 * 3591 * @throws {RangeError} The value must be a decimal number between larger than -65 and smaller 3592 * than 64. 3593 * 3594 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 3595 */ 3596 sendMasterTuning(value, options = {}) { 3597 // @todo allow passing value as msb/lsb pair (the same as pitch bend range) 3598 3599 value = parseFloat(value) || 0.0; 3600 if (wm.validation) { 3601 if (!(value > -65 && value < 64)) { 3602 throw new RangeError("The value must be a decimal number larger than -65 and smaller than 64."); 3603 } 3604 } 3605 let coarse = Math.floor(value) + 64; 3606 let fine = value - Math.floor(value); 3607 3608 // Calculate MSB and LSB for fine adjustment (14bit resolution) 3609 fine = Math.round((fine + 1) / 2 * 16383); 3610 let msb = fine >> 7 & 0x7F; 3611 let lsb = fine & 0x7F; 3612 this.sendRpnValue("channelcoarsetuning", coarse, options); 3613 this.sendRpnValue("channelfinetuning", [msb, lsb], options); 3614 return this; 3615 } 3616 3617 /** 3618 * Sends a **modulation depth range** message to adjust the depth of the modulation wheel's range. 3619 * The range can be specified with the `semitones` parameter, the `cents` parameter or by 3620 * specifying both parameters at the same time. 3621 * 3622 * @param {number} semitones The desired adjustment value in semitones (integer between 0 and 3623 * 127). 3624 * 3625 * @param {number} [cents=0] The desired adjustment value in cents (integer between 0 and 127). 3626 * 3627 * @param {Object} [options={}] 3628 * 3629 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3630 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3631 * positive number 3632 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3633 * the operation will be scheduled for that time. The current time can be retrieved with 3634 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3635 * operation will be carried out as soon as possible. 3636 * 3637 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 3638 */ 3639 sendModulationRange(semitones, cents, options = {}) { 3640 // @todo allow passing value as msb/lsb pair (the same as pitch bend range) 3641 // when passing a single argument, semitones and cents shoud be combined 3642 3643 if (wm.validation) { 3644 if (!Number.isInteger(semitones) || !(semitones >= 0 && semitones <= 127)) { 3645 throw new RangeError("The semitones value must be an integer between 0 and 127."); 3646 } 3647 if (!(cents == undefined) && (!Number.isInteger(cents) || !(cents >= 0 && cents <= 127))) { 3648 throw new RangeError("If specified, the cents value must be an integer between 0 and 127."); 3649 } 3650 } 3651 3652 // Default value for cents 3653 if (!(cents >= 0 && cents <= 127)) cents = 0; 3654 this.sendRpnValue("modulationrange", [semitones, cents], options); 3655 return this; 3656 } 3657 3658 /** 3659 * Sets a non-registered parameter (NRPN) to the specified value. The NRPN is selected by passing 3660 * in a two-position array specifying the values of the two control bytes. The value is specified 3661 * by passing in a single integer (most cases) or an array of two integers. 3662 * 3663 * NRPNs are not standardized in any way. Each manufacturer is free to implement them any way 3664 * they see fit. For example, according to the Roland GS specification, you can control the 3665 * **vibrato rate** using NRPN (1, 8). Therefore, to set the **vibrato rate** value to **123** you 3666 * would use: 3667 * 3668 * ```js 3669 * WebMidi.outputs[0].channels[0].sendNrpnValue([1, 8], 123); 3670 * ``` 3671 * 3672 * In some rarer cases, you need to send two values with your NRPN messages. In such cases, you 3673 * would use a 2-position array. For example, for its **ClockBPM** parameter (2, 63), Novation 3674 * uses a 14-bit value that combines an MSB and an LSB (7-bit values). So, for example, if the 3675 * value to send was 10, you could use: 3676 * 3677 * ```js 3678 * WebMidi.outputs[0].channels[0].sendNrpnValue([2, 63], [0, 10]); 3679 * ``` 3680 * 3681 * For further implementation details, refer to the manufacturer's documentation. 3682 * 3683 * @param nrpn {number[]} A two-position array specifying the two control bytes (0x63, 3684 * 0x62) that identify the non-registered parameter. 3685 * 3686 * @param [data=[]] {number|number[]} An integer or an array of integers with a length of 1 or 2 3687 * specifying the desired data. 3688 * 3689 * @param {Object} [options={}] 3690 * 3691 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3692 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3693 * positive number 3694 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3695 * the operation will be scheduled for that time. The current time can be retrieved with 3696 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3697 * operation will be carried out as soon as possible. 3698 * 3699 * @throws {RangeError} The control value must be between 0 and 127. 3700 * @throws {RangeError} The msb value must be between 0 and 127 3701 * 3702 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 3703 */ 3704 sendNrpnValue(nrpn, data, options = {}) { 3705 data = [].concat(data); 3706 if (wm.validation) { 3707 if (!Array.isArray(nrpn) || !Number.isInteger(nrpn[0]) || !Number.isInteger(nrpn[1])) { 3708 throw new TypeError("The specified NRPN is invalid."); 3709 } 3710 if (!(nrpn[0] >= 0 && nrpn[0] <= 127)) { 3711 throw new RangeError("The first byte of the NRPN must be between 0 and 127."); 3712 } 3713 if (!(nrpn[1] >= 0 && nrpn[1] <= 127)) { 3714 throw new RangeError("The second byte of the NRPN must be between 0 and 127."); 3715 } 3716 data.forEach(value => { 3717 if (!(value >= 0 && value <= 127)) { 3718 throw new RangeError("The data bytes of the NRPN must be between 0 and 127."); 3719 } 3720 }); 3721 } 3722 this._selectNonRegisteredParameter(nrpn, options); 3723 this._setCurrentParameter(data, options); 3724 this._deselectNonRegisteredParameter(options); 3725 return this; 3726 } 3727 3728 /** 3729 * Sends a MIDI **pitch bend** message at the scheduled time. The resulting bend is relative to 3730 * the pitch bend range that has been defined. The range can be set with 3731 * [`sendPitchBendRange()`]{@link #sendPitchBendRange}. So, for example, if the pitch 3732 * bend range has been set to 12 semitones, using a bend value of -1 will bend the note 1 octave 3733 * below its nominal value. 3734 * 3735 * @param {number|number[]} [value] The intensity of the bend (between -1.0 and 1.0). A value of 3736 * zero means no bend. If the `rawValue` option is set to `true`, the intensity of the bend can be 3737 * defined by either using a single integer between 0 and 127 (MSB) or an array of two integers 3738 * between 0 and 127 representing, respectively, the MSB (most significant byte) and the LSB 3739 * (least significant byte). The MSB is expressed in semitones with `64` meaning no bend. A value 3740 * lower than `64` bends downwards while a value higher than `64` bends upwards. The LSB is 3741 * expressed in cents (1/100 of a semitone). An LSB of `64` also means no bend. 3742 * 3743 * @param {Object} [options={}] 3744 * 3745 * @param {boolean} [options.rawValue=false] A boolean indicating whether the value should be 3746 * considered as a float between -1.0 and 1.0 (default) or as raw integer between 0 and 127 (or 3747 * an array of 2 integers if using both MSB and LSB). 3748 * 3749 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3750 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3751 * positive number 3752 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3753 * the operation will be scheduled for that time. The current time can be retrieved with 3754 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3755 * operation will be carried out as soon as possible. 3756 * 3757 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 3758 */ 3759 sendPitchBend(value, options = {}) { 3760 // @todo standardize the way msb/lsb are passed in 3761 3762 if (wm.validation) { 3763 if (options.rawValue && Array.isArray(value)) { 3764 if (!(value[0] >= 0 && value[0] <= 127)) { 3765 throw new RangeError("The pitch bend MSB must be an integer between 0 and 127."); 3766 } 3767 if (!(value[1] >= 0 && value[1] <= 127)) { 3768 throw new RangeError("The pitch bend LSB must be an integer between 0 and 127."); 3769 } 3770 } else if (options.rawValue && !Array.isArray(value)) { 3771 if (!(value >= 0 && value <= 127)) { 3772 throw new RangeError("The pitch bend MSB must be an integer between 0 and 127."); 3773 } 3774 } else { 3775 if (isNaN(value) || value === null) { 3776 throw new RangeError("Invalid pitch bend value."); 3777 } 3778 if (!(value >= -1 && value <= 1)) { 3779 throw new RangeError("The pitch bend value must be a float between -1 and 1."); 3780 } 3781 } 3782 } 3783 let msb = 0; 3784 let lsb = 0; 3785 3786 // Calculate MSB and LSB for both scenarios 3787 if (options.rawValue && Array.isArray(value)) { 3788 msb = value[0]; 3789 lsb = value[1]; 3790 } else if (options.rawValue && !Array.isArray(value)) { 3791 msb = value; 3792 } else { 3793 const result = Utilities.fromFloatToMsbLsb((value + 1) / 2); // b/c value is -1 to 1 3794 msb = result.msb; 3795 lsb = result.lsb; 3796 } 3797 this.send([(Enumerations.CHANNEL_MESSAGES.pitchbend << 4) + (this.number - 1), lsb, msb], { 3798 time: Utilities.toTimestamp(options.time) 3799 }); 3800 return this; 3801 } 3802 3803 /** 3804 * Sends a **pitch bend range** message at the scheduled time to adjust the range used by the 3805 * pitch bend lever. The range is specified by using the `semitones` and `cents` parameters. For 3806 * example, setting the `semitones` parameter to `12` means that the pitch bend range will be 12 3807 * semitones above and below the nominal pitch. 3808 * 3809 * @param semitones {number} The desired adjustment value in semitones (between 0 and 127). While 3810 * nothing imposes that in the specification, it is very common for manufacturers to limit the 3811 * range to 2 octaves (-12 semitones to 12 semitones). 3812 * 3813 * @param [cents=0] {number} The desired adjustment value in cents (integer between 0-127). 3814 * 3815 * @param {Object} [options={}] 3816 * 3817 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3818 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3819 * positive number 3820 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3821 * the operation will be scheduled for that time. The current time can be retrieved with 3822 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3823 * operation will be carried out as soon as possible. 3824 * 3825 * @throws {RangeError} The semitones value must be an integer between 0 and 127. 3826 * @throws {RangeError} The cents value must be an integer between 0 and 127. 3827 * 3828 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 3829 */ 3830 sendPitchBendRange(semitones, cents, options = {}) { 3831 // @todo use single value as parameter or pair of msb/lsb 3832 3833 if (wm.validation) { 3834 if (!Number.isInteger(semitones) || !(semitones >= 0 && semitones <= 127)) { 3835 throw new RangeError("The semitones value must be an integer between 0 and 127."); 3836 } 3837 if (!Number.isInteger(cents) || !(cents >= 0 && cents <= 127)) { 3838 throw new RangeError("The cents value must be an integer between 0 and 127."); 3839 } 3840 } 3841 this.sendRpnValue("pitchbendrange", [semitones, cents], options); 3842 return this; 3843 } 3844 3845 /** 3846 * Sends a MIDI **program change** message at the scheduled time. 3847 * 3848 * @param [program=1] {number} The MIDI patch (program) number (integer between `0` and `127`). 3849 * 3850 * @param {Object} [options={}] 3851 * 3852 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3853 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3854 * positive number 3855 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3856 * the operation will be scheduled for that time. The current time can be retrieved with 3857 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3858 * operation will be carried out as soon as possible. 3859 * 3860 * @throws {TypeError} Failed to execute 'send' on 'MIDIOutput': The value at index 1 is greater 3861 * than 0xFF. 3862 * 3863 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 3864 * 3865 */ 3866 sendProgramChange(program, options = {}) { 3867 program = parseInt(program) || 0; 3868 if (wm.validation) { 3869 if (!(program >= 0 && program <= 127)) { 3870 throw new RangeError("The program number must be between 0 and 127."); 3871 } 3872 } 3873 this.send([(Enumerations.CHANNEL_MESSAGES.programchange << 4) + (this.number - 1), program], { 3874 time: Utilities.toTimestamp(options.time) 3875 }); 3876 return this; 3877 } 3878 3879 /** 3880 * Sets the specified MIDI registered parameter to the desired value. The value is defined with 3881 * up to two bytes of data (msb, lsb) that each can go from 0 to 127. 3882 * 3883 * MIDI 3884 * [registered parameters](https://www.midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2) 3885 * extend the original list of control change messages. The MIDI 1.0 specification lists only a 3886 * limited number of them: 3887 * 3888 * | Numbers | Function | 3889 * |--------------|--------------------------| 3890 * | (0x00, 0x00) | `pitchbendrange` | 3891 * | (0x00, 0x01) | `channelfinetuning` | 3892 * | (0x00, 0x02) | `channelcoarsetuning` | 3893 * | (0x00, 0x03) | `tuningprogram` | 3894 * | (0x00, 0x04) | `tuningbank` | 3895 * | (0x00, 0x05) | `modulationrange` | 3896 * | (0x3D, 0x00) | `azimuthangle` | 3897 * | (0x3D, 0x01) | `elevationangle` | 3898 * | (0x3D, 0x02) | `gain` | 3899 * | (0x3D, 0x03) | `distanceratio` | 3900 * | (0x3D, 0x04) | `maximumdistance` | 3901 * | (0x3D, 0x05) | `maximumdistancegain` | 3902 * | (0x3D, 0x06) | `referencedistanceratio` | 3903 * | (0x3D, 0x07) | `panspreadangle` | 3904 * | (0x3D, 0x08) | `rollangle` | 3905 * 3906 * Note that the **Tuning Program** and **Tuning Bank** parameters are part of the *MIDI Tuning 3907 * Standard*, which is not widely implemented. 3908 * 3909 * @param rpn {string|number[]} A string identifying the parameter's name (see above) or a 3910 * two-position array specifying the two control bytes (e.g. `[0x65, 0x64]`) that identify the 3911 * registered parameter. 3912 * 3913 * @param [data=[]] {number|number[]} An single integer or an array of integers with a maximum 3914 * length of 2 specifying the desired data. 3915 * 3916 * @param {Object} [options={}] 3917 * 3918 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3919 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3920 * positive number 3921 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3922 * the operation will be scheduled for that time. The current time can be retrieved with 3923 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3924 * operation will be carried out as soon as possible. 3925 * 3926 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 3927 */ 3928 sendRpnValue(rpn, data, options = {}) { 3929 if (!Array.isArray(rpn)) rpn = Enumerations.REGISTERED_PARAMETERS[rpn]; 3930 if (wm.validation) { 3931 if (!Number.isInteger(rpn[0]) || !Number.isInteger(rpn[1])) { 3932 throw new TypeError("The specified NRPN is invalid."); 3933 } 3934 if (!(rpn[0] >= 0 && rpn[0] <= 127)) { 3935 throw new RangeError("The first byte of the RPN must be between 0 and 127."); 3936 } 3937 if (!(rpn[1] >= 0 && rpn[1] <= 127)) { 3938 throw new RangeError("The second byte of the RPN must be between 0 and 127."); 3939 } 3940 [].concat(data).forEach(value => { 3941 if (!(value >= 0 && value <= 127)) { 3942 throw new RangeError("The data bytes of the RPN must be between 0 and 127."); 3943 } 3944 }); 3945 } 3946 this._selectRegisteredParameter(rpn, options); 3947 this._setCurrentParameter(data, options); 3948 this._deselectRegisteredParameter(options); 3949 return this; 3950 } 3951 3952 /** 3953 * Sets the MIDI tuning bank to use. Note that the **Tuning Bank** parameter is part of the 3954 * *MIDI Tuning Standard*, which is not widely implemented. 3955 * 3956 * @param value {number} The desired tuning bank (integer between `0` and `127`). 3957 * 3958 * @param {Object} [options={}] 3959 * 3960 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3961 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3962 * positive number 3963 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3964 * the operation will be scheduled for that time. The current time can be retrieved with 3965 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3966 * operation will be carried out as soon as possible. 3967 * 3968 * @throws {RangeError} The bank value must be between 0 and 127. 3969 * 3970 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 3971 */ 3972 sendTuningBank(value, options = {}) { 3973 if (wm.validation) { 3974 if (!Number.isInteger(value) || !(value >= 0 && value <= 127)) { 3975 throw new RangeError("The tuning bank number must be between 0 and 127."); 3976 } 3977 } 3978 this.sendRpnValue("tuningbank", value, options); 3979 return this; 3980 } 3981 3982 /** 3983 * Sets the MIDI tuning program to use. Note that the **Tuning Program** parameter is part of the 3984 * *MIDI Tuning Standard*, which is not widely implemented. 3985 * 3986 * @param value {number} The desired tuning program (integer between `0` and `127`). 3987 * 3988 * @param {Object} [options={}] 3989 * 3990 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 3991 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 3992 * positive number 3993 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 3994 * the operation will be scheduled for that time. The current time can be retrieved with 3995 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 3996 * operation will be carried out as soon as possible. 3997 * 3998 * @throws {RangeError} The program value must be between 0 and 127. 3999 * 4000 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 4001 */ 4002 sendTuningProgram(value, options = {}) { 4003 if (wm.validation) { 4004 if (!Number.isInteger(value) || !(value >= 0 && value <= 127)) { 4005 throw new RangeError("The tuning program number must be between 0 and 127."); 4006 } 4007 } 4008 this.sendRpnValue("tuningprogram", value, options); 4009 return this; 4010 } 4011 4012 /** 4013 * Turns local control on or off. Local control is usually enabled by default. If you disable it, 4014 * the instrument will no longer trigger its own sounds. It will only send the MIDI messages to 4015 * its out port. 4016 * 4017 * @param [state=false] {boolean} Whether to activate local control (`true`) or disable it 4018 * (`false`). 4019 * 4020 * @param {Object} [options={}] 4021 * 4022 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4023 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4024 * positive number 4025 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4026 * the operation will be scheduled for that time. The current time can be retrieved with 4027 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4028 * operation will be carried out as soon as possible. 4029 * 4030 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 4031 */ 4032 sendLocalControl(state, options = {}) { 4033 if (state) { 4034 return this.sendChannelMode("localcontrol", 127, options); 4035 } else { 4036 return this.sendChannelMode("localcontrol", 0, options); 4037 } 4038 } 4039 4040 /** 4041 * Sends an **all notes off** channel mode message. This will make all currently playing notes 4042 * fade out just as if their key had been released. This is different from the 4043 * [`sendAllSoundOff()`]{@link #sendAllSoundOff} method which mutes all sounds immediately. 4044 * 4045 * @param {Object} [options={}] 4046 * 4047 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4048 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4049 * positive number 4050 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4051 * the operation will be scheduled for that time. The current time can be retrieved with 4052 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4053 * operation will be carried out as soon as possible. 4054 * 4055 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 4056 */ 4057 sendAllNotesOff(options = {}) { 4058 return this.sendChannelMode("allnotesoff", 0, options); 4059 } 4060 4061 /** 4062 * Sends an **all sound off** channel mode message. This will silence all sounds playing on that 4063 * channel but will not prevent new sounds from being triggered. 4064 * 4065 * @param {Object} [options={}] 4066 * 4067 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4068 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4069 * positive number 4070 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4071 * the operation will be scheduled for that time. The current time can be retrieved with 4072 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4073 * operation will be carried out as soon as possible. 4074 * 4075 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 4076 */ 4077 sendAllSoundOff(options = {}) { 4078 return this.sendChannelMode("allsoundoff", 0, options); 4079 } 4080 4081 /** 4082 * Sends a **reset all controllers** channel mode message. This resets all controllers, such as 4083 * the pitch bend, to their default value. 4084 * 4085 * @param {Object} [options={}] 4086 * 4087 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4088 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4089 * positive number 4090 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4091 * the operation will be scheduled for that time. The current time can be retrieved with 4092 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4093 * operation will be carried out as soon as possible. 4094 * 4095 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 4096 */ 4097 sendResetAllControllers(options = {}) { 4098 return this.sendChannelMode("resetallcontrollers", 0, options); 4099 } 4100 4101 /** 4102 * Sets the polyphonic mode. In `"poly"` mode (usually the default), multiple notes can be played 4103 * and heard at the same time. In `"mono"` mode, only one note will be heard at once even if 4104 * multiple notes are being played. 4105 * 4106 * @param {string} [mode=poly] The mode to use: `"mono"` or `"poly"`. 4107 * 4108 * @param {Object} [options={}] 4109 * 4110 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4111 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4112 * positive number 4113 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4114 * the operation will be scheduled for that time. The current time can be retrieved with 4115 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4116 * operation will be carried out as soon as possible. 4117 * 4118 * @returns {OutputChannel} Returns the `OutputChannel` object so methods can be chained. 4119 */ 4120 sendPolyphonicMode(mode, options = {}) { 4121 if (mode === "mono") { 4122 return this.sendChannelMode("monomodeon", 0, options); 4123 } else { 4124 return this.sendChannelMode("polymodeon", 0, options); 4125 } 4126 } 4127 4128 /** 4129 * An integer to offset the reported octave of outgoing note-specific messages (`noteon`, 4130 * `noteoff` and `keyaftertouch`). By default, middle C (MIDI note number 60) is placed on the 4th 4131 * octave (C4). 4132 * 4133 * Note that this value is combined with the global offset value defined in 4134 * [`WebMidi.octaveOffset`](WebMidi#octaveOffset) and with the parent value defined in 4135 * [`Output.octaveOffset`]{@link Output#octaveOffset}. 4136 * 4137 * @type {number} 4138 * 4139 * @since 3.0 4140 */ 4141 get octaveOffset() { 4142 return this._octaveOffset; 4143 } 4144 set octaveOffset(value) { 4145 if (this.validation) { 4146 value = parseInt(value); 4147 if (isNaN(value)) throw new TypeError("The 'octaveOffset' property must be an integer."); 4148 } 4149 this._octaveOffset = value; 4150 } 4151 4152 /** 4153 * The parent [`Output`]{@link Output} this channel belongs to. 4154 * @type {Output} 4155 * @since 3.0 4156 */ 4157 get output() { 4158 return this._output; 4159 } 4160 4161 /** 4162 * This channel's MIDI number (`1` - `16`). 4163 * @type {number} 4164 * @since 3.0 4165 */ 4166 get number() { 4167 return this._number; 4168 } 4169 } 4170 4171 /** 4172 * The `Output` class represents a single MIDI output port (not to be confused with a MIDI channel). 4173 * A port is made available by a MIDI device. A MIDI device can advertise several input and output 4174 * ports. Each port has 16 MIDI channels which can be accessed via the [`channels`](#channels) 4175 * property. 4176 * 4177 * The `Output` object is automatically instantiated by the library according to the host's MIDI 4178 * subsystem and should not be directly instantiated. 4179 * 4180 * You can access all available `Output` objects by referring to the 4181 * [`WebMidi.outputs`](WebMidi#outputs) array or by using methods such as 4182 * [`WebMidi.getOutputByName()`](WebMidi#getOutputByName) or 4183 * [`WebMidi.getOutputById()`](WebMidi#getOutputById). 4184 * 4185 * @fires Output#opened 4186 * @fires Output#disconnected 4187 * @fires Output#closed 4188 * 4189 * @extends EventEmitter 4190 * @license Apache-2.0 4191 */ 4192 class Output extends EventEmitter { 4193 /** 4194 * Creates an `Output` object. 4195 * 4196 * @param {MIDIOutput} midiOutput [`MIDIOutput`](https://developer.mozilla.org/en-US/docs/Web/API/MIDIOutput) 4197 * object as provided by the MIDI subsystem. 4198 */ 4199 constructor(midiOutput) { 4200 super(); 4201 4202 /** 4203 * A reference to the `MIDIOutput` object 4204 * @type {MIDIOutput} 4205 * @private 4206 */ 4207 this._midiOutput = midiOutput; 4208 4209 /** 4210 * @type {number} 4211 * @private 4212 */ 4213 this._octaveOffset = 0; 4214 4215 /** 4216 * Array containing the 16 [`OutputChannel`]{@link OutputChannel} objects available provided by 4217 * this `Output`. The channels are numbered 1 through 16. 4218 * 4219 * @type {OutputChannel[]} 4220 */ 4221 this.channels = []; 4222 for (let i = 1; i <= 16; i++) this.channels[i] = new OutputChannel(this, i); 4223 this._midiOutput.onstatechange = this._onStateChange.bind(this); 4224 } 4225 4226 /** 4227 * Destroys the `Output`. All listeners are removed, all channels are destroyed and the MIDI 4228 * subsystem is unlinked. 4229 * @returns {Promise<void>} 4230 */ 4231 async destroy() { 4232 this.removeListener(); 4233 this.channels.forEach(ch => ch.destroy()); 4234 this.channels = []; 4235 if (this._midiOutput) this._midiOutput.onstatechange = null; 4236 await this.close(); 4237 this._midiOutput = null; 4238 } 4239 4240 /** 4241 * @private 4242 */ 4243 _onStateChange(e) { 4244 let event = { 4245 timestamp: wm.time 4246 }; 4247 if (e.port.connection === "open") { 4248 /** 4249 * Event emitted when the {@link Output} has been opened by calling the 4250 * [open()]{@link Output#open} method. 4251 * 4252 * @event Output#opened 4253 * @type {object} 4254 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 4255 * milliseconds since the navigation start of the document). 4256 * @property {string} type `"opened"` 4257 * @property {Output} target The object to which the listener was originally added (`Output`). 4258 * @property {Output} port The port that was opened 4259 */ 4260 event.type = "opened"; 4261 event.target = this; 4262 event.port = event.target; // for consistency 4263 this.emit("opened", event); 4264 } else if (e.port.connection === "closed" && e.port.state === "connected") { 4265 /** 4266 * Event emitted when the {@link Output} has been closed by calling the 4267 * [close()]{@link Output#close} method. 4268 * 4269 * @event Output#closed 4270 * @type {object} 4271 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 4272 * milliseconds since the navigation start of the document). 4273 * @property {string} type `"closed"` 4274 * @property {Output} target The object to which the listener was originally added (`Output`). 4275 * @property {Output} port The port that was closed 4276 */ 4277 event.type = "closed"; 4278 event.target = this; 4279 event.port = event.target; // for consistency 4280 this.emit("closed", event); 4281 } else if (e.port.connection === "closed" && e.port.state === "disconnected") { 4282 /** 4283 * Event emitted when the {@link Output} becomes unavailable. This event is typically fired 4284 * when the MIDI device is unplugged. 4285 * 4286 * @event Output#disconnected 4287 * @type {object} 4288 * @property {number} timestamp The moment (DOMHighResTimeStamp0 when the event occurred (in 4289 * milliseconds since the navigation start of the document). 4290 * @property {string} type `"disconnected"` 4291 * @property {Output} target The object to which the listener was originally added (`Output`). 4292 * @property {object} port Object with properties describing the {@link Output} that was 4293 * disconnected. This is not the actual `Output` as it is no longer available. 4294 */ 4295 event.type = "disconnected"; 4296 event.port = { 4297 connection: e.port.connection, 4298 id: e.port.id, 4299 manufacturer: e.port.manufacturer, 4300 name: e.port.name, 4301 state: e.port.state, 4302 type: e.port.type 4303 }; 4304 this.emit("disconnected", event); 4305 } else if (e.port.connection === "pending" && e.port.state === "disconnected") ; else { 4306 console.warn("This statechange event was not caught:", e.port.connection, e.port.state); 4307 } 4308 } 4309 4310 /** 4311 * Opens the output for usage. When the library is enabled, all ports are automatically opened. 4312 * This method is only useful for ports that have been manually closed. 4313 * 4314 * @returns {Promise<Output>} The promise is fulfilled with the `Output` object. 4315 */ 4316 async open() { 4317 // Explicitly opens the port for usage. This is not mandatory. When the port is not explicitly 4318 // opened, it is implicitly opened (asynchronously) when calling `send()` on the `MIDIOutput`. 4319 // We do it explicitly so that 'connected' events are dispatched immediately and we are ready to 4320 // send. 4321 try { 4322 await this._midiOutput.open(); 4323 return Promise.resolve(this); 4324 } catch (err) { 4325 return Promise.reject(err); 4326 } 4327 } 4328 4329 /** 4330 * Closes the output connection. When an output is closed, it cannot be used to send MIDI messages 4331 * until the output is opened again by calling [`open()`]{@link #open}. You can check 4332 * the connection status by looking at the [`connection`]{@link #connection} property. 4333 * 4334 * @returns {Promise<void>} 4335 */ 4336 async close() { 4337 // We close the port. This triggers a 'statechange' event which we listen to to re-trigger the 4338 // 'closed' event. 4339 if (this._midiOutput) { 4340 await this._midiOutput.close(); 4341 } else { 4342 await Promise.resolve(); 4343 } 4344 } 4345 4346 /** 4347 * Sends a MIDI message on the MIDI output port. If no time is specified, the message will be 4348 * sent immediately. The message should be an array of 8 bit unsigned integers (0-225), a 4349 * [`Uint8Array`]{@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array} 4350 * object or a [`Message`](Message) object. 4351 * 4352 * It is usually not necessary to use this method directly as you can use one of the simpler 4353 * helper methods such as [`playNote()`](#playNote), [`stopNote()`](#stopNote), 4354 * [`sendControlChange()`](#sendControlChange), etc. 4355 * 4356 * Details on the format of MIDI messages are available in the summary of 4357 * [MIDI messages]{@link https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message} 4358 * from the MIDI Manufacturers Association. 4359 * 4360 * @param message {number[]|Uint8Array|Message} An array of 8bit unsigned integers, a `Uint8Array` 4361 * object (not available in Node.js) containing the message bytes or a `Message` object. 4362 * 4363 * @param {object} [options={}] 4364 * 4365 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4366 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4367 * positive number 4368 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4369 * the operation will be scheduled for that time. The current time can be retrieved with 4370 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4371 * operation will be carried out as soon as possible. 4372 * 4373 * @throws {RangeError} The first byte (status) must be an integer between 128 and 255. 4374 * 4375 * @returns {Output} Returns the `Output` object so methods can be chained. 4376 * 4377 * @license Apache-2.0 4378 */ 4379 send(message, options = { 4380 time: 0 4381 }, legacy = 0) { 4382 // If a Message object is passed in we extract the message data (the jzz plugin used on Node.js 4383 // does not support using Uint8Array). 4384 if (message instanceof Message) { 4385 message = Utilities.isNode ? message.data : message.rawData; 4386 } 4387 4388 // If the data is a Uint8Array and we are on Node, we must convert it to array so it works with 4389 // the jzz module. 4390 if (message instanceof Uint8Array && Utilities.isNode) { 4391 message = Array.from(message); 4392 } 4393 4394 // Validation 4395 if (wm.validation) { 4396 // If message is neither an array nor a Uint8Array, then we are in legacy mode 4397 if (!Array.isArray(message) && !(message instanceof Uint8Array)) { 4398 message = [message]; 4399 if (Array.isArray(options)) message = message.concat(options); 4400 options = isNaN(legacy) ? { 4401 time: 0 4402 } : { 4403 time: legacy 4404 }; 4405 } 4406 if (!(parseInt(message[0]) >= 128 && parseInt(message[0]) <= 255)) { 4407 throw new RangeError("The first byte (status) must be an integer between 128 and 255."); 4408 } 4409 message.slice(1).forEach(value => { 4410 value = parseInt(value); 4411 if (!(value >= 0 && value <= 255)) { 4412 throw new RangeError("Data bytes must be integers between 0 and 255."); 4413 } 4414 }); 4415 if (!options) options = { 4416 time: 0 4417 }; 4418 } 4419 4420 // Send message and return `Output` for chaining 4421 this._midiOutput.send(message, Utilities.toTimestamp(options.time)); 4422 return this; 4423 } 4424 4425 /** 4426 * Sends a MIDI [**system exclusive**]{@link 4427 * https://www.midi.org/specifications-old/item/table-4-universal-system-exclusive-messages} 4428 * (*sysex*) message. There are two categories of system exclusive messages: manufacturer-specific 4429 * messages and universal messages. Universal messages are further divided into three subtypes: 4430 * 4431 * * Universal non-commercial (for research and testing): `0x7D` 4432 * * Universal non-realtime: `0x7E` 4433 * * Universal realtime: `0x7F` 4434 * 4435 * The method's first parameter (`identification`) identifies the type of message. If the value of 4436 * `identification` is `0x7D` (125), `0x7E` (126) or `0x7F` (127), the message will be identified 4437 * as a **universal non-commercial**, **universal non-realtime** or **universal realtime** message 4438 * (respectively). 4439 * 4440 * If the `identification` value is an array or an integer between 0 and 124, it will be used to 4441 * identify the manufacturer targeted by the message. The *MIDI Manufacturers Association* 4442 * maintains a full list of 4443 * [Manufacturer ID Numbers](https://www.midi.org/specifications-old/item/manufacturer-id-numbers). 4444 * 4445 * The `data` parameter should only contain the data of the message. When sending out the actual 4446 * MIDI message, WEBMIDI.js will automatically prepend the data with the **sysex byte** (`0xF0`) 4447 * and the identification byte(s). It will also automatically terminate the message with the 4448 * **sysex end byte** (`0xF7`). 4449 * 4450 * To use the `sendSysex()` method, system exclusive message support must have been enabled. To 4451 * do so, you must set the `sysex` option to `true` when calling 4452 * [`WebMidi.enable()`]{@link WebMidi#enable}: 4453 * 4454 * ```js 4455 * WebMidi.enable({sysex: true}) 4456 * .then(() => console.log("System exclusive messages are enabled"); 4457 * ``` 4458 * 4459 * ##### Examples of manufacturer-specific system exclusive messages 4460 * 4461 * If you want to send a sysex message to a Korg device connected to the first output, you would 4462 * use the following code: 4463 * 4464 * ```js 4465 * WebMidi.outputs[0].sendSysex(0x42, [0x1, 0x2, 0x3, 0x4, 0x5]); 4466 * ``` 4467 * In this case `0x42` is the ID of the manufacturer (Korg) and `[0x1, 0x2, 0x3, 0x4, 0x5]` is the 4468 * data being sent. 4469 * 4470 * The parameters can be specified using any number notation (decimal, hex, binary, etc.). 4471 * Therefore, the code above is equivalent to this code: 4472 * 4473 * ```js 4474 * WebMidi.outputs[0].sendSysex(66, [1, 2, 3, 4, 5]); 4475 * ``` 4476 * 4477 * Some manufacturers are identified using 3 bytes. In this case, you would use a 3-position array 4478 * as the first parameter. For example, to send the same sysex message to a 4479 * *Native Instruments* device: 4480 * 4481 * ```js 4482 * WebMidi.outputs[0].sendSysex([0x00, 0x21, 0x09], [0x1, 0x2, 0x3, 0x4, 0x5]); 4483 * ``` 4484 * 4485 * There is no limit for the length of the data array. However, it is generally suggested to keep 4486 * system exclusive messages to 64Kb or less. 4487 * 4488 * ##### Example of universal system exclusive message 4489 * 4490 * If you want to send a universal sysex message, simply assign the correct identification number 4491 * in the first parameter. Number `0x7D` (125) is for non-commercial, `0x7E` (126) is for 4492 * non-realtime and `0x7F` (127) is for realtime. 4493 * 4494 * So, for example, if you wanted to send an identity request non-realtime message (`0x7E`), you 4495 * could use the following: 4496 * 4497 * ```js 4498 * WebMidi.outputs[0].sendSysex(0x7E, [0x7F, 0x06, 0x01]); 4499 * ``` 4500 * 4501 * For more details on the format of universal messages, consult the list of 4502 * [universal sysex messages](https://www.midi.org/specifications-old/item/table-4-universal-system-exclusive-messages). 4503 * 4504 * @param {number|number[]} identification An unsigned integer or an array of three unsigned 4505 * integers between `0` and `127` that either identify the manufacturer or sets the message to be 4506 * a **universal non-commercial message** (`0x7D`), a **universal non-realtime message** (`0x7E`) 4507 * or a **universal realtime message** (`0x7F`). The *MIDI Manufacturers Association* maintains a 4508 * full list of 4509 * [Manufacturer ID Numbers](https://www.midi.org/specifications-old/item/manufacturer-id-numbers). 4510 * 4511 * @param {number[]|Uint8Array} [data] A `Uint8Array` or an array of unsigned integers between `0` 4512 * and `127`. This is the data you wish to transfer. 4513 * 4514 * @param {object} [options={}] 4515 * 4516 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4517 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4518 * positive number 4519 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4520 * the operation will be scheduled for that time. The current time can be retrieved with 4521 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4522 * operation will be carried out as soon as possible. 4523 * 4524 * @throws {DOMException} Failed to execute 'send' on 'MIDIOutput': System exclusive message is 4525 * not allowed. 4526 * 4527 * @throws {TypeError} Failed to execute 'send' on 'MIDIOutput': The value at index x is greater 4528 * than 0xFF. 4529 * 4530 * @returns {Output} Returns the `Output` object so methods can be chained. 4531 */ 4532 sendSysex(identification, data = [], options = {}) { 4533 identification = [].concat(identification); 4534 4535 // Check if data is Uint8Array 4536 if (data instanceof Uint8Array) { 4537 const merged = new Uint8Array(1 + identification.length + data.length + 1); 4538 merged[0] = Enumerations.SYSTEM_MESSAGES.sysex; 4539 merged.set(Uint8Array.from(identification), 1); 4540 merged.set(data, 1 + identification.length); 4541 merged[merged.length - 1] = Enumerations.SYSTEM_MESSAGES.sysexend; 4542 this.send(merged, { 4543 time: options.time 4544 }); 4545 } else { 4546 const merged = identification.concat(data, Enumerations.SYSTEM_MESSAGES.sysexend); 4547 this.send([Enumerations.SYSTEM_MESSAGES.sysex].concat(merged), { 4548 time: options.time 4549 }); 4550 } 4551 return this; 4552 } 4553 /** 4554 * Clears all MIDI messages that have been queued and scheduled but not yet sent. 4555 * 4556 * **Warning**: this method is defined in the 4557 * [Web MIDI API specification](https://www.w3.org/TR/webmidi/#MIDIOutput) but has not been 4558 * implemented by all browsers yet. You can follow 4559 * [this issue](https://github.com/djipco/webmidi/issues/52) for more info. 4560 * 4561 * @returns {Output} Returns the `Output` object so methods can be chained. 4562 */ 4563 clear() { 4564 if (this._midiOutput.clear) { 4565 this._midiOutput.clear(); 4566 } else { 4567 if (wm.validation) { 4568 console.warn("The 'clear()' method has not yet been implemented in your environment."); 4569 } 4570 } 4571 return this; 4572 } 4573 4574 /** 4575 * Sends a MIDI **timecode quarter frame** message. Please note that no processing is being done 4576 * on the data. It is up to the developer to format the data according to the 4577 * [MIDI Timecode](https://en.wikipedia.org/wiki/MIDI_timecode) format. 4578 * 4579 * @param value {number} The quarter frame message content (integer between 0 and 127). 4580 * 4581 * @param {object} [options={}] 4582 * 4583 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4584 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4585 * positive number 4586 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4587 * the operation will be scheduled for that time. The current time can be retrieved with 4588 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4589 * operation will be carried out as soon as possible. 4590 * 4591 * @returns {Output} Returns the `Output` object so methods can be chained. 4592 */ 4593 sendTimecodeQuarterFrame(value, options = {}) { 4594 if (wm.validation) { 4595 value = parseInt(value); 4596 if (isNaN(value) || !(value >= 0 && value <= 127)) { 4597 throw new RangeError("The value must be an integer between 0 and 127."); 4598 } 4599 } 4600 this.send([Enumerations.SYSTEM_MESSAGES.timecode, value], { 4601 time: options.time 4602 }); 4603 return this; 4604 } 4605 /** 4606 * Sends a **song position** MIDI message. The value is expressed in MIDI beats (between `0` and 4607 * `16383`) which are 16th note. Position `0` is always the start of the song. 4608 * 4609 * @param {number} [value=0] The MIDI beat to cue to (integer between `0` and `16383`). 4610 * 4611 * @param {object} [options={}] 4612 * 4613 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4614 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4615 * positive number 4616 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4617 * the operation will be scheduled for that time. The current time can be retrieved with 4618 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4619 * operation will be carried out as soon as possible. 4620 * 4621 * @returns {Output} Returns the `Output` object so methods can be chained. 4622 * 4623 * @since 3.0.0 4624 */ 4625 sendSongPosition(value = 0, options = {}) { 4626 // @todo allow passing in 2-entries array for msb/lsb 4627 4628 value = Math.floor(value) || 0; 4629 var msb = value >> 7 & 0x7F; 4630 var lsb = value & 0x7F; 4631 this.send([Enumerations.SYSTEM_MESSAGES.songposition, msb, lsb], { 4632 time: options.time 4633 }); 4634 return this; 4635 } 4636 4637 /** 4638 * Sends a **song select** MIDI message. 4639 * 4640 * @param {number} [value=0] The number of the song to select (integer between `0` and `127`). 4641 * 4642 * @param {object} [options={}] 4643 * 4644 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4645 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4646 * positive number 4647 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4648 * the operation will be scheduled for that time. The current time can be retrieved with 4649 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4650 * operation will be carried out as soon as possible. 4651 * 4652 * @throws The song number must be between 0 and 127. 4653 * 4654 * @returns {Output} Returns the `Output` object so methods can be chained. 4655 * 4656 * @since 3.0.0 4657 */ 4658 sendSongSelect(value = 0, options = {}) { 4659 if (wm.validation) { 4660 value = parseInt(value); 4661 if (isNaN(value) || !(value >= 0 && value <= 127)) { 4662 throw new RangeError("The program value must be between 0 and 127"); 4663 } 4664 } 4665 this.send([Enumerations.SYSTEM_MESSAGES.songselect, value], { 4666 time: options.time 4667 }); 4668 return this; 4669 } 4670 4671 /** 4672 * Sends a MIDI **tune request** real-time message. 4673 * 4674 * @param {object} [options={}] 4675 * 4676 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4677 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4678 * positive number 4679 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4680 * the operation will be scheduled for that time. The current time can be retrieved with 4681 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4682 * operation will be carried out as soon as possible. 4683 * 4684 * @returns {Output} Returns the `Output` object so methods can be chained. 4685 * 4686 * @since 3.0.0 4687 */ 4688 sendTuneRequest(options = {}) { 4689 this.send([Enumerations.SYSTEM_MESSAGES.tunerequest], { 4690 time: options.time 4691 }); 4692 return this; 4693 } 4694 4695 /** 4696 * Sends a MIDI **clock** real-time message. According to the standard, there are 24 MIDI clocks 4697 * for every quarter note. 4698 * 4699 * @param {object} [options={}] 4700 * 4701 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4702 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4703 * positive number 4704 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4705 * the operation will be scheduled for that time. The current time can be retrieved with 4706 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4707 * operation will be carried out as soon as possible. 4708 * 4709 * @returns {Output} Returns the `Output` object so methods can be chained. 4710 */ 4711 sendClock(options = {}) { 4712 this.send([Enumerations.SYSTEM_MESSAGES.clock], { 4713 time: options.time 4714 }); 4715 return this; 4716 } 4717 4718 /** 4719 * Sends a **start** real-time message. A MIDI Start message starts the playback of the current 4720 * song at beat 0. To start playback elsewhere in the song, use the 4721 * [`sendContinue()`]{@link #sendContinue} method. 4722 * 4723 * @param {object} [options={}] 4724 * 4725 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4726 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4727 * positive number 4728 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4729 * the operation will be scheduled for that time. The current time can be retrieved with 4730 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4731 * operation will be carried out as soon as possible. 4732 * 4733 * @returns {Output} Returns the `Output` object so methods can be chained. 4734 */ 4735 sendStart(options = {}) { 4736 this.send([Enumerations.SYSTEM_MESSAGES.start], { 4737 time: options.time 4738 }); 4739 return this; 4740 } 4741 4742 /** 4743 * Sends a **continue** real-time message. This resumes song playback where it was previously 4744 * stopped or where it was last cued with a song position message. To start playback from the 4745 * start, use the [`sendStart()`]{@link Output#sendStart}` method. 4746 * 4747 * @param {object} [options={}] 4748 * 4749 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4750 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4751 * positive number 4752 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4753 * the operation will be scheduled for that time. The current time can be retrieved with 4754 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4755 * operation will be carried out as soon as possible. 4756 * 4757 * @returns {Output} Returns the `Output` object so methods can be chained. 4758 */ 4759 sendContinue(options = {}) { 4760 this.send([Enumerations.SYSTEM_MESSAGES.continue], { 4761 time: options.time 4762 }); 4763 return this; 4764 } 4765 4766 /** 4767 * Sends a **stop** real-time message. This tells the device connected to this output to stop 4768 * playback immediately (or at the scheduled time, if specified). 4769 * 4770 * @param {object} [options={}] 4771 * 4772 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4773 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4774 * positive number 4775 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4776 * the operation will be scheduled for that time. The current time can be retrieved with 4777 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4778 * operation will be carried out as soon as possible. 4779 * 4780 * @returns {Output} Returns the `Output` object so methods can be chained. 4781 */ 4782 sendStop(options = {}) { 4783 this.send([Enumerations.SYSTEM_MESSAGES.stop], { 4784 time: options.time 4785 }); 4786 return this; 4787 } 4788 4789 /** 4790 * Sends an **active sensing** real-time message. This tells the device connected to this port 4791 * that the connection is still good. Active sensing messages are often sent every 300 ms if there 4792 * was no other activity on the MIDI port. 4793 * 4794 * @param {object} [options={}] 4795 * 4796 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4797 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4798 * positive number 4799 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4800 * the operation will be scheduled for that time. The current time can be retrieved with 4801 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4802 * operation will be carried out as soon as possible. 4803 * 4804 * @returns {Output} Returns the `Output` object so methods can be chained. 4805 */ 4806 sendActiveSensing(options = {}) { 4807 this.send([Enumerations.SYSTEM_MESSAGES.activesensing], { 4808 time: options.time 4809 }); 4810 return this; 4811 } 4812 4813 /** 4814 * Sends a **reset** real-time message. This tells the device connected to this output that it 4815 * should reset itself to a default state. 4816 * 4817 * @param {object} [options={}] 4818 * 4819 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4820 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4821 * positive number 4822 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4823 * the operation will be scheduled for that time. The current time can be retrieved with 4824 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4825 * operation will be carried out as soon as possible. 4826 * 4827 * @returns {Output} Returns the `Output` object so methods can be chained. 4828 */ 4829 sendReset(options = {}) { 4830 this.send([Enumerations.SYSTEM_MESSAGES.reset], { 4831 time: options.time 4832 }); 4833 return this; 4834 } 4835 4836 /** 4837 * @private 4838 * @deprecated since version 3.0 4839 */ 4840 sendTuningRequest(options = {}) { 4841 if (wm.validation) { 4842 console.warn("The sendTuningRequest() method has been deprecated. Use sendTuningRequest() instead."); 4843 } 4844 return this.sendTuneRequest(options); 4845 } 4846 4847 /** 4848 * Sends a MIDI **key aftertouch** message to the specified channel(s) at the scheduled time. This 4849 * is a key-specific aftertouch. For a channel-wide aftertouch message, use 4850 * [`se
4850tChannelAftertouch()`]{@link #set
vendor: 15,214 bytes, lines 4850-5144
4850ChannelAftertouch}. 4851 * 4852 * @param note {number|Note|string|number[]|Note[]|string[]} The note(s) for which you are sending 4853 * an aftertouch value. The notes can be specified by using a MIDI note number (`0` - `127`), a 4854 * [`Note`](Note) object, a note identifier (e.g. `C3`, `G#4`, `F-1`, `Db7`) or an array of the 4855 * previous types. When using a note identifier, octave range must be between `-1` and `9`. The 4856 * lowest note is `C-1` (MIDI note number `0`) and the highest note is `G9` (MIDI note number 4857 * `127`). 4858 * 4859 * @param [pressure=0.5] {number} The pressure level (between 0 and 1). An invalid pressure value 4860 * will silently trigger the default behaviour. If the `rawValue` option is set to `true`, the 4861 * pressure can be defined by using an integer between 0 and 127. 4862 * 4863 * @param {object} [options={}] 4864 * 4865 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 4866 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 4867 * channel is specified, all channels will be used. 4868 * 4869 * @param {boolean} [options.rawValue=false] A boolean indicating whether the value should be 4870 * considered a float between `0` and `1.0` (default) or a raw integer between `0` and `127`. 4871 * 4872 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4873 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4874 * positive number 4875 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4876 * the operation will be scheduled for that time. The current time can be retrieved with 4877 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4878 * operation will be carried out as soon as possible. 4879 * 4880 * @return {Output} Returns the `Output` object so methods can be chained. 4881 * 4882 * @since 3.0.0 4883 */ 4884 sendKeyAftertouch(note, pressure, options = {}) { 4885 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 4886 Utilities.sanitizeChannels(options.channels).forEach(ch => { 4887 this.channels[ch].sendKeyAftertouch(note, pressure, options); 4888 }); 4889 return this; 4890 } 4891 /** 4892 * Sends a MIDI **control change** message to the specified channel(s) at the scheduled time. The 4893 * control change message to send can be specified numerically (0-127) or by using one of the 4894 * following common names: 4895 * 4896 * | Number | Name | 4897 * |--------|-------------------------------| 4898 * | 0 |`bankselectcoarse` | 4899 * | 1 |`modulationwheelcoarse` | 4900 * | 2 |`breathcontrollercoarse` | 4901 * | 4 |`footcontrollercoarse` | 4902 * | 5 |`portamentotimecoarse` | 4903 * | 6 |`dataentrycoarse` | 4904 * | 7 |`volumecoarse` | 4905 * | 8 |`balancecoarse` | 4906 * | 10 |`pancoarse` | 4907 * | 11 |`expressioncoarse` | 4908 * | 12 |`effectcontrol1coarse` | 4909 * | 13 |`effectcontrol2coarse` | 4910 * | 18 |`generalpurposeslider3` | 4911 * | 19 |`generalpurposeslider4` | 4912 * | 32 |`bankselectfine` | 4913 * | 33 |`modulationwheelfine` | 4914 * | 34 |`breathcontrollerfine` | 4915 * | 36 |`footcontrollerfine` | 4916 * | 37 |`portamentotimefine` | 4917 * | 38 |`dataentryfine` | 4918 * | 39 |`volumefine` | 4919 * | 40 |`balancefine` | 4920 * | 42 |`panfine` | 4921 * | 43 |`expressionfine` | 4922 * | 44 |`effectcontrol1fine` | 4923 * | 45 |`effectcontrol2fine` | 4924 * | 64 |`holdpedal` | 4925 * | 65 |`portamento` | 4926 * | 66 |`sustenutopedal` | 4927 * | 67 |`softpedal` | 4928 * | 68 |`legatopedal` | 4929 * | 69 |`hold2pedal` | 4930 * | 70 |`soundvariation` | 4931 * | 71 |`resonance` | 4932 * | 72 |`soundreleasetime` | 4933 * | 73 |`soundattacktime` | 4934 * | 74 |`brightness` | 4935 * | 75 |`soundcontrol6` | 4936 * | 76 |`soundcontrol7` | 4937 * | 77 |`soundcontrol8` | 4938 * | 78 |`soundcontrol9` | 4939 * | 79 |`soundcontrol10` | 4940 * | 80 |`generalpurposebutton1` | 4941 * | 81 |`generalpurposebutton2` | 4942 * | 82 |`generalpurposebutton3` | 4943 * | 83 |`generalpurposebutton4` | 4944 * | 91 |`reverblevel` | 4945 * | 92 |`tremololevel` | 4946 * | 93 |`choruslevel` | 4947 * | 94 |`celestelevel` | 4948 * | 95 |`phaserlevel` | 4949 * | 96 |`dataincrement` | 4950 * | 97 |`datadecrement` | 4951 * | 98 |`nonregisteredparametercoarse` | 4952 * | 99 |`nonregisteredparameterfine` | 4953 * | 100 |`registeredparametercoarse` | 4954 * | 101 |`registeredparameterfine` | 4955 * | 120 |`allsoundoff` | 4956 * | 121 |`resetallcontrollers` | 4957 * | 122 |`localcontrol` | 4958 * | 123 |`allnotesoff` | 4959 * | 124 |`omnimodeoff` | 4960 * | 125 |`omnimodeon` | 4961 * | 126 |`monomodeon` | 4962 * | 127 |`polymodeon` | 4963 * 4964 * Note: as you can see above, not all control change message have a matching name. This does not 4965 * mean you cannot use the others. It simply means you will need to use their number (`0` - `127`) 4966 * instead of their name. While you can still use them, numbers `120` to `127` are usually 4967 * reserved for *channel mode* messages. See [`sendChannelMode()`]{@link #sendChannelMode} method 4968 * for more info. 4969 * 4970 * To view a list of all available **control change** messages, please consult [Table 3 - Control 4971 * Change Messages](https://www.midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2) 4972 * from the MIDI specification. 4973 * 4974 * @param controller {number|string} The MIDI controller name or number (0-127). 4975 * 4976 * @param [value=0] {number} The value to send (0-127). 4977 * 4978 * @param {object} [options={}] 4979 * 4980 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 4981 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 4982 * channel is specified, all channels will be used. 4983 * 4984 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 4985 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 4986 * positive number 4987 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 4988 * the operation will be scheduled for that time. The current time can be retrieved with 4989 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 4990 * operation will be carried out as soon as possible. 4991 * 4992 * @throws {RangeError} Controller numbers must be between 0 and 127. 4993 * @throws {RangeError} Invalid controller name. 4994 * 4995 * @return {Output} Returns the `Output` object so methods can be chained. 4996 */ 4997 sendControlChange(controller, value, options = {}, legacy = {}) { 4998 if (wm.validation) { 4999 // Legacy compatibility 5000 if (Array.isArray(options) || Number.isInteger(options) || options === "all") { 5001 const channels = options; 5002 options = legacy; 5003 options.channels = channels; 5004 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5005 } 5006 } 5007 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5008 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5009 this.channels[ch].sendControlChange(controller, value, options); 5010 }); 5011 return this; 5012 } 5013 /** 5014 * Sends a **pitch bend range** message to the specified channel(s) at the scheduled time so that 5015 * they adjust the range used by their pitch bend lever. The range is specified by using the 5016 * `semitones` and `cents` parameters. For example, setting the `semitones` parameter to `12` 5017 * means that the pitch bend range will be 12 semitones above and below the nominal pitch. 5018 * 5019 * @param {number} [semitones=0] The desired adjustment value in semitones (between `0` and `127`). 5020 * While nothing imposes that in the specification, it is very common for manufacturers to limit 5021 * the range to 2 octaves (-12 semitones to 12 semitones). 5022 * 5023 * @param {number} [cents=0] The desired adjustment value in cents (integer between `0` and 5024 * `127`). 5025 * 5026 * @param {object} [options={}] 5027 * 5028 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5029 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5030 * channel is specified, all channels will be used. 5031 * 5032 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5033 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5034 * positive number 5035 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5036 * the operation will be scheduled for that time. The current time can be retrieved with 5037 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5038 * operation will be carried out as soon as possible. 5039 * 5040 * @throws {RangeError} The msb value must be between 0 and 127. 5041 * @throws {RangeError} The lsb value must be between 0 and 127. 5042 * 5043 * @returns {Output} Returns the `Output` object so methods can be chained. 5044 * 5045 * @since 3.0.0 5046 */ 5047 sendPitchBendRange(semitones = 0, cents = 0, options = {}) { 5048 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5049 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5050 this.channels[ch].sendPitchBendRange(semitones, cents, options); 5051 }); 5052 return this; 5053 } 5054 5055 /** 5056 * @private 5057 * @deprecated since version 3.0 5058 */ 5059 setPitchBendRange(semitones = 0, cents = 0, channel = "all", options = {}) { 5060 if (wm.validation) { 5061 console.warn("The setPitchBendRange() method is deprecated. Use sendPitchBendRange() instead."); 5062 options.channels = channel; 5063 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5064 } 5065 return this.sendPitchBendRange(semitones, cents, options); 5066 } 5067 5068 /** 5069 * Sets the specified MIDI registered parameter to the desired value. The value is defined with 5070 * up to two bytes of data (msb, lsb) that each can go from `0` to `127`. 5071 * 5072 * MIDI 5073 * [registered parameters](https://www.midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2) 5074 * extend the original list of control change messages. The MIDI 1.0 specification lists only a 5075 * limited number of them: 5076 * 5077 * | Numbers | Function | 5078 * |--------------|--------------------------| 5079 * | (0x00, 0x00) | `pitchbendrange` | 5080 * | (0x00, 0x01) | `channelfinetuning` | 5081 * | (0x00, 0x02) | `channelcoarsetuning` | 5082 * | (0x00, 0x03) | `tuningprogram` | 5083 * | (0x00, 0x04) | `tuningbank` | 5084 * | (0x00, 0x05) | `modulationrange` | 5085 * | (0x3D, 0x00) | `azimuthangle` | 5086 * | (0x3D, 0x01) | `elevationangle` | 5087 * | (0x3D, 0x02) | `gain` | 5088 * | (0x3D, 0x03) | `distanceratio` | 5089 * | (0x3D, 0x04) | `maximumdistance` | 5090 * | (0x3D, 0x05) | `maximumdistancegain` | 5091 * | (0x3D, 0x06) | `referencedistanceratio` | 5092 * | (0x3D, 0x07) | `panspreadangle` | 5093 * | (0x3D, 0x08) | `rollangle` | 5094 * 5095 * Note that the `tuningprogram` and `tuningbank` parameters are part of the *MIDI Tuning 5096 * Standard*, which is not widely implemented. 5097 * 5098 * @param parameter {string|number[]} A string identifying the parameter's name (see above) or a 5099 * two-position array specifying the two control bytes (e.g. `[0x65, 0x64]`) that identify the 5100 * registered parameter. 5101 * 5102 * @param [data=[]] {number|number[]} A single integer or an array of integers with a maximum 5103 * length of 2 specifying the desired data. 5104 * 5105 * @param {object} [options={}] 5106 * 5107 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5108 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5109 * channel is specified, all channels will be used. 5110 * 5111 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5112 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5113 * positive number 5114 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5115 * the operation will be scheduled for that time. The current time can be retrieved with 5116 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5117 * operation will be carried out as soon as possible. 5118 * 5119 * @returns {Output} Returns the `Output` object so methods can be chained. 5120 */ 5121 sendRpnValue(parameter, data, options = {}) { 5122 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5123 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5124 this.channels[ch].sendRpnValue(parameter, data, options); 5125 }); 5126 return this; 5127 } 5128 5129 /** 5130 * @private 5131 * @deprecated since version 3.0 5132 */ 5133 setRegisteredParameter(parameter, data = [], channel = "all", options = {}) { 5134 if (wm.validation) { 5135 console.warn("The setRegisteredParameter() method is deprecated. Use sendRpnValue() instead."); 5136 options.channels = channel; 5137 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5138 } 5139 return this.sendRpnValue(parameter, data, options); 5140 } 5141 5142 /** 5143 * Sends a MIDI **channel aftertouch** message to the specified channel(s). For key-specific 5144 * aftertouch, you should instead use [`se
5144tKeyAftertouch()`]{@link #set
vendor: 68,842 bytes, lines 5144-6557
5144KeyAftertouch}. 5145 * 5146 * @param [pressure=0.5] {number} The pressure level (between `0` and `1`). An invalid pressure 5147 * value will silently trigger the default behaviour. If the `rawValue` option is set to `true`, 5148 * the pressure can be defined by using an integer between `0` and `127`. 5149 * 5150 * @param {object} [options={}] 5151 * 5152 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5153 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5154 * channel is specified, all channels will be used. 5155 * 5156 * @param {boolean} [options.rawValue=false] A boolean indicating whether the value should be 5157 * considered a float between `0` and `1.0` (default) or a raw integer between `0` and `127`. 5158 * 5159 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5160 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5161 * positive number 5162 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5163 * the operation will be scheduled for that time. The current time can be retrieved with 5164 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5165 * operation will be carried out as soon as possible. 5166 * 5167 * @return {Output} Returns the `Output` object so methods can be chained. 5168 * @since 3.0.0 5169 */ 5170 sendChannelAftertouch(pressure, options = {}, legacy = {}) { 5171 if (wm.validation) { 5172 // Legacy compatibility 5173 if (Array.isArray(options) || Number.isInteger(options) || options === "all") { 5174 const channels = options; 5175 options = legacy; 5176 options.channels = channels; 5177 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5178 } 5179 } 5180 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5181 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5182 this.channels[ch].sendChannelAftertouch(pressure, options); 5183 }); 5184 return this; 5185 } 5186 5187 /** 5188 * Sends a MIDI **pitch bend** message to the specified channel(s) at the scheduled time. 5189 * 5190 * The resulting bend is relative to the pitch bend range that has been defined. The range can be 5191 * set with [`sendPitchBendRange()`]{@link #sendPitchBendRange}. So, for example, if the pitch 5192 * bend range has been set to 12 semitones, using a bend value of `-1` will bend the note 1 octave 5193 * below its nominal value. 5194 * 5195 * @param {number|number[]} value The intensity of the bend (between `-1.0` and `1.0`). A value of 5196 * `0` means no bend. If an invalid value is specified, the nearest valid value will be used 5197 * instead. If the `rawValue` option is set to `true`, the intensity of the bend can be defined by 5198 * either using a single integer between `0` and `127` (MSB) or an array of two integers between 5199 * `0` and `127` representing, respectively, the MSB (most significant byte) and the LSB (least 5200 * significant byte). The MSB is expressed in semitones with `64` meaning no bend. A value lower 5201 * than `64` bends downwards while a value higher than `64` bends upwards. The LSB is expressed 5202 * in cents (1/100 of a semitone). An LSB of `64` also means no bend. 5203 * 5204 * @param {object} [options={}] 5205 * 5206 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5207 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5208 * channel is specified, all channels will be used. 5209 * 5210 * @param {boolean} [options.rawValue=false] A boolean indicating whether the value should be 5211 * considered as a float between `-1.0` and `1.0` (default) or as raw integer between `0` and 5212 * 127` (or an array of 2 integers if using both MSB and LSB). 5213 * 5214 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5215 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5216 * positive number 5217 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5218 * the operation will be scheduled for that time. The current time can be retrieved with 5219 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5220 * operation will be carried out as soon as possible. 5221 * 5222 * @returns {Output} Returns the `Output` object so methods can be chained. 5223 * 5224 * @since 3.0.0 5225 */ 5226 sendPitchBend(value, options = {}, legacy = {}) { 5227 if (wm.validation) { 5228 // Legacy compatibility 5229 if (Array.isArray(options) || Number.isInteger(options) || options === "all") { 5230 const channels = options; 5231 options = legacy; 5232 options.channels = channels; 5233 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5234 } 5235 } 5236 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5237 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5238 this.channels[ch].sendPitchBend(value, options); 5239 }); 5240 return this; 5241 } 5242 5243 /** 5244 * Sends a MIDI **program change** message to the specified channel(s) at the scheduled time. 5245 * 5246 * @param {number} [program=0] The MIDI patch (program) number (integer between `0` and `127`). 5247 * 5248 * @param {Object} [options={}] 5249 * 5250 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5251 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5252 * channel is specified, all channels will be used. 5253 * 5254 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5255 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5256 * positive number 5257 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5258 * the operation will be scheduled for that time. The current time can be retrieved with 5259 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5260 * operation will be carried out as soon as possible. 5261 * 5262 * @throws {TypeError} Failed to execute 'send' on 'MIDIOutput': The value at index 1 is greater 5263 * than 0xFF. 5264 * 5265 * @return {Output} Returns the `Output` object so methods can be chained. 5266 * 5267 * @since 3.0.0 5268 */ 5269 sendProgramChange(program = 0, options = {}, legacy = {}) { 5270 if (wm.validation) { 5271 // Legacy compatibility 5272 if (Array.isArray(options) || Number.isInteger(options) || options === "all") { 5273 const channels = options; 5274 options = legacy; 5275 options.channels = channels; 5276 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5277 } 5278 } 5279 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5280 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5281 this.channels[ch].sendProgramChange(program, options); 5282 }); 5283 return this; 5284 } 5285 5286 /** 5287 * Sends a **modulation depth range** message to the specified channel(s) so that they adjust the 5288 * depth of their modulation wheel's range. The range can be specified with the `semitones` 5289 * parameter, the `cents` parameter or by specifying both parameters at the same time. 5290 * 5291 * @param [semitones=0] {number} The desired adjustment value in semitones (integer between 5292 * 0 and 127). 5293 * 5294 * @param [cents=0] {number} The desired adjustment value in cents (integer between 0 and 127). 5295 * 5296 * @param {Object} [options={}] 5297 * 5298 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5299 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5300 * channel is specified, all channels will be used. 5301 * 5302 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5303 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5304 * positive number 5305 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5306 * the operation will be scheduled for that time. The current time can be retrieved with 5307 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5308 * operation will be carried out as soon as possible. 5309 * 5310 * @throws {RangeError} The msb value must be between 0 and 127 5311 * @throws {RangeError} The lsb value must be between 0 and 127 5312 * 5313 * @return {Output} Returns the `Output` object so methods can be chained. 5314 * 5315 * @since 3.0.0 5316 */ 5317 sendModulationRange(semitones, cents, options = {}) { 5318 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5319 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5320 this.channels[ch].sendModulationRange(semitones, cents, options); 5321 }); 5322 return this; 5323 } 5324 /** 5325 * @private 5326 * @deprecated since version 3.0 5327 */ 5328 setModulationRange(semitones = 0, cents = 0, channel = "all", options = {}) { 5329 if (wm.validation) { 5330 console.warn("The setModulationRange() method is deprecated. Use sendModulationRange() instead."); 5331 options.channels = channel; 5332 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5333 } 5334 return this.sendModulationRange(semitones, cents, options); 5335 } 5336 5337 /** 5338 * Sends a master tuning message to the specified channel(s). The value is decimal and must be 5339 * larger than `-65` semitones and smaller than `64` semitones. 5340 * 5341 * Because of the way the MIDI specification works, the decimal portion of the value will be 5342 * encoded with a resolution of 14bit. The integer portion must be between -64 and 63 5343 * inclusively. This function actually generates two MIDI messages: a **Master Coarse Tuning** and 5344 * a **Master Fine Tuning** RPN messages. 5345 * 5346 * @param [value=0.0] {number} The desired decimal adjustment value in semitones (-65 < x < 64) 5347 * 5348 * @param {Object} [options={}] 5349 * 5350 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5351 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5352 * channel is specified, all channels will be used. 5353 * 5354 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5355 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5356 * positive number 5357 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5358 * the operation will be scheduled for that time. The current time can be retrieved with 5359 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5360 * operation will be carried out as soon as possible. 5361 * 5362 * @throws {RangeError} The value must be a decimal number between larger than -65 and smaller 5363 * than 64. 5364 * 5365 * @return {Output} Returns the `Output` object so methods can be chained. 5366 * 5367 * @since 3.0.0 5368 */ 5369 sendMasterTuning(value, options = {}) { 5370 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5371 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5372 this.channels[ch].sendMasterTuning(value, options); 5373 }); 5374 return this; 5375 } 5376 5377 /** 5378 * @private 5379 * @deprecated since version 3.0 5380 */ 5381 setMasterTuning(value, channel = {}, options = {}) { 5382 if (wm.validation) { 5383 console.warn("The setMasterTuning() method is deprecated. Use sendMasterTuning() instead."); 5384 options.channels = channel; 5385 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5386 } 5387 return this.sendMasterTuning(value, options); 5388 } 5389 5390 /** 5391 * Sets the MIDI tuning program to use. Note that the **Tuning Program** parameter is part of the 5392 * *MIDI Tuning Standard*, which is not widely implemented. 5393 * 5394 * @param value {number} The desired tuning program (integer between `0` and `127`). 5395 * 5396 * @param {Object} [options={}] 5397 * 5398 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5399 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5400 * channel is specified, all channels will be used. 5401 * 5402 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5403 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5404 * positive number 5405 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5406 * the operation will be scheduled for that time. The current time can be retrieved with 5407 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5408 * operation will be carried out as soon as possible. 5409 * 5410 * @throws {RangeError} The program value must be between 0 and 127. 5411 * 5412 * @return {Output} Returns the `Output` object so methods can be chained. 5413 * 5414 * @since 3.0.0 5415 */ 5416 sendTuningProgram(value, options = {}) { 5417 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5418 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5419 this.channels[ch].sendTuningProgram(value, options); 5420 }); 5421 return this; 5422 } 5423 5424 /** 5425 * @private 5426 * @deprecated since version 3.0 5427 */ 5428 setTuningProgram(value, channel = "all", options = {}) { 5429 if (wm.validation) { 5430 console.warn("The setTuningProgram() method is deprecated. Use sendTuningProgram() instead."); 5431 options.channels = channel; 5432 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5433 } 5434 return this.sendTuningProgram(value, options); 5435 } 5436 5437 /** 5438 * Sets the MIDI tuning bank to use. Note that the **Tuning Bank** parameter is part of the 5439 * *MIDI Tuning Standard*, which is not widely implemented. 5440 * 5441 * @param {number} [value=0] The desired tuning bank (integer between `0` and `127`). 5442 * 5443 * @param {Object} [options={}] 5444 * 5445 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5446 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5447 * channel is specified, all channels will be used. 5448 * 5449 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5450 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5451 * positive number 5452 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5453 * the operation will be scheduled for that time. The current time can be retrieved with 5454 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5455 * operation will be carried out as soon as possible. 5456 * 5457 * @throws {RangeError} The bank value must be between 0 and 127. 5458 * 5459 * @return {Output} Returns the `Output` object so methods can be chained. 5460 * 5461 * @since 3.0.0 5462 */ 5463 sendTuningBank(value = 0, options = {}) { 5464 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5465 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5466 this.channels[ch].sendTuningBank(value, options); 5467 }); 5468 return this; 5469 } 5470 /** 5471 * @private 5472 * @deprecated since version 3.0 5473 */ 5474 setTuningBank(parameter, channel = "all", options = {}) { 5475 if (wm.validation) { 5476 console.warn("The setTuningBank() method is deprecated. Use sendTuningBank() instead."); 5477 options.channels = channel; 5478 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5479 } 5480 return this.sendTuningBank(parameter, options); 5481 } 5482 5483 /** 5484 * Sends a MIDI **channel mode** message to the specified channel(s). The channel mode message to 5485 * send can be specified numerically or by using one of the following common names: 5486 * 5487 * | Type |Number| Shortcut Method | 5488 * | ---------------------|------|-------------------------------------------------------------- | 5489 * | `allsoundoff` | 120 | [`sendAllSoundOff()`]{@link #sendAllSoundOff} | 5490 * | `resetallcontrollers`| 121 | [`sendResetAllControllers()`]{@link #sendResetAllControllers} | 5491 * | `localcontrol` | 122 | [`sendLocalControl()`]{@link #sendLocalControl} | 5492 * | `allnotesoff` | 123 | [`sendAllNotesOff()`]{@link #sendAllNotesOff} | 5493 * | `omnimodeoff` | 124 | [`sendOmniMode(false)`]{@link #sendOmniMode} | 5494 * | `omnimodeon` | 125 | [`sendOmniMode(true)`]{@link #sendOmniMode} | 5495 * | `monomodeon` | 126 | [`sendPolyphonicMode("mono")`]{@link #sendPolyphonicMode} | 5496 * | `polymodeon` | 127 | [`sendPolyphonicMode("poly")`]{@link #sendPolyphonicMode} | 5497 * 5498 * Note: as you can see above, to make it easier, all channel mode messages also have a matching 5499 * helper method. 5500 * 5501 * It should also be noted that, per the MIDI specification, only `localcontrol` and `monomodeon` 5502 * may require a value that's not zero. For that reason, the `value` parameter is optional and 5503 * defaults to 0. 5504 * 5505 * @param {number|string} command The numerical identifier of the channel mode message (integer 5506 * between 120-127) or its name as a string. 5507 * 5508 * @param {number} [value=0] The value to send (integer between 0-127). 5509 * 5510 * @param {Object} [options={}] 5511 * 5512 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5513 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5514 * channel is specified, all channels will be used. 5515 * 5516 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5517 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5518 * positive number 5519 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5520 * the operation will be scheduled for that time. The current time can be retrieved with 5521 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5522 * operation will be carried out as soon as possible. 5523 * 5524 * @throws {TypeError} Invalid channel mode message name. 5525 * @throws {RangeError} Channel mode controller numbers must be between 120 and 127. 5526 * @throws {RangeError} Value must be an integer between 0 and 127. 5527 * 5528 * @return {Output} Returns the `Output` object so methods can be chained. 5529 * 5530 */ 5531 sendChannelMode(command, value = 0, options = {}, legacy = {}) { 5532 if (wm.validation) { 5533 // Legacy compatibility 5534 if (Array.isArray(options) || Number.isInteger(options) || options === "all") { 5535 const channels = options; 5536 options = legacy; 5537 options.channels = channels; 5538 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5539 } 5540 } 5541 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5542 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5543 this.channels[ch].sendChannelMode(command, value, options); 5544 }); 5545 return this; 5546 } 5547 5548 /** 5549 * Sends an **all sound off** channel mode message. This will silence all sounds playing on that 5550 * channel but will not prevent new sounds from being triggered. 5551 * 5552 * @param {Object} [options={}] 5553 * 5554 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5555 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5556 * channel is specified, all channels will be used. 5557 * 5558 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5559 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5560 * positive number 5561 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5562 * the operation will be scheduled for that time. The current time can be retrieved with 5563 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5564 * operation will be carried out as soon as possible. 5565 * 5566 * @returns {Output} 5567 * 5568 * @since 3.0.0 5569 */ 5570 sendAllSoundOff(options = {}) { 5571 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5572 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5573 this.channels[ch].sendAllSoundOff(options); 5574 }); 5575 return this; 5576 } 5577 5578 /** 5579 * Sends an **all notes off** channel mode message. This will make all currently playing notes 5580 * fade out just as if their key had been released. This is different from the 5581 * [`sendAllSoundOff()`]{@link #sendAllSoundOff} method which mutes all sounds immediately. 5582 * 5583 * @param {Object} [options={}] 5584 * 5585 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5586 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5587 * channel is specified, all channels will be used. 5588 * 5589 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5590 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5591 * positive number 5592 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5593 * the operation will be scheduled for that time. The current time can be retrieved with 5594 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5595 * operation will be carried out as soon as possible. 5596 * 5597 * @returns {Output} 5598 * 5599 * @since 3.0.0 5600 */ 5601 sendAllNotesOff(options = {}) { 5602 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5603 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5604 this.channels[ch].sendAllNotesOff(options); 5605 }); 5606 return this; 5607 } 5608 5609 /** 5610 * Sends a **reset all controllers** channel mode message. This resets all controllers, such as 5611 * the pitch bend, to their default value. 5612 * 5613 * @param {Object} [options={}] 5614 * 5615 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5616 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5617 * channel is specified, all channels will be used. 5618 * 5619 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5620 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5621 * positive number 5622 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5623 * the operation will be scheduled for that time. The current time can be retrieved with 5624 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5625 * operation will be carried out as soon as possible. 5626 * 5627 * @returns {Output} 5628 */ 5629 sendResetAllControllers(options = {}, legacy = {}) { 5630 if (wm.validation) { 5631 // Legacy compatibility 5632 if (Array.isArray(options) || Number.isInteger(options) || options === "all") { 5633 const channels = options; 5634 options = legacy; 5635 options.channels = channels; 5636 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5637 } 5638 } 5639 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5640 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5641 this.channels[ch].sendResetAllControllers(options); 5642 }); 5643 return this; 5644 } 5645 5646 /** 5647 * Sets the polyphonic mode. In `poly` mode (usually the default), multiple notes can be played 5648 * and heard at the same time. In `mono` mode, only one note will be heard at once even if 5649 * multiple notes are being played. 5650 * 5651 * @param mode {string} The mode to use: `mono` or `poly`. 5652 * 5653 * @param {Object} [options={}] 5654 * 5655 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5656 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5657 * channel is specified, all channels will be used. 5658 * 5659 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5660 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5661 * positive number 5662 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5663 * the operation will be scheduled for that time. The current time can be retrieved with 5664 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5665 * operation will be carried out as soon as possible. 5666 * 5667 * @return {Output} Returns the `Output` object so methods can be chained. 5668 * 5669 * @since 3.0.0 5670 */ 5671 sendPolyphonicMode(mode, options = {}, legacy = {}) { 5672 if (wm.validation) { 5673 // Legacy compatibility 5674 if (Array.isArray(options) || Number.isInteger(options) || options === "all") { 5675 const channels = options; 5676 options = legacy; 5677 options.channels = channels; 5678 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5679 } 5680 } 5681 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5682 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5683 this.channels[ch].sendPolyphonicMode(mode, options); 5684 }); 5685 return this; 5686 } 5687 5688 /** 5689 * Turns local control on or off. Local control is usually enabled by default. If you disable it, 5690 * the instrument will no longer trigger its own sounds. It will only send the MIDI messages to 5691 * its out port. 5692 * 5693 * @param [state=false] {boolean} Whether to activate local control (`true`) or disable it 5694 * (`false`). 5695 * 5696 * @param {Object} [options={}] 5697 * 5698 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5699 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5700 * channel is specified, all channels will be used. 5701 * 5702 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5703 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5704 * positive number 5705 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5706 * the operation will be scheduled for that time. The current time can be retrieved with 5707 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5708 * operation will be carried out as soon as possible. 5709 * 5710 * @return {Output} Returns the `Output` object so methods can be chained. 5711 * 5712 * @since 3.0.0 5713 */ 5714 sendLocalControl(state, options = {}, legacy = {}) { 5715 if (wm.validation) { 5716 // Legacy compatibility 5717 if (Array.isArray(options) || Number.isInteger(options) || options === "all") { 5718 const channels = options; 5719 options = legacy; 5720 options.channels = channels; 5721 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5722 } 5723 } 5724 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5725 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5726 this.channels[ch].sendLocalControl(state, options); 5727 }); 5728 return this; 5729 } 5730 5731 /** 5732 * Sets OMNI mode to **on** or **off** for the specified channel(s). MIDI's OMNI mode causes the 5733 * instrument to respond to messages from all channels. 5734 * 5735 * It should be noted that support for OMNI mode is not as common as it used to be. 5736 * 5737 * @param [state] {boolean} Whether to activate OMNI mode (`true`) or not (`false`). 5738 * 5739 * @param {Object} [options={}] 5740 * 5741 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5742 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5743 * channel is specified, all channels will be used. 5744 * 5745 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5746 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5747 * positive number 5748 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5749 * the operation will be scheduled for that time. The current time can be retrieved with 5750 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5751 * operation will be carried out as soon as possible. 5752 * 5753 * @throws {TypeError} Invalid channel mode message name. 5754 * @throws {RangeError} Channel mode controller numbers must be between 120 and 127. 5755 * @throws {RangeError} Value must be an integer between 0 and 127. 5756 * 5757 * @return {Output} Returns the `Output` object so methods can be chained. 5758 * 5759 * @since 3.0.0 5760 */ 5761 sendOmniMode(state, options = {}, legacy = {}) { 5762 if (wm.validation) { 5763 // Legacy compatibility 5764 if (Array.isArray(options) || Number.isInteger(options) || options === "all") { 5765 const channels = options; 5766 options = legacy; 5767 options.channels = channels; 5768 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5769 } 5770 } 5771 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5772 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5773 this.channels[ch].sendOmniMode(state, options); 5774 }); 5775 return this; 5776 } 5777 5778 /** 5779 * Sets a non-registered parameter to the specified value. The NRPN is selected by passing a 5780 * two-position array specifying the values of the two control bytes. The value is specified by 5781 * passing a single integer (most cases) or an array of two integers. 5782 * 5783 * NRPNs are not standardized in any way. Each manufacturer is free to implement them any way 5784 * they see fit. For example, according to the Roland GS specification, you can control the 5785 * **vibrato rate** using NRPN (`1`, `8`). Therefore, to set the **vibrato rate** value to `123` 5786 * you would use: 5787 * 5788 * ```js 5789 * WebMidi.outputs[0].sendNrpnValue([1, 8], 123); 5790 * ``` 5791 * 5792 * You probably want to should select a channel so the message is not sent to all channels. For 5793 * instance, to send to channel `1` of the first output port, you would use: 5794 * 5795 * ```js 5796 * WebMidi.outputs[0].sendNrpnValue([1, 8], 123, 1); 5797 * ``` 5798 * 5799 * In some rarer cases, you need to send two values with your NRPN messages. In such cases, you 5800 * would use a 2-position array. For example, for its **ClockBPM** parameter (`2`, `63`), Novation 5801 * uses a 14-bit value that combines an MSB and an LSB (7-bit values). So, for example, if the 5802 * value to send was `10`, you could use: 5803 * 5804 * ```js 5805 * WebMidi.outputs[0].sendNrpnValue([2, 63], [0, 10], 1); 5806 * ``` 5807 * 5808 * For further implementation details, refer to the manufacturer's documentation. 5809 * 5810 * @param parameter {number[]} A two-position array specifying the two control bytes (`0x63`, 5811 * `0x62`) that identify the non-registered parameter. 5812 * 5813 * @param [data=[]] {number|number[]} An integer or an array of integers with a length of 1 or 2 5814 * specifying the desired data. 5815 * 5816 * @param {Object} [options={}] 5817 * 5818 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5819 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5820 * channel is specified, all channels will be used. 5821 * 5822 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5823 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5824 * positive number 5825 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5826 * the operation will be scheduled for that time. The current time can be retrieved with 5827 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5828 * operation will be carried out as soon as possible. 5829 * 5830 * @throws {RangeError} The control value must be between 0 and 127. 5831 * @throws {RangeError} The msb value must be between 0 and 127 5832 * 5833 * @returns {Output} Returns the `Output` object so methods can be chained. 5834 */ 5835 sendNrpnValue(parameter, data, options = {}) { 5836 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5837 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5838 this.channels[ch].sendNrpnValue(parameter, data, options); 5839 }); 5840 return this; 5841 } 5842 5843 /** 5844 * @private 5845 * @deprecated since version 3.0 5846 */ 5847 setNonRegisteredParameter(parameter, data = [], channel = "all", options = {}) { 5848 if (wm.validation) { 5849 console.warn("The setNonRegisteredParameter() method is deprecated. Use sendNrpnValue() instead."); 5850 options.channels = channel; 5851 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5852 } 5853 return this.sendNrpnValue(parameter, data, options); 5854 } 5855 5856 /** 5857 * Increments the specified MIDI registered parameter by 1. Here is the full list of parameter 5858 * names that can be used with this method: 5859 * 5860 * * Pitchbend Range (0x00, 0x00): `"pitchbendrange"` 5861 * * Channel Fine Tuning (0x00, 0x01): `"channelfinetuning"` 5862 * * Channel Coarse Tuning (0x00, 0x02): `"channelcoarsetuning"` 5863 * * Tuning Program (0x00, 0x03): `"tuningprogram"` 5864 * * Tuning Bank (0x00, 0x04): `"tuningbank"` 5865 * * Modulation Range (0x00, 0x05): `"modulationrange"` 5866 * * Azimuth Angle (0x3D, 0x00): `"azimuthangle"` 5867 * * Elevation Angle (0x3D, 0x01): `"elevationangle"` 5868 * * Gain (0x3D, 0x02): `"gain"` 5869 * * Distance Ratio (0x3D, 0x03): `"distanceratio"` 5870 * * Maximum Distance (0x3D, 0x04): `"maximumdistance"` 5871 * * Maximum Distance Gain (0x3D, 0x05): `"maximumdistancegain"` 5872 * * Reference Distance Ratio (0x3D, 0x06): `"referencedistanceratio"` 5873 * * Pan Spread Angle (0x3D, 0x07): `"panspreadangle"` 5874 * * Roll Angle (0x3D, 0x08): `"rollangle"` 5875 * 5876 * @param parameter {String|number[]} A string identifying the parameter's name (see above) or a 5877 * two-position array specifying the two control bytes (0x65, 0x64) that identify the registered 5878 * parameter. 5879 * 5880 * @param {Object} [options={}] 5881 * 5882 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5883 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5884 * channel is specified, all channels will be used. 5885 * 5886 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5887 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5888 * positive number 5889 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5890 * the operation will be scheduled for that time. The current time can be retrieved with 5891 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5892 * operation will be carried out as soon as possible. 5893 * 5894 * @returns {Output} Returns the `Output` object so methods can be chained. 5895 */ 5896 sendRpnIncrement(parameter, options = {}) { 5897 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5898 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5899 this.channels[ch].sendRpnIncrement(parameter, options); 5900 }); 5901 return this; 5902 } 5903 5904 /** 5905 * @private 5906 * @deprecated since version 3.0 5907 */ 5908 incrementRegisteredParameter(parameter, channel = "all", options = {}) { 5909 if (wm.validation) { 5910 console.warn("The incrementRegisteredParameter() method is deprecated. Use sendRpnIncrement() instead."); 5911 options.channels = channel; 5912 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5913 } 5914 return this.sendRpnIncrement(parameter, options); 5915 } 5916 5917 /** 5918 * Decrements the specified MIDI registered parameter by 1. Here is the full list of parameter 5919 * names that can be used with this method: 5920 * 5921 * * Pitchbend Range (0x00, 0x00): `"pitchbendrange"` 5922 * * Channel Fine Tuning (0x00, 0x01): `"channelfinetuning"` 5923 * * Channel Coarse Tuning (0x00, 0x02): `"channelcoarsetuning"` 5924 * * Tuning Program (0x00, 0x03): `"tuningprogram"` 5925 * * Tuning Bank (0x00, 0x04): `"tuningbank"` 5926 * * Modulation Range (0x00, 0x05): `"modulationrange"` 5927 * * Azimuth Angle (0x3D, 0x00): `"azimuthangle"` 5928 * * Elevation Angle (0x3D, 0x01): `"elevationangle"` 5929 * * Gain (0x3D, 0x02): `"gain"` 5930 * * Distance Ratio (0x3D, 0x03): `"distanceratio"` 5931 * * Maximum Distance (0x3D, 0x04): `"maximumdistance"` 5932 * * Maximum Distance Gain (0x3D, 0x05): `"maximumdistancegain"` 5933 * * Reference Distance Ratio (0x3D, 0x06): `"referencedistanceratio"` 5934 * * Pan Spread Angle (0x3D, 0x07): `"panspreadangle"` 5935 * * Roll Angle (0x3D, 0x08): `"rollangle"` 5936 * 5937 * @param parameter {String|number[]} A string identifying the parameter's name (see above) or a 5938 * two-position array specifying the two control bytes (0x65, 0x64) that identify the registered 5939 * parameter. 5940 * 5941 * @param {Object} [options={}] 5942 * 5943 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 5944 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 5945 * channel is specified, all channels will be used. 5946 * 5947 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 5948 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 5949 * positive number 5950 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 5951 * the operation will be scheduled for that time. The current time can be retrieved with 5952 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 5953 * operation will be carried out as soon as possible. 5954 * 5955 * @throws TypeError The specified parameter is not available. 5956 * 5957 * @returns {Output} Returns the `Output` object so methods can be chained. 5958 */ 5959 sendRpnDecrement(parameter, options = {}) { 5960 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5961 Utilities.sanitizeChannels(options.channels).forEach(ch => { 5962 this.channels[ch].sendRpnDecrement(parameter, options); 5963 }); 5964 return this; 5965 } 5966 5967 /** 5968 * @private 5969 * @deprecated since version 3.0 5970 */ 5971 decrementRegisteredParameter(parameter, channel = "all", options = {}) { 5972 if (wm.validation) { 5973 console.warn("The decrementRegisteredParameter() method is deprecated. Use sendRpnDecrement() instead."); 5974 options.channels = channel; 5975 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 5976 } 5977 return this.sendRpnDecrement(parameter, options); 5978 } 5979 5980 /** 5981 * Sends a **note off** message for the specified MIDI note number on the specified channel(s). 5982 * The first parameter is the note to stop. It can be a single value or an array of the following 5983 * valid values: 5984 * 5985 * - A MIDI note number (integer between `0` and `127`) 5986 * - A note identifier (e.g. `"C3"`, `"G#4"`, `"F-1"`, `"Db7"`) 5987 * - A [`Note`](Note) object 5988 * 5989 * The execution of the **note off** command can be delayed by using the `time` property of the 5990 * `options` parameter. 5991 * 5992 * @param note {number|Note|string|number[]|Note[]|string[]} The note(s) to stop. The notes can be 5993 * specified by using a MIDI note number (`0` - `127`), a note identifier (e.g. `C3`, `G#4`, 5994 * `F-1`, `Db7`) or an array of the previous types. When using a note identifier, octave range 5995 * must be between `-1` and `9`. The lowest note is `C-1` (MIDI note number `0`) and the highest 5996 * note is `G9` (MIDI note number `127`). 5997 * 5998 * @param {Object} [options={}] 5999 * 6000 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 6001 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 6002 * channel is specified, all channels will be used. 6003 * 6004 * @param {number} [options.release=0.5] The velocity at which to release the note 6005 * (between `0` and `1`). If the `rawRelease` option is also defined, `rawRelease` will have 6006 * priority. An invalid velocity value will silently trigger the default of `0.5`. 6007 * 6008 * @param {number} [options.rawRelease=64] The velocity at which to release the note 6009 * (between `0` and `127`). If the `release` option is also defined, `rawRelease` will have 6010 * priority. An invalid velocity value will silently trigger the default of `64`. 6011 * 6012 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 6013 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 6014 * positive number 6015 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 6016 * the operation will be scheduled for that time. The current time can be retrieved with 6017 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 6018 * operation will be carried out as soon as possible. 6019 * 6020 * @returns {Output} Returns the `Output` object so methods can be chained. 6021 */ 6022 sendNoteOff(note, options = {}, legacy = {}) { 6023 if (wm.validation) { 6024 // Legacy compatibility 6025 if (Array.isArray(options) || Number.isInteger(options) || options === "all") { 6026 const channels = options; 6027 options = legacy; 6028 options.channels = channels; 6029 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 6030 } 6031 } 6032 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 6033 Utilities.sanitizeChannels(options.channels).forEach(ch => { 6034 this.channels[ch].sendNoteOff(note, options); 6035 }); 6036 return this; 6037 } 6038 6039 /** 6040 * Sends a **note off** message for the specified MIDI note number on the specified channel(s). 6041 * The first parameter is the note to stop. It can be a single value or an array of the following 6042 * valid values: 6043 * 6044 * - A MIDI note number (integer between `0` and `127`) 6045 * - A note identifier (e.g. `"C3"`, `"G#4"`, `"F-1"`, `"Db7"`) 6046 * - A [`Note`](Note) object 6047 * 6048 * The execution of the **note off** command can be delayed by using the `time` property of the 6049 * `options` parameter. 6050 * 6051 * @param note {number|Note|string|number[]|Note[]|string[]} The note(s) to stop. The notes can be 6052 * specified by using a MIDI note number (`0` - `127`), a note identifier (e.g. `C3`, `G#4`, `F-1`, 6053 * `Db7`) or an array of the previous types. When using a note identifier, octave range must be 6054 * between `-1` and `9`. The lowest note is `C-1` (MIDI note number `0`) and the highest note is 6055 * `G9` (MIDI note number `127`). 6056 * 6057 * @param {Object} [options={}] 6058 * 6059 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 6060 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 6061 * channel is specified, all channels will be used. 6062 * 6063 * @param {number} [options.release=0.5] The velocity at which to release the note 6064 * (between `0` and `1`). If the `rawRelease` option is also defined, `rawRelease` will have 6065 * priority. An invalid velocity value will silently trigger the default of `0.5`. 6066 * 6067 * @param {number} [options.rawRelease=64] The velocity at which to release the note 6068 * (between `0` and `127`). If the `release` option is also defined, `rawRelease` will have 6069 * priority. An invalid velocity value will silently trigger the default of `64`. 6070 * 6071 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 6072 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 6073 * positive number 6074 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 6075 * the operation will be scheduled for that time. The current time can be retrieved with 6076 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 6077 * operation will be carried out as soon as possible. 6078 * 6079 * @returns {Output} Returns the `Output` object so methods can be chained. 6080 */ 6081 stopNote(note, options) { 6082 return this.sendNoteOff(note, options); 6083 } 6084 6085 /** 6086 * Plays a note or an array of notes on one or more channels of this output. If you intend to play 6087 * notes on a single channel, you should probably use 6088 * [`OutputChannel.playNote()`](OutputChannel#playNote) instead. 6089 * 6090 * The first parameter is the note to play. It can be a single value or an array of the following 6091 * valid values: 6092 * 6093 * - A MIDI note number (integer between `0` and `127`) 6094 * - A note identifier (e.g. `"C3"`, `"G#4"`, `"F-1"`, `"Db7"`) 6095 * - A [`Note`]{@link Note} object 6096 * 6097 * The `playNote()` method sends a **note on** MIDI message for all specified notes on all 6098 * specified channels. If no channel is specified, it will send to all channels. If a `duration` 6099 * is set in the `options` parameter or in the [`Note`]{@link Note} object's 6100 * [`duration`]{@link Note#duration} property, it will also schedule a **note off** message to end 6101 * the note after said duration. If no `duration` is set, the note will simply play until a 6102 * matching **note off** message is sent with [`stopNote()`]{@link #stopNote}. 6103 * 6104 * The execution of the **note on** command can be delayed by using the `time` property of the 6105 * `options` parameter. 6106 * 6107 * When using [`Note`]{@link Note} objects, the durations and velocities defined in the 6108 * [`Note`]{@link Note} objects have precedence over the ones specified via the method's `options` 6109 * parameter. 6110 * 6111 * **Note**: As per the MIDI standard, a **note on** message with an attack velocity of `0` is 6112 * functionally equivalent to a **note off** message. 6113 * 6114 * @param note {number|string|Note|number[]|string[]|Note[]} The note(s) to play. The notes can be 6115 * specified by using a MIDI note number (0-127), a note identifier (e.g. C3, G#4, F-1, Db7), a 6116 * [`Note`]{@link Note} object or an array of the previous types. When using a note identifier, 6117 * octave range must be between -1 and 9. The lowest note is C-1 (MIDI note number `0`) and the 6118 * highest note is G9 (MIDI note number `127`). 6119 * 6120 * @param {Object} [options={}] 6121 * 6122 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 6123 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 6124 * channel is specified, all channels will be used. 6125 * 6126 * @param {number} [options.duration=undefined] The number of milliseconds after which a 6127 * **note off** message will be scheduled. If left undefined, only a **note on** message is sent. 6128 * 6129 * @param {number} [options.attack=0.5] The velocity at which to play the note (between `0` and 6130 * `1`). If the `rawAttack` option is also defined, it will have priority. An invalid velocity 6131 * value will silently trigger the default of `0.5`. 6132 * 6133 * @param {number} [options.rawAttack=64] The attack velocity at which to play the note (between 6134 * `0` and `127`). This has priority over the `attack` property. An invalid velocity value will 6135 * silently trigger the default of 64. 6136 * 6137 * @param {number} [options.release=0.5] The velocity at which to release the note (between `0` 6138 * and `1`). If the `rawRelease` option is also defined, it will have priority. An invalid 6139 * velocity value will silently trigger the default of `0.5`. This is only used with the 6140 * **note off** event triggered when `options.duration` is set. 6141 * 6142 * @param {number} [options.rawRelease=64] The velocity at which to release the note (between `0` 6143 * and `127`). This has priority over the `release` property. An invalid velocity value will 6144 * silently trigger the default of 64. This is only used with the **note off** event triggered 6145 * when `options.duration` is set. 6146 * 6147 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 6148 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 6149 * positive number 6150 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 6151 * the operation will be scheduled for that time. The current time can be retrieved with 6152 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 6153 * operation will be carried out as soon as possible. 6154 * 6155 * @returns {Output} Returns the `Output` object so methods can be chained. 6156 */ 6157 playNote(note, options = {}, legacy = {}) { 6158 if (wm.validation) { 6159 // Legacy-compatibility warnings 6160 if (options.rawVelocity) { 6161 console.warn("The 'rawVelocity' option is deprecated. Use 'rawAttack' instead."); 6162 } 6163 if (options.velocity) { 6164 console.warn("The 'velocity' option is deprecated. Use 'velocity' instead."); 6165 } 6166 6167 // Legacy compatibility 6168 if (Array.isArray(options) || Number.isInteger(options) || options === "all") { 6169 const channels = options; 6170 options = legacy; 6171 options.channels = channels; 6172 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 6173 } 6174 } 6175 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 6176 Utilities.sanitizeChannels(options.channels).forEach(ch => { 6177 this.channels[ch].playNote(note, options); 6178 }); 6179 return this; 6180 } 6181 6182 /** 6183 * Sends a **note on** message for the specified MIDI note number on the specified channel(s). The 6184 * first parameter is the number. It can be a single value or an array of the following valid 6185 * values: 6186 * 6187 * - A MIDI note number (integer between `0` and `127`) 6188 * - A note identifier (e.g. `"C3"`, `"G#4"`, `"F-1"`, `"Db7"`) 6189 * - A [`Note`](Note) object 6190 * 6191 * The execution of the **note on** command can be delayed by using the `time` property of the 6192 * `options` parameter. 6193 * 6194 * **Note**: As per the MIDI standard, a **note on** message with an attack velocity of `0` is 6195 * functionally equivalent to a **note off** message. 6196 * 6197 * @param note {number|Note|string|number[]|Note[]|string[]} The note(s) to stop. The notes can be 6198 * specified by using a MIDI note number (`0` - `127`), a note identifier (e.g. `C3`, `G#4`, `F-1`, 6199 * `Db7`) or an array of the previous types. When using a note identifier, octave range must be 6200 * between `-1` and `9`. The lowest note is `C-1` (MIDI note number `0`) and the highest note is 6201 * `G9` (MIDI note number `127`). 6202 * 6203 * @param {Object} [options={}] 6204 * 6205 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 6206 * The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no 6207 * channel is specified, all channels will be used. 6208 * 6209 * @param {number} [options.attack=0.5] The velocity at which to play the note (between `0` and 6210 * `1`). If the `rawAttack` option is also defined, `rawAttack` will have priority. An invalid 6211 * velocity value will silently trigger the default of `0.5`. 6212 * 6213 * @param {number} [options.rawAttack=64] The velocity at which to release the note (between `0` 6214 * and `127`). If the `attack` option is also defined, `rawAttack` will have priority. An invalid 6215 * velocity value will silently trigger the default of `64`. 6216 * 6217 * @param {number|string} [options.time=(now)] If `time` is a string prefixed with `"+"` and 6218 * followed by a number, the message will be delayed by that many milliseconds. If the value is a 6219 * positive number 6220 * ([`DOMHighResTimeStamp`]{@link https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp}), 6221 * the operation will be scheduled for that time. The current time can be retrieved with 6222 * [`WebMidi.time`]{@link WebMidi#time}. If `options.time` is omitted, or in the past, the 6223 * operation will be carried out as soon as possible. 6224 * 6225 * @returns {Output} Returns the `Output` object so methods can be chained. 6226 */ 6227 sendNoteOn(note, options = {}, legacy = {}) { 6228 if (wm.validation) { 6229 // Legacy compatibility 6230 if (Array.isArray(options) || Number.isInteger(options) || options === "all") { 6231 const channels = options; 6232 options = legacy; 6233 options.channels = channels; 6234 if (options.channels === "all") options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 6235 } 6236 } 6237 if (options.channels == undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 6238 6239 // This actually supports passing a Note object even if, semantically, this does not make sense. 6240 Utilities.sanitizeChannels(options.channels).forEach(ch => { 6241 this.channels[ch].sendNoteOn(note, options); 6242 }); 6243 return this; 6244 } 6245 6246 /** 6247 * Name of the MIDI output. 6248 * 6249 * @type {string} 6250 * @readonly 6251 */ 6252 get name() { 6253 return this._midiOutput.name; 6254 } 6255 6256 /** 6257 * ID string of the MIDI output. The ID is host-specific. Do not expect the same ID on different 6258 * platforms. For example, Google Chrome and the Jazz-Plugin report completely different IDs for 6259 * the same port. 6260 * 6261 * @type {string} 6262 * @readonly 6263 */ 6264 get id() { 6265 return this._midiOutput.id; 6266 } 6267 6268 /** 6269 * Output port's connection state: `pending`, `open` or `closed`. 6270 * 6271 * @type {string} 6272 * @readonly 6273 */ 6274 get connection() { 6275 return this._midiOutput.connection; 6276 } 6277 6278 /** 6279 * Name of the manufacturer of the device that makes this output port available. 6280 * 6281 * @type {string} 6282 * @readonly 6283 */ 6284 get manufacturer() { 6285 return this._midiOutput.manufacturer; 6286 } 6287 6288 /** 6289 * State of the output port: `connected` or `disconnected`. 6290 * 6291 * @type {string} 6292 * @readonly 6293 */ 6294 get state() { 6295 return this._midiOutput.state; 6296 } 6297 6298 /** 6299 * Type of the output port (it will always be: `output`). 6300 * 6301 * @type {string} 6302 * @readonly 6303 */ 6304 get type() { 6305 return this._midiOutput.type; 6306 } 6307 6308 /** 6309 * An integer to offset the octave of outgoing notes. By default, middle C (MIDI note number 60) 6310 * is placed on the 4th octave (C4). 6311 * 6312 * Note that this value is combined with the global offset value defined in 6313 * [`WebMidi.octaveOffset`](WebMidi#octaveOffset) (if any). 6314 * 6315 * @type {number} 6316 * 6317 * @since 3.0 6318 */ 6319 get octaveOffset() { 6320 return this._octaveOffset; 6321 } 6322 set octaveOffset(value) { 6323 if (this.validation) { 6324 value = parseInt(value); 6325 if (isNaN(value)) throw new TypeError("The 'octaveOffset' property must be an integer."); 6326 } 6327 this._octaveOffset = value; 6328 } 6329 } 6330 6331 /** 6332 * The `Forwarder` class allows the forwarding of MIDI messages to predetermined outputs. When you 6333 * call its [`forward()`](#forward) method, it will send the specified [`Message`](Message) object 6334 * to all the outputs listed in its [`destinations`](#destinations) property. 6335 * 6336 * If specific channels or message types have been defined in the [`channels`](#channels) or 6337 * [`types`](#types) properties, only messages matching the channels/types will be forwarded. 6338 * 6339 * While it can be manually instantiated, you are more likely to come across a `Forwarder` object as 6340 * the return value of the [`Input.addForwarder()`](Input#addForwarder) method. 6341 * 6342 * @license Apache-2.0 6343 * @since 3.0.0 6344 */ 6345 class Forwarder { 6346 /** 6347 * Creates a `Forwarder` object. 6348 * 6349 * @param {Output|Output[]} [destinations=\[\]] An [`Output`](Output) object, or an array of such 6350 * objects, to forward the message to. 6351 * 6352 * @param {object} [options={}] 6353 * @param {string|string[]} [options.types=(all messages)] A MIDI message type or an array of such 6354 * types (`"noteon"`, `"controlchange"`, etc.), that the specified message must match in order to 6355 * be forwarded. If this option is not specified, all types of messages will be forwarded. Valid 6356 * messages are the ones found in either 6357 * [`SYSTEM_MESSAGES`](Enumerations#SYSTEM_MESSAGES) 6358 * or [`CHANNEL_MESSAGES`](Enumerations#CHANNEL_MESSAGES). 6359 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 6360 * A MIDI channel number or an array of channel numbers that the message must match in order to be 6361 * forwarded. By default all MIDI channels are included (`1` to `16`). 6362 */ 6363 constructor(destinations = [], options = {}) { 6364 /** 6365 * An array of [`Output`](Output) objects to forward the message to. 6366 * @type {Output[]} 6367 */ 6368 this.destinations = []; 6369 6370 /** 6371 * An array of message types (`"noteon"`, `"controlchange"`, etc.) that must be matched in order 6372 * for messages to be forwarded. By default, this array includes all 6373 * [`Enumerations.SYSTEM_MESSAGES`](Enumerations#SYSTEM_MESSAGES) and 6374 * [`Enumerations.CHANNEL_MESSAGES`](Enumerations#CHANNEL_MESSAGES). 6375 * @type {string[]} 6376 */ 6377 this.types = [...Object.keys(Enumerations.SYSTEM_MESSAGES), ...Object.keys(Enumerations.CHANNEL_MESSAGES)]; 6378 6379 /** 6380 * An array of MIDI channel numbers that the message must match in order to be forwarded. By 6381 * default, this array includes all MIDI channels (`1` to `16`). 6382 * @type {number[]} 6383 */ 6384 this.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 6385 6386 /** 6387 * Indicates whether message forwarding is currently suspended or not in this forwarder. 6388 * @type {boolean} 6389 */ 6390 this.suspended = false; 6391 6392 // Make sure parameters are arrays 6393 if (!Array.isArray(destinations)) destinations = [destinations]; 6394 if (options.types && !Array.isArray(options.types)) options.types = [options.types]; 6395 if (options.channels && !Array.isArray(options.channels)) options.channels = [options.channels]; 6396 if (wm.validation) { 6397 // Validate destinations 6398 destinations.forEach(destination => { 6399 if (!(destination instanceof Output)) { 6400 throw new TypeError("Destinations must be of type 'Output'."); 6401 } 6402 }); 6403 6404 // Validate types 6405 if (options.types !== undefined) { 6406 options.types.forEach(type => { 6407 if (!Enumerations.SYSTEM_MESSAGES.hasOwnProperty(type) && !Enumerations.CHANNEL_MESSAGES.hasOwnProperty(type)) { 6408 throw new TypeError("Type must be a valid message type."); 6409 } 6410 }); 6411 } 6412 6413 // Validate channels 6414 if (options.channels !== undefined) { 6415 options.channels.forEach(channel => { 6416 if (!Enumerations.MIDI_CHANNEL_NUMBERS.includes(channel)) { 6417 throw new TypeError("MIDI channel must be between 1 and 16."); 6418 } 6419 }); 6420 } 6421 } 6422 this.destinations = destinations; 6423 if (options.types) this.types = options.types; 6424 if (options.channels) this.channels = options.channels; 6425 } 6426 6427 /** 6428 * Sends the specified message to the forwarder's destination(s) if it matches the specified 6429 * type(s) and channel(s). 6430 * 6431 * @param {Message} message The [`Message`](Message) object to forward. 6432 */ 6433 forward(message) { 6434 // Abort if forwarding is currently suspended 6435 if (this.suspended) return; 6436 6437 // Abort if this message type should not be forwarded 6438 if (!this.types.includes(message.type)) return; 6439 6440 // Abort if this channel should not be forwarded 6441 if (message.channel && !this.channels.includes(message.channel)) return; 6442 6443 // Forward 6444 this.destinations.forEach(destination => { 6445 if (wm.validation && !(destination instanceof Output)) return; 6446 destination.send(message); 6447 }); 6448 } 6449 } 6450 6451 /** 6452 * The `InputChannel` class represents a single MIDI input channel (1-16) from a single input 6453 * device. This object is derived from the host's MIDI subsystem and should not be instantiated 6454 * directly. 6455 * 6456 * All 16 `InputChannel` objects can be found inside the input's [`channels`](Input#channels) 6457 * property. 6458 * 6459 * @fires InputChannel#midimessage 6460 * @fires InputChannel#unknownmessage 6461 * 6462 * @fires InputChannel#noteoff 6463 * @fires InputChannel#noteon 6464 * @fires InputChannel#keyaftertouch 6465 * @fires InputChannel#programchange 6466 * @fires InputChannel#channelaftertouch 6467 * @fires InputChannel#pitchbend 6468 * 6469 * @fires InputChannel#allnotesoff 6470 * @fires InputChannel#allsoundoff 6471 * @fires InputChannel#localcontrol 6472 * @fires InputChannel#monomode 6473 * @fires InputChannel#omnimode 6474 * @fires InputChannel#resetallcontrollers 6475 * 6476 * @fires InputChannel#event:nrpn 6477 * @fires InputChannel#event:nrpn-dataentrycoarse 6478 * @fires InputChannel#event:nrpn-dataentryfine 6479 * @fires InputChannel#event:nrpn-dataincrement 6480 * @fires InputChannel#event:nrpn-datadecrement 6481 * @fires InputChannel#event:rpn 6482 * @fires InputChannel#event:rpn-dataentrycoarse 6483 * @fires InputChannel#event:rpn-dataentryfine 6484 * @fires InputChannel#event:rpn-dataincrement 6485 * @fires InputChannel#event:rpn-datadecrement 6486 * 6487 * @fires InputChannel#controlchange 6488 * @fires InputChannel#event:controlchange-controllerxxx 6489 * @fires InputChannel#event:controlchange-bankselectcoarse 6490 * @fires InputChannel#event:controlchange-modulationwheelcoarse 6491 * @fires InputChannel#event:controlchange-breathcontrollercoarse 6492 * @fires InputChannel#event:controlchange-footcontrollercoarse 6493 * @fires InputChannel#event:controlchange-portamentotimecoarse 6494 * @fires InputChannel#event:controlchange-dataentrycoarse 6495 * @fires InputChannel#event:controlchange-volumecoarse 6496 * @fires InputChannel#event:controlchange-balancecoarse 6497 * @fires InputChannel#event:controlchange-pancoarse 6498 * @fires InputChannel#event:controlchange-expressioncoarse 6499 * @fires InputChannel#event:controlchange-effectcontrol1coarse 6500 * @fires InputChannel#event:controlchange-effectcontrol2coarse 6501 * @fires InputChannel#event:controlchange-generalpurposecontroller1 6502 * @fires InputChannel#event:controlchange-generalpurposecontroller2 6503 * @fires InputChannel#event:controlchange-generalpurposecontroller3 6504 * @fires InputChannel#event:controlchange-generalpurposecontroller4 6505 * @fires InputChannel#event:controlchange-bankselectfine 6506 * @fires InputChannel#event:controlchange-modulationwheelfine 6507 * @fires InputChannel#event:controlchange-breathcontrollerfine 6508 * @fires InputChannel#event:controlchange-footcontrollerfine 6509 * @fires InputChannel#event:controlchange-portamentotimefine 6510 * @fires InputChannel#event:controlchange-dataentryfine 6511 * @fires InputChannel#event:controlchange-channelvolumefine 6512 * @fires InputChannel#event:controlchange-balancefine 6513 * @fires InputChannel#event:controlchange-panfine 6514 * @fires InputChannel#event:controlchange-expressionfine 6515 * @fires InputChannel#event:controlchange-effectcontrol1fine 6516 * @fires InputChannel#event:controlchange-effectcontrol2fine 6517 * @fires InputChannel#event:controlchange-damperpedal 6518 * @fires InputChannel#event:controlchange-portamento 6519 * @fires InputChannel#event:controlchange-sostenuto 6520 * @fires InputChannel#event:controlchange-softpedal 6521 * @fires InputChannel#event:controlchange-legatopedal 6522 * @fires InputChannel#event:controlchange-hold2 6523 * @fires InputChannel#event:controlchange-soundvariation 6524 * @fires InputChannel#event:controlchange-resonance 6525 * @fires InputChannel#event:controlchange-releasetime 6526 * @fires InputChannel#event:controlchange-attacktime 6527 * @fires InputChannel#event:controlchange-brightness 6528 * @fires InputChannel#event:controlchange-decaytime 6529 * @fires InputChannel#event:controlchange-vibratorate 6530 * @fires InputChannel#event:controlchange-vibratodepth 6531 * @fires InputChannel#event:controlchange-vibratodelay 6532 * @fires InputChannel#event:controlchange-generalpurposecontroller5 6533 * @fires InputChannel#event:controlchange-generalpurposecontroller6 6534 * @fires InputChannel#event:controlchange-generalpurposecontroller7 6535 * @fires InputChannel#event:controlchange-generalpurposecontroller8 6536 * @fires InputChannel#event:controlchange-portamentocontrol 6537 * @fires InputChannel#event:controlchange-highresolutionvelocityprefix 6538 * @fires InputChannel#event:controlchange-effect1depth 6539 * @fires InputChannel#event:controlchange-effect2depth 6540 * @fires InputChannel#event:controlchange-effect3depth 6541 * @fires InputChannel#event:controlchange-effect4depth 6542 * @fires InputChannel#event:controlchange-effect5depth 6543 * @fires InputChannel#event:controlchange-dataincrement 6544 * @fires InputChannel#event:controlchange-datadecrement 6545 * @fires InputChannel#event:controlchange-nonregisteredparameterfine 6546 * @fires InputChannel#event:controlchange-nonregisteredparametercoarse 6547 * @fires InputChannel#event:controlchange-registeredparameterfine 6548 * @fires InputChannel#event:controlchange-registeredparametercoarse 6549 * @fires InputChannel#event:controlchange-allsoundoff 6550 * @fires InputChannel#event:controlchange-resetallcontrollers 6551 * @fires InputChannel#event:controlchange-localcontrol 6552 * @fires InputChannel#event:controlchange-allnotesoff 6553 * @fires InputChannel#event:controlchange-omnimodeoff 6554 * @fires InputChannel#event:controlchange-omnimodeon 6555 * @fires InputChannel#event:controlchange-monomodeon 6556 * @fires InputChannel#event:controlchange-polymodeon 6557 *
6557 @fires InputChannel#event:
vendor: 147,864 bytes, lines 6557-9507
6557 6558 * 6559 * @extends EventEmitter 6560 * @license Apache-2.0 6561 * @since 3.0.0 6562 */ 6563 class InputChannel extends EventEmitter { 6564 /** 6565 * Creates an `InputChannel` object. 6566 * 6567 * @param {Input} input The [`Input`](Input) object this channel belongs to. 6568 * @param {number} number The channel's MIDI number (1-16). 6569 */ 6570 constructor(input, number) { 6571 super(); 6572 6573 /** 6574 * @type {Input} 6575 * @private 6576 */ 6577 this._input = input; 6578 6579 /** 6580 * @type {number} 6581 * @private 6582 */ 6583 this._number = number; 6584 6585 /** 6586 * @type {number} 6587 * @private 6588 */ 6589 this._octaveOffset = 0; 6590 6591 /** 6592 * An array of messages that form the current NRPN sequence 6593 * @private 6594 * @type {Message[]} 6595 */ 6596 this._nrpnBuffer = []; 6597 6598 /** 6599 * An array of messages that form the current RPN sequence 6600 * @private 6601 * @type {Message[]} 6602 */ 6603 this._rpnBuffer = []; 6604 6605 /** 6606 * Indicates whether events for **Registered Parameter Number** and **Non-Registered Parameter 6607 * Number** should be dispatched. RPNs and NRPNs are composed of a sequence of specific 6608 * **control change** messages. When a valid sequence of such control change messages is 6609 * received, an [`rpn`](#event-rpn) or [`nrpn`](#event-nrpn) event will fire. 6610 * 6611 * If an invalid or out-of-order **control change** message is received, it will fall through 6612 * the collector logic and all buffered **control change** messages will be discarded as 6613 * incomplete. 6614 * 6615 * @type {boolean} 6616 */ 6617 this.parameterNumberEventsEnabled = true; 6618 6619 /** 6620 * Contains the current playing state of all MIDI notes of this channel (0-127). The state is 6621 * `true` for a currently playing note and `false` otherwise. 6622 * @type {boolean[]} 6623 */ 6624 this.notesState = new Array(128).fill(false); 6625 } 6626 6627 /** 6628 * Destroys the `InputChannel` by removing all listeners and severing the link with the MIDI 6629 * subsystem's input. 6630 */ 6631 destroy() { 6632 this._input = null; 6633 this._number = null; 6634 this._octaveOffset = 0; 6635 this._nrpnBuffer = []; 6636 this.notesState = new Array(128).fill(false); 6637 this.parameterNumberEventsEnabled = false; 6638 this.removeListener(); 6639 } 6640 6641 /** 6642 * @param e MIDIMessageEvent 6643 * @private 6644 */ 6645 _processMidiMessageEvent(e) { 6646 // Create and emit a new 'midimessage' event based on the incoming one 6647 const event = Object.assign({}, e); 6648 event.port = this.input; 6649 event.target = this; 6650 event.type = "midimessage"; 6651 6652 /** 6653 * Event emitted when a MIDI message of any kind is received by an `InputChannel` 6654 * 6655 * @event InputChannel#midimessage 6656 * 6657 * @type {object} 6658 * 6659 * @property {string} type `midimessage` 6660 * @property {InputChannel} target The object that dispatched the event. 6661 * @property {Input} port The `Input` that triggered the event. 6662 * @property {Message} message A [`Message`](Message) object containing information about the 6663 * incoming MIDI message. 6664 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 6665 * milliseconds since the navigation start of the document). 6666 */ 6667 this.emit(event.type, event); 6668 6669 // Parse the inbound event for regular MIDI messages 6670 this._parseEventForStandardMessages(event); 6671 } 6672 6673 /** 6674 * Parses incoming channel events and emit standard MIDI message events (noteon, noteoff, etc.) 6675 * @param e Event 6676 * @private 6677 */ 6678 _parseEventForStandardMessages(e) { 6679 const event = Object.assign({}, e); 6680 event.type = event.message.type || "unknownmessage"; 6681 const data1 = e.message.dataBytes[0]; 6682 const data2 = e.message.dataBytes[1]; 6683 if (event.type === "noteoff" || event.type === "noteon" && data2 === 0) { 6684 this.notesState[data1] = false; 6685 event.type = "noteoff"; // necessary for note on with 0 velocity 6686 6687 /** 6688 * Event emitted when a **note off** MIDI message has been received on the channel. 6689 * 6690 * @event InputChannel#noteoff 6691 * 6692 * @type {object} 6693 * @property {string} type `noteoff` 6694 * 6695 * @property {InputChannel} target The object that dispatched the event. 6696 * @property {Input} port The `Input` that triggered the event. 6697 * @property {Message} message A [`Message`](Message) object containing information about the incoming 6698 * MIDI message. 6699 * @property {number} timestamp The moment 6700 * ([`DOMHighResTimeStamp`](https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp)) 6701 * when the event occurred (in milliseconds since the navigation start of the document). 6702 * 6703 * @property {object} note A [`Note`](Note) object containing information such as note name, 6704 * octave and release velocity. 6705 * @property {number} value The release velocity amount expressed as a float between 0 and 1. 6706 * @property {number} rawValue The release velocity amount expressed as an integer (between 0 6707 * and 127). 6708 */ 6709 6710 // The object created when a noteoff event arrives is a Note with an attack velocity of 0. 6711 event.note = new Note(Utilities.offsetNumber(data1, this.octaveOffset + this.input.octaveOffset + wm.octaveOffset), { 6712 rawAttack: 0, 6713 rawRelease: data2 6714 }); 6715 event.value = Utilities.from7bitToFloat(data2); 6716 event.rawValue = data2; 6717 6718 // Those are kept for backwards-compatibility but are gone from the documentation. They will 6719 // be removed in future versions (@deprecated). 6720 event.velocity = event.note.release; 6721 event.rawVelocity = event.note.rawRelease; 6722 } else if (event.type === "noteon") { 6723 this.notesState[data1] = true; 6724 6725 /** 6726 * Event emitted when a **note on** MIDI message has been received. 6727 * 6728 * @event InputChannel#noteon 6729 * 6730 * @type {object} 6731 * @property {string} type `noteon` 6732 * @property {InputChannel} target The object that dispatched the event. 6733 * @property {Input} port The `Input` that triggered the event. 6734 * @property {Message} message A [`Message`](Message) object containing information about the 6735 * incoming MIDI message. 6736 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 6737 * milliseconds since the navigation start of the document). 6738 * 6739 * @property {object} note A [`Note`](Note) object containing information such as note name, 6740 * octave and release velocity. 6741 * @property {number} value The attack velocity amount expressed as a float between 0 and 1. 6742 * @property {number} rawValue The attack velocity amount expressed as an integer (between 0 6743 * and 127). 6744 */ 6745 event.note = new Note(Utilities.offsetNumber(data1, this.octaveOffset + this.input.octaveOffset + wm.octaveOffset), { 6746 rawAttack: data2 6747 }); 6748 event.value = Utilities.from7bitToFloat(data2); 6749 event.rawValue = data2; 6750 6751 // Those are kept for backwards-compatibility but are gone from the documentation. They will 6752 // be removed in future versions (@deprecated). 6753 event.velocity = event.note.attack; 6754 event.rawVelocity = event.note.rawAttack; 6755 } else if (event.type === "keyaftertouch") { 6756 /** 6757 * Event emitted when a **key-specific aftertouch** MIDI message has been received. 6758 * 6759 * @event InputChannel#keyaftertouch 6760 * 6761 * @type {object} 6762 * @property {string} type `"keyaftertouch"` 6763 * 6764 * @property {InputChannel} target The object that dispatched the event. 6765 * @property {Input} port The `Input` that triggered the event. 6766 * @property {Message} message A [`Message`](Message) object containing information about the 6767 * incoming MIDI message. 6768 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 6769 * milliseconds since the navigation start of the document). 6770 * 6771 * @property {object} note A [`Note`](Note) object containing information such as note name 6772 * and number. 6773 * @property {number} value The aftertouch amount expressed as a float between 0 and 1. 6774 * @property {number} rawValue The aftertouch amount expressed as an integer (between 0 and 6775 * 127). 6776 */ 6777 event.note = new Note(Utilities.offsetNumber(data1, this.octaveOffset + this.input.octaveOffset + wm.octaveOffset)); 6778 6779 // Aftertouch value 6780 event.value = Utilities.from7bitToFloat(data2); 6781 event.rawValue = data2; 6782 6783 // @deprecated 6784 event.identifier = event.note.identifier; 6785 event.key = event.note.number; 6786 event.rawKey = data1; 6787 } else if (event.type === "controlchange") { 6788 /** 6789 * Event emitted when a **control change** MIDI message has been received. 6790 * 6791 * @event InputChannel#controlchange 6792 * 6793 * @type {object} 6794 * @property {string} type `controlchange` 6795 * @property {string} subtype The type of control change message that was received. 6796 * 6797 * @property {InputChannel} target The object that dispatched the event. 6798 * @property {Input} port The `Input` that triggered the event. 6799 * @property {Message} message A [`Message`](Message) object containing information about the 6800 * incoming MIDI message. 6801 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 6802 * milliseconds since the navigation start of the document). 6803 * 6804 * @property {object} controller 6805 * @property {object} controller.number The number of the controller. 6806 * @property {object} controller.name The usual name or function of the controller. 6807 * @property {object} controller.description A user-friendly representation of the 6808 * controller's default function 6809 * @property {number} value The value expressed as a float between 0 and 1. 6810 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 6811 */ 6812 event.controller = { 6813 number: data1, 6814 name: Enumerations.CONTROL_CHANGE_MESSAGES[data1].name, 6815 description: Enumerations.CONTROL_CHANGE_MESSAGES[data1].description, 6816 position: Enumerations.CONTROL_CHANGE_MESSAGES[data1].position 6817 }; 6818 event.subtype = event.controller.name || "controller" + data1; 6819 event.value = Utilities.from7bitToFloat(data2); 6820 event.rawValue = data2; 6821 6822 /** 6823 * Event emitted when a **control change** MIDI message has been received and that message is 6824 * targeting the controller numbered "xxx". Of course, "xxx" should be replaced by a valid 6825 * controller number (0-127). 6826 * 6827 * @event InputChannel#controlchange-controllerxxx 6828 * 6829 * @type {object} 6830 * @property {string} type `controlchange-controllerxxx` 6831 * @property {string} subtype The type of control change message that was received. 6832 * 6833 * @property {InputChannel} target The object that dispatched the event. 6834 * @property {Input} port The `Input` that triggered the event. 6835 * @property {Message} message A [`Message`](Message) object containing information about the 6836 * incoming MIDI message. 6837 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 6838 * milliseconds since the navigation start of the document). 6839 * 6840 * @property {object} controller 6841 * @property {object} controller.number The number of the controller. 6842 * @property {object} controller.name The usual name or function of the controller. 6843 * @property {object} controller.description A user-friendly representation of the 6844 * controller's default function 6845 * @property {number} value The value expressed as a float between 0 and 1. 6846 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 6847 */ 6848 const numberedEvent = Object.assign({}, event); 6849 numberedEvent.type = `${event.type}-controller${data1}`; 6850 delete numberedEvent.subtype; 6851 this.emit(numberedEvent.type, numberedEvent); 6852 6853 /** 6854 * Event emitted when a **controlchange-bankselectcoarse** MIDI message has been 6855 * received. 6856 * 6857 * @event InputChannel#controlchange-bankselectcoarse 6858 * 6859 * @type {object} 6860 * @property {string} type `controlchange-bankselectcoarse` 6861 * 6862 * @property {InputChannel} target The object that dispatched the event. 6863 * @property {Input} port The `Input` that triggered the event. 6864 * @property {Message} message A [`Message`](Message) object containing information about the 6865 * incoming MIDI message. 6866 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 6867 * milliseconds since the navigation start of the document). 6868 * 6869 * @property {object} controller 6870 * @property {object} controller.number The number of the controller. 6871 * @property {object} controller.name The usual name or function of the controller. 6872 * @property {object} controller.description A user-friendly representation of the 6873 * controller's default function 6874 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 6875 * @property {number} value The value expressed as a float between 0 and 1. 6876 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 6877 */ 6878 6879 /** 6880 * Event emitted when a **controlchange-modulationwheelcoarse** MIDI message has been 6881 * received. 6882 * 6883 * @event InputChannel#controlchange-modulationwheelcoarse 6884 * 6885 * @type {object} 6886 * @property {string} type `controlchange-modulationwheelcoarse` 6887 * 6888 * @property {InputChannel} target The object that dispatched the event. 6889 * @property {Input} port The `Input` that triggered the event. 6890 * @property {Message} message A [`Message`](Message) object containing information about the 6891 * incoming MIDI message. 6892 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 6893 * milliseconds since the navigation start of the document). 6894 * 6895 * @property {object} controller 6896 * @property {object} controller.number The number of the controller. 6897 * @property {object} controller.name The usual name or function of the controller. 6898 * @property {object} controller.description A user-friendly representation of the 6899 * controller's default function 6900 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 6901 * @property {number} value The value expressed as a float between 0 and 1. 6902 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 6903 */ 6904 6905 /** 6906 * Event emitted when a **controlchange-breathcontrollercoarse** MIDI message has been 6907 * received. 6908 * 6909 * @event InputChannel#controlchange-breathcontrollercoarse 6910 * 6911 * @type {object} 6912 * @property {string} type `controlchange-breathcontrollercoarse` 6913 * 6914 * @property {InputChannel} target The object that dispatched the event. 6915 * @property {Input} port The `Input` that triggered the event. 6916 * @property {Message} message A [`Message`](Message) object containing information about the 6917 * incoming MIDI message. 6918 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 6919 * milliseconds since the navigation start of the document). 6920 * 6921 * @property {object} controller 6922 * @property {object} controller.number The number of the controller. 6923 * @property {object} controller.name The usual name or function of the controller. 6924 * @property {object} controller.description A user-friendly representation of the 6925 * controller's default function 6926 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 6927 * @property {number} value The value expressed as a float between 0 and 1. 6928 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 6929 */ 6930 6931 /** 6932 * Event emitted when a **controlchange-footcontrollercoarse** MIDI message has been 6933 * received. 6934 * 6935 * @event InputChannel#controlchange-footcontrollercoarse 6936 * 6937 * @type {object} 6938 * @property {string} type `controlchange-footcontrollercoarse` 6939 * 6940 * @property {InputChannel} target The object that dispatched the event. 6941 * @property {Input} port The `Input` that triggered the event. 6942 * @property {Message} message A [`Message`](Message) object containing information about the 6943 * incoming MIDI message. 6944 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 6945 * milliseconds since the navigation start of the document). 6946 * 6947 * @property {object} controller 6948 * @property {object} controller.number The number of the controller. 6949 * @property {object} controller.name The usual name or function of the controller. 6950 * @property {object} controller.description A user-friendly representation of the 6951 * controller's default function 6952 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 6953 * @property {number} value The value expressed as a float between 0 and 1. 6954 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 6955 */ 6956 6957 /** 6958 * Event emitted when a **controlchange-portamentotimecoarse** MIDI message has been 6959 * received. 6960 * 6961 * @event InputChannel#controlchange-portamentotimecoarse 6962 * 6963 * @type {object} 6964 * @property {string} type `controlchange-portamentotimecoarse` 6965 * 6966 * @property {InputChannel} target The object that dispatched the event. 6967 * @property {Input} port The `Input` that triggered the event. 6968 * @property {Message} message A [`Message`](Message) object containing information about the 6969 * incoming MIDI message. 6970 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 6971 * milliseconds since the navigation start of the document). 6972 * 6973 * @property {object} controller 6974 * @property {object} controller.number The number of the controller. 6975 * @property {object} controller.name The usual name or function of the controller. 6976 * @property {object} controller.description A user-friendly representation of the 6977 * controller's default function 6978 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 6979 * @property {number} value The value expressed as a float between 0 and 1. 6980 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 6981 */ 6982 6983 /** 6984 * Event emitted when a **controlchange-dataentrycoarse** MIDI message has been 6985 * received. 6986 * 6987 * @event InputChannel#controlchange-dataentrycoarse 6988 * 6989 * @type {object} 6990 * @property {string} type `controlchange-dataentrycoarse` 6991 * 6992 * @property {InputChannel} target The object that dispatched the event. 6993 * @property {Input} port The `Input` that triggered the event. 6994 * @property {Message} message A [`Message`](Message) object containing information about the 6995 * incoming MIDI message. 6996 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 6997 * milliseconds since the navigation start of the document). 6998 * 6999 * @property {object} controller 7000 * @property {object} controller.number The number of the controller. 7001 * @property {object} controller.name The usual name or function of the controller. 7002 * @property {object} controller.description A user-friendly representation of the 7003 * controller's default function 7004 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7005 * @property {number} value The value expressed as a float between 0 and 1. 7006 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7007 */ 7008 7009 /** 7010 * Event emitted when a **controlchange-volumecoarse** MIDI message has been 7011 * received. 7012 * 7013 * @event InputChannel#controlchange-volumecoarse 7014 * 7015 * @type {object} 7016 * @property {string} type `controlchange-volumecoarse` 7017 * 7018 * @property {InputChannel} target The object that dispatched the event. 7019 * @property {Input} port The `Input` that triggered the event. 7020 * @property {Message} message A [`Message`](Message) object containing information about the 7021 * incoming MIDI message. 7022 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7023 * milliseconds since the navigation start of the document). 7024 * 7025 * @property {object} controller 7026 * @property {object} controller.number The number of the controller. 7027 * @property {object} controller.name The usual name or function of the controller. 7028 * @property {object} controller.description A user-friendly representation of the 7029 * controller's default function 7030 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7031 * @property {number} value The value expressed as a float between 0 and 1. 7032 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7033 */ 7034 7035 /** 7036 * Event emitted when a **controlchange-balancecoarse** MIDI message has been 7037 * received. 7038 * 7039 * @event InputChannel#controlchange-balancecoarse 7040 * 7041 * @type {object} 7042 * @property {string} type `controlchange-balancecoarse` 7043 * 7044 * @property {InputChannel} target The object that dispatched the event. 7045 * @property {Input} port The `Input` that triggered the event. 7046 * @property {Message} message A [`Message`](Message) object containing information about the 7047 * incoming MIDI message. 7048 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7049 * milliseconds since the navigation start of the document). 7050 * 7051 * @property {object} controller 7052 * @property {object} controller.number The number of the controller. 7053 * @property {object} controller.name The usual name or function of the controller. 7054 * @property {object} controller.description A user-friendly representation of the 7055 * controller's default function 7056 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7057 * @property {number} value The value expressed as a float between 0 and 1. 7058 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7059 */ 7060 7061 /** 7062 * Event emitted when a **controlchange-pancoarse** MIDI message has been 7063 * received. 7064 * 7065 * @event InputChannel#controlchange-pancoarse 7066 * 7067 * @type {object} 7068 * @property {string} type `controlchange-pancoarse` 7069 * 7070 * @property {InputChannel} target The object that dispatched the event. 7071 * @property {Input} port The `Input` that triggered the event. 7072 * @property {Message} message A [`Message`](Message) object containing information about the 7073 * incoming MIDI message. 7074 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7075 * milliseconds since the navigation start of the document). 7076 * 7077 * @property {object} controller 7078 * @property {object} controller.number The number of the controller. 7079 * @property {object} controller.name The usual name or function of the controller. 7080 * @property {object} controller.description A user-friendly representation of the 7081 * controller's default function 7082 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7083 * @property {number} value The value expressed as a float between 0 and 1. 7084 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7085 */ 7086 7087 /** 7088 * Event emitted when a **controlchange-expressioncoarse** MIDI message has been 7089 * received. 7090 * 7091 * @event InputChannel#controlchange-expressioncoarse 7092 * 7093 * @type {object} 7094 * @property {string} type `controlchange-expressioncoarse` 7095 * 7096 * @property {InputChannel} target The object that dispatched the event. 7097 * @property {Input} port The `Input` that triggered the event. 7098 * @property {Message} message A [`Message`](Message) object containing information about the 7099 * incoming MIDI message. 7100 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7101 * milliseconds since the navigation start of the document). 7102 * 7103 * @property {object} controller 7104 * @property {object} controller.number The number of the controller. 7105 * @property {object} controller.name The usual name or function of the controller. 7106 * @property {object} controller.description A user-friendly representation of the 7107 * controller's default function 7108 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7109 * @property {number} value The value expressed as a float between 0 and 1. 7110 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7111 */ 7112 7113 /** 7114 * Event emitted when a **controlchange-effectcontrol1coarse** MIDI message has been 7115 * received. 7116 * 7117 * @event InputChannel#controlchange-effectcontrol1coarse 7118 * 7119 * @type {object} 7120 * @property {string} type `controlchange-effectcontrol1coarse` 7121 * 7122 * @property {InputChannel} target The object that dispatched the event. 7123 * @property {Input} port The `Input` that triggered the event. 7124 * @property {Message} message A [`Message`](Message) object containing information about the 7125 * incoming MIDI message. 7126 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7127 * milliseconds since the navigation start of the document). 7128 * 7129 * @property {object} controller 7130 * @property {object} controller.number The number of the controller. 7131 * @property {object} controller.name The usual name or function of the controller. 7132 * @property {object} controller.description A user-friendly representation of the 7133 * controller's default function 7134 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7135 * @property {number} value The value expressed as a float between 0 and 1. 7136 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7137 */ 7138 7139 /** 7140 * Event emitted when a **controlchange-effectcontrol2coarse** MIDI message has been 7141 * received. 7142 * 7143 * @event InputChannel#controlchange-effectcontrol2coarse 7144 * 7145 * @type {object} 7146 * @property {string} type `controlchange-effectcontrol2coarse` 7147 * 7148 * @property {InputChannel} target The object that dispatched the event. 7149 * @property {Input} port The `Input` that triggered the event. 7150 * @property {Message} message A [`Message`](Message) object containing information about the 7151 * incoming MIDI message. 7152 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7153 * milliseconds since the navigation start of the document). 7154 * 7155 * @property {object} controller 7156 * @property {object} controller.number The number of the controller. 7157 * @property {object} controller.name The usual name or function of the controller. 7158 * @property {object} controller.description A user-friendly representation of the 7159 * controller's default function 7160 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7161 * @property {number} value The value expressed as a float between 0 and 1. 7162 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7163 */ 7164 7165 /** 7166 * Event emitted when a **controlchange-generalpurposecontroller1** MIDI message has been 7167 * received. 7168 * 7169 * @event InputChannel#controlchange-generalpurposecontroller1 7170 * 7171 * @type {object} 7172 * @property {string} type `controlchange-generalpurposecontroller1` 7173 * 7174 * @property {InputChannel} target The object that dispatched the event. 7175 * @property {Input} port The `Input` that triggered the event. 7176 * @property {Message} message A [`Message`](Message) object containing information about the 7177 * incoming MIDI message. 7178 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7179 * milliseconds since the navigation start of the document). 7180 * 7181 * @property {object} controller 7182 * @property {object} controller.number The number of the controller. 7183 * @property {object} controller.name The usual name or function of the controller. 7184 * @property {object} controller.description A user-friendly representation of the 7185 * controller's default function 7186 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7187 * @property {number} value The value expressed as a float between 0 and 1. 7188 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7189 */ 7190 7191 /** 7192 * Event emitted when a **controlchange-generalpurposecontroller2** MIDI message has been 7193 * received. 7194 * 7195 * @event InputChannel#controlchange-generalpurposecontroller2 7196 * 7197 * @type {object} 7198 * @property {string} type `controlchange-generalpurposecontroller2` 7199 * 7200 * @property {InputChannel} target The object that dispatched the event. 7201 * @property {Input} port The `Input` that triggered the event. 7202 * @property {Message} message A [`Message`](Message) object containing information about the 7203 * incoming MIDI message. 7204 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7205 * milliseconds since the navigation start of the document). 7206 * 7207 * @property {object} controller 7208 * @property {object} controller.number The number of the controller. 7209 * @property {object} controller.name The usual name or function of the controller. 7210 * @property {object} controller.description A user-friendly representation of the 7211 * controller's default function 7212 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7213 * @property {number} value The value expressed as a float between 0 and 1. 7214 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7215 */ 7216 7217 /** 7218 * Event emitted when a **controlchange-generalpurposecontroller3** MIDI message has been 7219 * received. 7220 * 7221 * @event InputChannel#controlchange-generalpurposecontroller3 7222 * 7223 * @type {object} 7224 * @property {string} type `controlchange-generalpurposecontroller3` 7225 * 7226 * @property {InputChannel} target The object that dispatched the event. 7227 * @property {Input} port The `Input` that triggered the event. 7228 * @property {Message} message A [`Message`](Message) object containing information about the 7229 * incoming MIDI message. 7230 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7231 * milliseconds since the navigation start of the document). 7232 * 7233 * @property {object} controller 7234 * @property {object} controller.number The number of the controller. 7235 * @property {object} controller.name The usual name or function of the controller. 7236 * @property {object} controller.description A user-friendly representation of the 7237 * controller's default function 7238 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7239 * @property {number} value The value expressed as a float between 0 and 1. 7240 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7241 */ 7242 7243 /** 7244 * Event emitted when a **controlchange-generalpurposecontroller4** MIDI message has been 7245 * received. 7246 * 7247 * @event InputChannel#controlchange-generalpurposecontroller4 7248 * 7249 * @type {object} 7250 * @property {string} type `controlchange-generalpurposecontroller4` 7251 * 7252 * @property {InputChannel} target The object that dispatched the event. 7253 * @property {Input} port The `Input` that triggered the event. 7254 * @property {Message} message A [`Message`](Message) object containing information about the 7255 * incoming MIDI message. 7256 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7257 * milliseconds since the navigation start of the document). 7258 * 7259 * @property {object} controller 7260 * @property {object} controller.number The number of the controller. 7261 * @property {object} controller.name The usual name or function of the controller. 7262 * @property {object} controller.description A user-friendly representation of the 7263 * controller's default function 7264 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7265 * @property {number} value The value expressed as a float between 0 and 1. 7266 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7267 */ 7268 7269 /** 7270 * Event emitted when a **controlchange-bankselectfine** MIDI message has been 7271 * received. 7272 * 7273 * @event InputChannel#controlchange-bankselectfine 7274 * 7275 * @type {object} 7276 * @property {string} type `controlchange-bankselectfine` 7277 * 7278 * @property {InputChannel} target The object that dispatched the event. 7279 * @property {Input} port The `Input` that triggered the event. 7280 * @property {Message} message A [`Message`](Message) object containing information about the 7281 * incoming MIDI message. 7282 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7283 * milliseconds since the navigation start of the document). 7284 * 7285 * @property {object} controller 7286 * @property {object} controller.number The number of the controller. 7287 * @property {object} controller.name The usual name or function of the controller. 7288 * @property {object} controller.description A user-friendly representation of the 7289 * controller's default function 7290 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7291 * @property {number} value The value expressed as a float between 0 and 1. 7292 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7293 */ 7294 7295 /** 7296 * Event emitted when a **controlchange-modulationwheelfine** MIDI message has been 7297 * received. 7298 * 7299 * @event InputChannel#controlchange-modulationwheelfine 7300 * 7301 * @type {object} 7302 * @property {string} type `controlchange-modulationwheelfine` 7303 * 7304 * @property {InputChannel} target The object that dispatched the event. 7305 * @property {Input} port The `Input` that triggered the event. 7306 * @property {Message} message A [`Message`](Message) object containing information about the 7307 * incoming MIDI message. 7308 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7309 * milliseconds since the navigation start of the document). 7310 * 7311 * @property {object} controller 7312 * @property {object} controller.number The number of the controller. 7313 * @property {object} controller.name The usual name or function of the controller. 7314 * @property {object} controller.description A user-friendly representation of the 7315 * controller's default function 7316 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7317 * @property {number} value The value expressed as a float between 0 and 1. 7318 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7319 */ 7320 7321 /** 7322 * Event emitted when a **controlchange-breathcontrollerfine** MIDI message has been 7323 * received. 7324 * 7325 * @event InputChannel#controlchange-breathcontrollerfine 7326 * 7327 * @type {object} 7328 * @property {string} type `controlchange-breathcontrollerfine` 7329 * 7330 * @property {InputChannel} target The object that dispatched the event. 7331 * @property {Input} port The `Input` that triggered the event. 7332 * @property {Message} message A [`Message`](Message) object containing information about the 7333 * incoming MIDI message. 7334 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7335 * milliseconds since the navigation start of the document). 7336 * 7337 * @property {object} controller 7338 * @property {object} controller.number The number of the controller. 7339 * @property {object} controller.name The usual name or function of the controller. 7340 * @property {object} controller.description A user-friendly representation of the 7341 * controller's default function 7342 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7343 * @property {number} value The value expressed as a float between 0 and 1. 7344 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7345 */ 7346 7347 /** 7348 * Event emitted when a **controlchange-footcontrollerfine** MIDI message has been 7349 * received. 7350 * 7351 * @event InputChannel#controlchange-footcontrollerfine 7352 * 7353 * @type {object} 7354 * @property {string} type `controlchange-footcontrollerfine` 7355 * 7356 * @property {InputChannel} target The object that dispatched the event. 7357 * @property {Input} port The `Input` that triggered the event. 7358 * @property {Message} message A [`Message`](Message) object containing information about the 7359 * incoming MIDI message. 7360 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7361 * milliseconds since the navigation start of the document). 7362 * 7363 * @property {object} controller 7364 * @property {object} controller.number The number of the controller. 7365 * @property {object} controller.name The usual name or function of the controller. 7366 * @property {object} controller.description A user-friendly representation of the 7367 * controller's default function 7368 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7369 * @property {number} value The value expressed as a float between 0 and 1. 7370 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7371 */ 7372 7373 /** 7374 * Event emitted when a **controlchange-portamentotimefine** MIDI message has been 7375 * received. 7376 * 7377 * @event InputChannel#controlchange-portamentotimefine 7378 * 7379 * @type {object} 7380 * @property {string} type `controlchange-portamentotimefine` 7381 * 7382 * @property {InputChannel} target The object that dispatched the event. 7383 * @property {Input} port The `Input` that triggered the event. 7384 * @property {Message} message A [`Message`](Message) object containing information about the 7385 * incoming MIDI message. 7386 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7387 * milliseconds since the navigation start of the document). 7388 * 7389 * @property {object} controller 7390 * @property {object} controller.number The number of the controller. 7391 * @property {object} controller.name The usual name or function of the controller. 7392 * @property {object} controller.description A user-friendly representation of the 7393 * controller's default function 7394 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7395 * @property {number} value The value expressed as a float between 0 and 1. 7396 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7397 */ 7398 7399 /** 7400 * Event emitted when a **controlchange-dataentryfine** MIDI message has been 7401 * received. 7402 * 7403 * @event InputChannel#controlchange-dataentryfine 7404 * 7405 * @type {object} 7406 * @property {string} type `controlchange-dataentryfine` 7407 * 7408 * @property {InputChannel} target The object that dispatched the event. 7409 * @property {Input} port The `Input` that triggered the event. 7410 * @property {Message} message A [`Message`](Message) object containing information about the 7411 * incoming MIDI message. 7412 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7413 * milliseconds since the navigation start of the document). 7414 * 7415 * @property {object} controller 7416 * @property {object} controller.number The number of the controller. 7417 * @property {object} controller.name The usual name or function of the controller. 7418 * @property {object} controller.description A user-friendly representation of the 7419 * controller's default function 7420 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7421 * @property {number} value The value expressed as a float between 0 and 1. 7422 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7423 */ 7424 7425 /** 7426 * Event emitted when a **controlchange-channelvolumefine** MIDI message has been 7427 * received. 7428 * 7429 * @event InputChannel#controlchange-channelvolumefine 7430 * 7431 * @type {object} 7432 * @property {string} type `controlchange-channelvolumefine` 7433 * 7434 * @property {InputChannel} target The object that dispatched the event. 7435 * @property {Input} port The `Input` that triggered the event. 7436 * @property {Message} message A [`Message`](Message) object containing information about the 7437 * incoming MIDI message. 7438 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7439 * milliseconds since the navigation start of the document). 7440 * 7441 * @property {object} controller 7442 * @property {object} controller.number The number of the controller. 7443 * @property {object} controller.name The usual name or function of the controller. 7444 * @property {object} controller.description A user-friendly representation of the 7445 * controller's default function 7446 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7447 * @property {number} value The value expressed as a float between 0 and 1. 7448 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7449 */ 7450 7451 /** 7452 * Event emitted when a **controlchange-balancefine** MIDI message has been 7453 * received. 7454 * 7455 * @event InputChannel#controlchange-balancefine 7456 * 7457 * @type {object} 7458 * @property {string} type `controlchange-balancefine` 7459 * 7460 * @property {InputChannel} target The object that dispatched the event. 7461 * @property {Input} port The `Input` that triggered the event. 7462 * @property {Message} message A [`Message`](Message) object containing information about the 7463 * incoming MIDI message. 7464 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7465 * milliseconds since the navigation start of the document). 7466 * 7467 * @property {object} controller 7468 * @property {object} controller.number The number of the controller. 7469 * @property {object} controller.name The usual name or function of the controller. 7470 * @property {object} controller.description A user-friendly representation of the 7471 * controller's default function 7472 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7473 * @property {number} value The value expressed as a float between 0 and 1. 7474 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7475 */ 7476 7477 /** 7478 * Event emitted when a **controlchange-panfine** MIDI message has been 7479 * received. 7480 * 7481 * @event InputChannel#controlchange-panfine 7482 * 7483 * @type {object} 7484 * @property {string} type `controlchange-panfine` 7485 * 7486 * @property {InputChannel} target The object that dispatched the event. 7487 * @property {Input} port The `Input` that triggered the event. 7488 * @property {Message} message A [`Message`](Message) object containing information about the 7489 * incoming MIDI message. 7490 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7491 * milliseconds since the navigation start of the document). 7492 * 7493 * @property {object} controller 7494 * @property {object} controller.number The number of the controller. 7495 * @property {object} controller.name The usual name or function of the controller. 7496 * @property {object} controller.description A user-friendly representation of the 7497 * controller's default function 7498 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7499 * @property {number} value The value expressed as a float between 0 and 1. 7500 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7501 */ 7502 7503 /** 7504 * Event emitted when a **controlchange-expressionfine** MIDI message has been 7505 * received. 7506 * 7507 * @event InputChannel#controlchange-expressionfine 7508 * 7509 * @type {object} 7510 * @property {string} type `controlchange-expressionfine` 7511 * 7512 * @property {InputChannel} target The object that dispatched the event. 7513 * @property {Input} port The `Input` that triggered the event. 7514 * @property {Message} message A [`Message`](Message) object containing information about the 7515 * incoming MIDI message. 7516 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7517 * milliseconds since the navigation start of the document). 7518 * 7519 * @property {object} controller 7520 * @property {object} controller.number The number of the controller. 7521 * @property {object} controller.name The usual name or function of the controller. 7522 * @property {object} controller.description A user-friendly representation of the 7523 * controller's default function 7524 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7525 * @property {number} value The value expressed as a float between 0 and 1. 7526 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7527 */ 7528 7529 /** 7530 * Event emitted when a **controlchange-effectcontrol1fine** MIDI message has been 7531 * received. 7532 * 7533 * @event InputChannel#controlchange-effectcontrol1fine 7534 * 7535 * @type {object} 7536 * @property {string} type `controlchange-effectcontrol1fine` 7537 * 7538 * @property {InputChannel} target The object that dispatched the event. 7539 * @property {Input} port The `Input` that triggered the event. 7540 * @property {Message} message A [`Message`](Message) object containing information about the 7541 * incoming MIDI message. 7542 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7543 * milliseconds since the navigation start of the document). 7544 * 7545 * @property {object} controller 7546 * @property {object} controller.number The number of the controller. 7547 * @property {object} controller.name The usual name or function of the controller. 7548 * @property {object} controller.description A user-friendly representation of the 7549 * controller's default function 7550 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7551 * @property {number} value The value expressed as a float between 0 and 1. 7552 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7553 */ 7554 7555 /** 7556 * Event emitted when a **controlchange-effectcontrol2fine** MIDI message has been 7557 * received. 7558 * 7559 * @event InputChannel#controlchange-effectcontrol2fine 7560 * 7561 * @type {object} 7562 * @property {string} type `controlchange-effectcontrol2fine` 7563 * 7564 * @property {InputChannel} target The object that dispatched the event. 7565 * @property {Input} port The `Input` that triggered the event. 7566 * @property {Message} message A [`Message`](Message) object containing information about the 7567 * incoming MIDI message. 7568 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7569 * milliseconds since the navigation start of the document). 7570 * 7571 * @property {object} controller 7572 * @property {object} controller.number The number of the controller. 7573 * @property {object} controller.name The usual name or function of the controller. 7574 * @property {object} controller.description A user-friendly representation of the 7575 * controller's default function 7576 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7577 * @property {number} value The value expressed as a float between 0 and 1. 7578 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7579 */ 7580 7581 /** 7582 * Event emitted when a **controlchange-damperpedal** MIDI message has been 7583 * received. 7584 * 7585 * @event InputChannel#controlchange-damperpedal 7586 * 7587 * @type {object} 7588 * @property {string} type `controlchange-damperpedal` 7589 * 7590 * @property {InputChannel} target The object that dispatched the event. 7591 * @property {Input} port The `Input` that triggered the event. 7592 * @property {Message} message A [`Message`](Message) object containing information about the 7593 * incoming MIDI message. 7594 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7595 * milliseconds since the navigation start of the document). 7596 * 7597 * @property {object} controller 7598 * @property {object} controller.number The number of the controller. 7599 * @property {object} controller.name The usual name or function of the controller. 7600 * @property {object} controller.description A user-friendly representation of the 7601 * controller's default function 7602 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7603 * @property {number} value The value expressed as a float between 0 and 1. 7604 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7605 */ 7606 7607 /** 7608 * Event emitted when a **controlchange-portamento** MIDI message has been 7609 * received. 7610 * 7611 * @event InputChannel#controlchange-portamento 7612 * 7613 * @type {object} 7614 * @property {string} type `controlchange-portamento` 7615 * 7616 * @property {InputChannel} target The object that dispatched the event. 7617 * @property {Input} port The `Input` that triggered the event. 7618 * @property {Message} message A [`Message`](Message) object containing information about the 7619 * incoming MIDI message. 7620 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7621 * milliseconds since the navigation start of the document). 7622 * 7623 * @property {object} controller 7624 * @property {object} controller.number The number of the controller. 7625 * @property {object} controller.name The usual name or function of the controller. 7626 * @property {object} controller.description A user-friendly representation of the 7627 * controller's default function 7628 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7629 * @property {number} value The value expressed as a float between 0 and 1. 7630 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7631 */ 7632 7633 /** 7634 * Event emitted when a **controlchange-sostenuto** MIDI message has been 7635 * received. 7636 * 7637 * @event InputChannel#controlchange-sostenuto 7638 * 7639 * @type {object} 7640 * @property {string} type `controlchange-sostenuto` 7641 * 7642 * @property {InputChannel} target The object that dispatched the event. 7643 * @property {Input} port The `Input` that triggered the event. 7644 * @property {Message} message A [`Message`](Message) object containing information about the 7645 * incoming MIDI message. 7646 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7647 * milliseconds since the navigation start of the document). 7648 * 7649 * @property {object} controller 7650 * @property {object} controller.number The number of the controller. 7651 * @property {object} controller.name The usual name or function of the controller. 7652 * @property {object} controller.description A user-friendly representation of the 7653 * controller's default function 7654 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7655 * @property {number} value The value expressed as a float between 0 and 1. 7656 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7657 */ 7658 7659 /** 7660 * Event emitted when a **controlchange-softpedal** MIDI message has been 7661 * received. 7662 * 7663 * @event InputChannel#controlchange-softpedal 7664 * 7665 * @type {object} 7666 * @property {string} type `controlchange-softpedal` 7667 * 7668 * @property {InputChannel} target The object that dispatched the event. 7669 * @property {Input} port The `Input` that triggered the event. 7670 * @property {Message} message A [`Message`](Message) object containing information about the 7671 * incoming MIDI message. 7672 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7673 * milliseconds since the navigation start of the document). 7674 * 7675 * @property {object} controller 7676 * @property {object} controller.number The number of the controller. 7677 * @property {object} controller.name The usual name or function of the controller. 7678 * @property {object} controller.description A user-friendly representation of the 7679 * controller's default function 7680 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7681 * @property {number} value The value expressed as a float between 0 and 1. 7682 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7683 */ 7684 7685 /** 7686 * Event emitted when a **controlchange-legatopedal** MIDI message has been 7687 * received. 7688 * 7689 * @event InputChannel#controlchange-legatopedal 7690 * 7691 * @type {object} 7692 * @property {string} type `controlchange-legatopedal` 7693 * 7694 * @property {InputChannel} target The object that dispatched the event. 7695 * @property {Input} port The `Input` that triggered the event. 7696 * @property {Message} message A [`Message`](Message) object containing information about the 7697 * incoming MIDI message. 7698 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7699 * milliseconds since the navigation start of the document). 7700 * 7701 * @property {object} controller 7702 * @property {object} controller.number The number of the controller. 7703 * @property {object} controller.name The usual name or function of the controller. 7704 * @property {object} controller.description A user-friendly representation of the 7705 * controller's default function 7706 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7707 * @property {number} value The value expressed as a float between 0 and 1. 7708 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7709 */ 7710 7711 /** 7712 * Event emitted when a **controlchange-hold2** MIDI message has been 7713 * received. 7714 * 7715 * @event InputChannel#controlchange-hold2 7716 * 7717 * @type {object} 7718 * @property {string} type `controlchange-hold2` 7719 * 7720 * @property {InputChannel} target The object that dispatched the event. 7721 * @property {Input} port The `Input` that triggered the event. 7722 * @property {Message} message A [`Message`](Message) object containing information about the 7723 * incoming MIDI message. 7724 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7725 * milliseconds since the navigation start of the document). 7726 * 7727 * @property {object} controller 7728 * @property {object} controller.number The number of the controller. 7729 * @property {object} controller.name The usual name or function of the controller. 7730 * @property {object} controller.description A user-friendly representation of the 7731 * controller's default function 7732 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7733 * @property {number} value The value expressed as a float between 0 and 1. 7734 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7735 */ 7736 7737 /** 7738 * Event emitted when a **controlchange-soundvariation** MIDI message has been 7739 * received. 7740 * 7741 * @event InputChannel#controlchange-soundvariation 7742 * 7743 * @type {object} 7744 * @property {string} type `controlchange-soundvariation` 7745 * 7746 * @property {InputChannel} target The object that dispatched the event. 7747 * @property {Input} port The `Input` that triggered the event. 7748 * @property {Message} message A [`Message`](Message) object containing information about the 7749 * incoming MIDI message. 7750 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7751 * milliseconds since the navigation start of the document). 7752 * 7753 * @property {object} controller 7754 * @property {object} controller.number The number of the controller. 7755 * @property {object} controller.name The usual name or function of the controller. 7756 * @property {object} controller.description A user-friendly representation of the 7757 * controller's default function 7758 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7759 * @property {number} value The value expressed as a float between 0 and 1. 7760 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7761 */ 7762 7763 /** 7764 * Event emitted when a **controlchange-resonance** MIDI message has been 7765 * received. 7766 * 7767 * @event InputChannel#controlchange-resonance 7768 * 7769 * @type {object} 7770 * @property {string} type `controlchange-resonance` 7771 * 7772 * @property {InputChannel} target The object that dispatched the event. 7773 * @property {Input} port The `Input` that triggered the event. 7774 * @property {Message} message A [`Message`](Message) object containing information about the 7775 * incoming MIDI message. 7776 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7777 * milliseconds since the navigation start of the document). 7778 * 7779 * @property {object} controller 7780 * @property {object} controller.number The number of the controller. 7781 * @property {object} controller.name The usual name or function of the controller. 7782 * @property {object} controller.description A user-friendly representation of the 7783 * controller's default function 7784 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7785 * @property {number} value The value expressed as a float between 0 and 1. 7786 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7787 */ 7788 7789 /** 7790 * Event emitted when a **controlchange-releasetime** MIDI message has been 7791 * received. 7792 * 7793 * @event InputChannel#controlchange-releasetime 7794 * 7795 * @type {object} 7796 * @property {string} type `controlchange-releasetime` 7797 * 7798 * @property {InputChannel} target The object that dispatched the event. 7799 * @property {Input} port The `Input` that triggered the event. 7800 * @property {Message} message A [`Message`](Message) object containing information about the 7801 * incoming MIDI message. 7802 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7803 * milliseconds since the navigation start of the document). 7804 * 7805 * @property {object} controller 7806 * @property {object} controller.number The number of the controller. 7807 * @property {object} controller.name The usual name or function of the controller. 7808 * @property {object} controller.description A user-friendly representation of the 7809 * controller's default function 7810 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7811 * @property {number} value The value expressed as a float between 0 and 1. 7812 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7813 */ 7814 7815 /** 7816 * Event emitted when a **controlchange-attacktime** MIDI message has been 7817 * received. 7818 * 7819 * @event InputChannel#controlchange-attacktime 7820 * 7821 * @type {object} 7822 * @property {string} type `controlchange-attacktime` 7823 * 7824 * @property {InputChannel} target The object that dispatched the event. 7825 * @property {Input} port The `Input` that triggered the event. 7826 * @property {Message} message A [`Message`](Message) object containing information about the 7827 * incoming MIDI message. 7828 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7829 * milliseconds since the navigation start of the document). 7830 * 7831 * @property {object} controller 7832 * @property {object} controller.number The number of the controller. 7833 * @property {object} controller.name The usual name or function of the controller. 7834 * @property {object} controller.description A user-friendly representation of the 7835 * controller's default function 7836 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7837 * @property {number} value The value expressed as a float between 0 and 1. 7838 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7839 */ 7840 7841 /** 7842 * Event emitted when a **controlchange-brightness** MIDI message has been 7843 * received. 7844 * 7845 * @event InputChannel#controlchange-brightness 7846 * 7847 * @type {object} 7848 * @property {string} type `controlchange-brightness` 7849 * 7850 * @property {InputChannel} target The object that dispatched the event. 7851 * @property {Input} port The `Input` that triggered the event. 7852 * @property {Message} message A [`Message`](Message) object containing information about the 7853 * incoming MIDI message. 7854 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7855 * milliseconds since the navigation start of the document). 7856 * 7857 * @property {object} controller 7858 * @property {object} controller.number The number of the controller. 7859 * @property {object} controller.name The usual name or function of the controller. 7860 * @property {object} controller.description A user-friendly representation of the 7861 * controller's default function 7862 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7863 * @property {number} value The value expressed as a float between 0 and 1. 7864 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7865 */ 7866 7867 /** 7868 * Event emitted when a **controlchange-decaytime** MIDI message has been 7869 * received. 7870 * 7871 * @event InputChannel#controlchange-decaytime 7872 * 7873 * @type {object} 7874 * @property {string} type `controlchange-decaytime` 7875 * 7876 * @property {InputChannel} target The object that dispatched the event. 7877 * @property {Input} port The `Input` that triggered the event. 7878 * @property {Message} message A [`Message`](Message) object containing information about the 7879 * incoming MIDI message. 7880 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7881 * milliseconds since the navigation start of the document). 7882 * 7883 * @property {object} controller 7884 * @property {object} controller.number The number of the controller. 7885 * @property {object} controller.name The usual name or function of the controller. 7886 * @property {object} controller.description A user-friendly representation of the 7887 * controller's default function 7888 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7889 * @property {number} value The value expressed as a float between 0 and 1. 7890 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7891 */ 7892 7893 /** 7894 * Event emitted when a **controlchange-vibratorate** MIDI message has been 7895 * received. 7896 * 7897 * @event InputChannel#controlchange-vibratorate 7898 * 7899 * @type {object} 7900 * @property {string} type `controlchange-vibratorate` 7901 * 7902 * @property {InputChannel} target The object that dispatched the event. 7903 * @property {Input} port The `Input` that triggered the event. 7904 * @property {Message} message A [`Message`](Message) object containing information about the 7905 * incoming MIDI message. 7906 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7907 * milliseconds since the navigation start of the document). 7908 * 7909 * @property {object} controller 7910 * @property {object} controller.number The number of the controller. 7911 * @property {object} controller.name The usual name or function of the controller. 7912 * @property {object} controller.description A user-friendly representation of the 7913 * controller's default function 7914 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7915 * @property {number} value The value expressed as a float between 0 and 1. 7916 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7917 */ 7918 7919 /** 7920 * Event emitted when a **controlchange-vibratodepth** MIDI message has been 7921 * received. 7922 * 7923 * @event InputChannel#controlchange-vibratodepth 7924 * 7925 * @type {object} 7926 * @property {string} type `controlchange-vibratodepth` 7927 * 7928 * @property {InputChannel} target The object that dispatched the event. 7929 * @property {Input} port The `Input` that triggered the event. 7930 * @property {Message} message A [`Message`](Message) object containing information about the 7931 * incoming MIDI message. 7932 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7933 * milliseconds since the navigation start of the document). 7934 * 7935 * @property {object} controller 7936 * @property {object} controller.number The number of the controller. 7937 * @property {object} controller.name The usual name or function of the controller. 7938 * @property {object} controller.description A user-friendly representation of the 7939 * controller's default function 7940 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7941 * @property {number} value The value expressed as a float between 0 and 1. 7942 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7943 */ 7944 7945 /** 7946 * Event emitted when a **controlchange-vibratodelay** MIDI message has been 7947 * received. 7948 * 7949 * @event InputChannel#controlchange-vibratodelay 7950 * 7951 * @type {object} 7952 * @property {string} type `controlchange-vibratodelay` 7953 * 7954 * @property {InputChannel} target The object that dispatched the event. 7955 * @property {Input} port The `Input` that triggered the event. 7956 * @property {Message} message A [`Message`](Message) object containing information about the 7957 * incoming MIDI message. 7958 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7959 * milliseconds since the navigation start of the document). 7960 * 7961 * @property {object} controller 7962 * @property {object} controller.number The number of the controller. 7963 * @property {object} controller.name The usual name or function of the controller. 7964 * @property {object} controller.description A user-friendly representation of the 7965 * controller's default function 7966 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7967 * @property {number} value The value expressed as a float between 0 and 1. 7968 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7969 */ 7970 7971 /** 7972 * Event emitted when a **controlchange-generalpurposecontroller5** MIDI message has been 7973 * received. 7974 * 7975 * @event InputChannel#controlchange-generalpurposecontroller5 7976 * 7977 * @type {object} 7978 * @property {string} type `controlchange-generalpurposecontroller5` 7979 * 7980 * @property {InputChannel} target The object that dispatched the event. 7981 * @property {Input} port The `Input` that triggered the event. 7982 * @property {Message} message A [`Message`](Message) object containing information about the 7983 * incoming MIDI message. 7984 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 7985 * milliseconds since the navigation start of the document). 7986 * 7987 * @property {object} controller 7988 * @property {object} controller.number The number of the controller. 7989 * @property {object} controller.name The usual name or function of the controller. 7990 * @property {object} controller.description A user-friendly representation of the 7991 * controller's default function 7992 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 7993 * @property {number} value The value expressed as a float between 0 and 1. 7994 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 7995 */ 7996 7997 /** 7998 * Event emitted when a **controlchange-generalpurposecontroller6** MIDI message has been 7999 * received. 8000 * 8001 * @event InputChannel#controlchange-generalpurposecontroller6 8002 * 8003 * @type {object} 8004 * @property {string} type `controlchange-generalpurposecontroller6` 8005 * 8006 * @property {InputChannel} target The object that dispatched the event. 8007 * @property {Input} port The `Input` that triggered the event. 8008 * @property {Message} message A [`Message`](Message) object containing information about the 8009 * incoming MIDI message. 8010 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8011 * milliseconds since the navigation start of the document). 8012 * 8013 * @property {object} controller 8014 * @property {object} controller.number The number of the controller. 8015 * @property {object} controller.name The usual name or function of the controller. 8016 * @property {object} controller.description A user-friendly representation of the 8017 * controller's default function 8018 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8019 * @property {number} value The value expressed as a float between 0 and 1. 8020 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8021 */ 8022 8023 /** 8024 * Event emitted when a **controlchange-generalpurposecontroller7** MIDI message has been 8025 * received. 8026 * 8027 * @event InputChannel#controlchange-generalpurposecontroller7 8028 * 8029 * @type {object} 8030 * @property {string} type `controlchange-generalpurposecontroller7` 8031 * 8032 * @property {InputChannel} target The object that dispatched the event. 8033 * @property {Input} port The `Input` that triggered the event. 8034 * @property {Message} message A [`Message`](Message) object containing information about the 8035 * incoming MIDI message. 8036 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8037 * milliseconds since the navigation start of the document). 8038 * 8039 * @property {object} controller 8040 * @property {object} controller.number The number of the controller. 8041 * @property {object} controller.name The usual name or function of the controller. 8042 * @property {object} controller.description A user-friendly representation of the 8043 * controller's default function 8044 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8045 * @property {number} value The value expressed as a float between 0 and 1. 8046 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8047 */ 8048 8049 /** 8050 * Event emitted when a **controlchange-generalpurposecontroller8** MIDI message has been 8051 * received. 8052 * 8053 * @event InputChannel#controlchange-generalpurposecontroller8 8054 * 8055 * @type {object} 8056 * @property {string} type `controlchange-generalpurposecontroller8` 8057 * 8058 * @property {InputChannel} target The object that dispatched the event. 8059 * @property {Input} port The `Input` that triggered the event. 8060 * @property {Message} message A [`Message`](Message) object containing information about the 8061 * incoming MIDI message. 8062 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8063 * milliseconds since the navigation start of the document). 8064 * 8065 * @property {object} controller 8066 * @property {object} controller.number The number of the controller. 8067 * @property {object} controller.name The usual name or function of the controller. 8068 * @property {object} controller.description A user-friendly representation of the 8069 * controller's default function 8070 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8071 * @property {number} value The value expressed as a float between 0 and 1. 8072 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8073 */ 8074 8075 /** 8076 * Event emitted when a **controlchange-portamentocontrol** MIDI message has been 8077 * received. 8078 * 8079 * @event InputChannel#controlchange-portamentocontrol 8080 * 8081 * @type {object} 8082 * @property {string} type `controlchange-portamentocontrol` 8083 * 8084 * @property {InputChannel} target The object that dispatched the event. 8085 * @property {Input} port The `Input` that triggered the event. 8086 * @property {Message} message A [`Message`](Message) object containing information about the 8087 * incoming MIDI message. 8088 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8089 * milliseconds since the navigation start of the document). 8090 * 8091 * @property {object} controller 8092 * @property {object} controller.number The number of the controller. 8093 * @property {object} controller.name The usual name or function of the controller. 8094 * @property {object} controller.description A user-friendly representation of the 8095 * controller's default function 8096 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8097 * @property {number} value The value expressed as a float between 0 and 1. 8098 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8099 */ 8100 8101 /** 8102 * Event emitted when a **controlchange-highresolutionvelocityprefix** MIDI message has been 8103 * received. 8104 * 8105 * @event InputChannel#controlchange-highresolutionvelocityprefix 8106 * 8107 * @type {object} 8108 * @property {string} type `controlchange-highresolutionvelocityprefix` 8109 * 8110 * @property {InputChannel} target The object that dispatched the event. 8111 * @property {Input} port The `Input` that triggered the event. 8112 * @property {Message} message A [`Message`](Message) object containing information about the 8113 * incoming MIDI message. 8114 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8115 * milliseconds since the navigation start of the document). 8116 * 8117 * @property {object} controller 8118 * @property {object} controller.number The number of the controller. 8119 * @property {object} controller.name The usual name or function of the controller. 8120 * @property {object} controller.description A user-friendly representation of the 8121 * controller's default function 8122 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8123 * @property {number} value The value expressed as a float between 0 and 1. 8124 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8125 */ 8126 8127 /** 8128 * Event emitted when a **controlchange-effect1depth** MIDI message has been 8129 * received. 8130 * 8131 * @event InputChannel#controlchange-effect1depth 8132 * 8133 * @type {object} 8134 * @property {string} type `controlchange-effect1depth` 8135 * 8136 * @property {InputChannel} target The object that dispatched the event. 8137 * @property {Input} port The `Input` that triggered the event. 8138 * @property {Message} message A [`Message`](Message) object containing information about the 8139 * incoming MIDI message. 8140 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8141 * milliseconds since the navigation start of the document). 8142 * 8143 * @property {object} controller 8144 * @property {object} controller.number The number of the controller. 8145 * @property {object} controller.name The usual name or function of the controller. 8146 * @property {object} controller.description A user-friendly representation of the 8147 * controller's default function 8148 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8149 * @property {number} value The value expressed as a float between 0 and 1. 8150 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8151 */ 8152 8153 /** 8154 * Event emitted when a **controlchange-effect2depth** MIDI message has been 8155 * received. 8156 * 8157 * @event InputChannel#controlchange-effect2depth 8158 * 8159 * @type {object} 8160 * @property {string} type `controlchange-effect2depth` 8161 * 8162 * @property {InputChannel} target The object that dispatched the event. 8163 * @property {Input} port The `Input` that triggered the event. 8164 * @property {Message} message A [`Message`](Message) object containing information about the 8165 * incoming MIDI message. 8166 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8167 * milliseconds since the navigation start of the document). 8168 * 8169 * @property {object} controller 8170 * @property {object} controller.number The number of the controller. 8171 * @property {object} controller.name The usual name or function of the controller. 8172 * @property {object} controller.description A user-friendly representation of the 8173 * controller's default function 8174 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8175 * @property {number} value The value expressed as a float between 0 and 1. 8176 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8177 */ 8178 8179 /** 8180 * Event emitted when a **controlchange-effect3depth** MIDI message has been 8181 * received. 8182 * 8183 * @event InputChannel#controlchange-effect3depth 8184 * 8185 * @type {object} 8186 * @property {string} type `controlchange-effect3depth` 8187 * 8188 * @property {InputChannel} target The object that dispatched the event. 8189 * @property {Input} port The `Input` that triggered the event. 8190 * @property {Message} message A [`Message`](Message) object containing information about the 8191 * incoming MIDI message. 8192 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8193 * milliseconds since the navigation start of the document). 8194 * 8195 * @property {object} controller 8196 * @property {object} controller.number The number of the controller. 8197 * @property {object} controller.name The usual name or function of the controller. 8198 * @property {object} controller.description A user-friendly representation of the 8199 * controller's default function 8200 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8201 * @property {number} value The value expressed as a float between 0 and 1. 8202 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8203 */ 8204 8205 /** 8206 * Event emitted when a **controlchange-effect4depth** MIDI message has been 8207 * received. 8208 * 8209 * @event InputChannel#controlchange-effect4depth 8210 * 8211 * @type {object} 8212 * @property {string} type `controlchange-effect4depth` 8213 * 8214 * @property {InputChannel} target The object that dispatched the event. 8215 * @property {Input} port The `Input` that triggered the event. 8216 * @property {Message} message A [`Message`](Message) object containing information about the 8217 * incoming MIDI message. 8218 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8219 * milliseconds since the navigation start of the document). 8220 * 8221 * @property {object} controller 8222 * @property {object} controller.number The number of the controller. 8223 * @property {object} controller.name The usual name or function of the controller. 8224 * @property {object} controller.description A user-friendly representation of the 8225 * controller's default function 8226 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8227 * @property {number} value The value expressed as a float between 0 and 1. 8228 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8229 */ 8230 8231 /** 8232 * Event emitted when a **controlchange-effect5depth** MIDI message has been 8233 * received. 8234 * 8235 * @event InputChannel#controlchange-effect5depth 8236 * 8237 * @type {object} 8238 * @property {string} type `controlchange-effect5depth` 8239 * 8240 * @property {InputChannel} target The object that dispatched the event. 8241 * @property {Input} port The `Input` that triggered the event. 8242 * @property {Message} message A [`Message`](Message) object containing information about the 8243 * incoming MIDI message. 8244 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8245 * milliseconds since the navigation start of the document). 8246 * 8247 * @property {object} controller 8248 * @property {object} controller.number The number of the controller. 8249 * @property {object} controller.name The usual name or function of the controller. 8250 * @property {object} controller.description A user-friendly representation of the 8251 * controller's default function 8252 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8253 * @property {number} value The value expressed as a float between 0 and 1. 8254 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8255 */ 8256 8257 /** 8258 * Event emitted when a **controlchange-dataincrement** MIDI message has been 8259 * received. 8260 * 8261 * @event InputChannel#controlchange-dataincrement 8262 * 8263 * @type {object} 8264 * @property {string} type `controlchange-dataincrement` 8265 * 8266 * @property {InputChannel} target The object that dispatched the event. 8267 * @property {Input} port The `Input` that triggered the event. 8268 * @property {Message} message A [`Message`](Message) object containing information about the 8269 * incoming MIDI message. 8270 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8271 * milliseconds since the navigation start of the document). 8272 * 8273 * @property {object} controller 8274 * @property {object} controller.number The number of the controller. 8275 * @property {object} controller.name The usual name or function of the controller. 8276 * @property {object} controller.description A user-friendly representation of the 8277 * controller's default function 8278 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8279 * @property {number} value The value expressed as a float between 0 and 1. 8280 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8281 */ 8282 8283 /** 8284 * Event emitted when a **controlchange-datadecrement** MIDI message has been 8285 * received. 8286 * 8287 * @event InputChannel#controlchange-datadecrement 8288 * 8289 * @type {object} 8290 * @property {string} type `controlchange-datadecrement` 8291 * 8292 * @property {InputChannel} target The object that dispatched the event. 8293 * @property {Input} port The `Input` that triggered the event. 8294 * @property {Message} message A [`Message`](Message) object containing information about the 8295 * incoming MIDI message. 8296 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8297 * milliseconds since the navigation start of the document). 8298 * 8299 * @property {object} controller 8300 * @property {object} controller.number The number of the controller. 8301 * @property {object} controller.name The usual name or function of the controller. 8302 * @property {object} controller.description A user-friendly representation of the 8303 * controller's default function 8304 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8305 * @property {number} value The value expressed as a float between 0 and 1. 8306 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8307 */ 8308 8309 /** 8310 * Event emitted when a **controlchange-nonregisteredparameterfine** MIDI message has been 8311 * received. 8312 * 8313 * @event InputChannel#controlchange-nonregisteredparameterfine 8314 * 8315 * @type {object} 8316 * @property {string} type `controlchange-nonregisteredparameterfine` 8317 * 8318 * @property {InputChannel} target The object that dispatched the event. 8319 * @property {Input} port The `Input` that triggered the event. 8320 * @property {Message} message A [`Message`](Message) object containing information about the 8321 * incoming MIDI message. 8322 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8323 * milliseconds since the navigation start of the document). 8324 * 8325 * @property {object} controller 8326 * @property {object} controller.number The number of the controller. 8327 * @property {object} controller.name The usual name or function of the controller. 8328 * @property {object} controller.description A user-friendly representation of the 8329 * controller's default function 8330 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8331 * @property {number} value The value expressed as a float between 0 and 1. 8332 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8333 */ 8334 8335 /** 8336 * Event emitted when a **controlchange-nonregisteredparametercoarse** MIDI message has been 8337 * received. 8338 * 8339 * @event InputChannel#controlchange-nonregisteredparametercoarse 8340 * 8341 * @type {object} 8342 * @property {string} type `controlchange-nonregisteredparametercoarse` 8343 * 8344 * @property {InputChannel} target The object that dispatched the event. 8345 * @property {Input} port The `Input` that triggered the event. 8346 * @property {Message} message A [`Message`](Message) object containing information about the 8347 * incoming MIDI message. 8348 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8349 * milliseconds since the navigation start of the document). 8350 * 8351 * @property {object} controller 8352 * @property {object} controller.number The number of the controller. 8353 * @property {object} controller.name The usual name or function of the controller. 8354 * @property {object} controller.description A user-friendly representation of the 8355 * controller's default function 8356 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8357 * @property {number} value The value expressed as a float between 0 and 1. 8358 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8359 */ 8360 8361 /** 8362 * Event emitted when a **controlchange-registeredparameterfine** MIDI message has been 8363 * received. 8364 * 8365 * @event InputChannel#controlchange-registeredparameterfine 8366 * 8367 * @type {object} 8368 * @property {string} type `controlchange-registeredparameterfine` 8369 * 8370 * @property {InputChannel} target The object that dispatched the event. 8371 * @property {Input} port The `Input` that triggered the event. 8372 * @property {Message} message A [`Message`](Message) object containing information about the 8373 * incoming MIDI message. 8374 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8375 * milliseconds since the navigation start of the document). 8376 * 8377 * @property {object} controller 8378 * @property {object} controller.number The number of the controller. 8379 * @property {object} controller.name The usual name or function of the controller. 8380 * @property {object} controller.description A user-friendly representation of the 8381 * controller's default function 8382 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8383 * @property {number} value The value expressed as a float between 0 and 1. 8384 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8385 */ 8386 8387 /** 8388 * Event emitted when a **controlchange-registeredparametercoarse** MIDI message has been 8389 * received. 8390 * 8391 * @event InputChannel#controlchange-registeredparametercoarse 8392 * 8393 * @type {object} 8394 * @property {string} type `controlchange-registeredparametercoarse` 8395 * 8396 * @property {InputChannel} target The object that dispatched the event. 8397 * @property {Input} port The `Input` that triggered the event. 8398 * @property {Message} message A [`Message`](Message) object containing information about the 8399 * incoming MIDI message. 8400 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8401 * milliseconds since the navigation start of the document). 8402 * 8403 * @property {object} controller 8404 * @property {object} controller.number The number of the controller. 8405 * @property {object} controller.name The usual name or function of the controller. 8406 * @property {object} controller.description A user-friendly representation of the 8407 * controller's default function 8408 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8409 * @property {number} value The value expressed as a float between 0 and 1. 8410 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8411 */ 8412 8413 /** 8414 * Event emitted when a **controlchange-allsoundoff** MIDI message has been 8415 * received. 8416 * 8417 * @event InputChannel#controlchange-allsoundoff 8418 * 8419 * @type {object} 8420 * @property {string} type `controlchange-allsoundoff` 8421 * 8422 * @property {InputChannel} target The object that dispatched the event. 8423 * @property {Input} port The `Input` that triggered the event. 8424 * @property {Message} message A [`Message`](Message) object containing information about the 8425 * incoming MIDI message. 8426 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8427 * milliseconds since the navigation start of the document). 8428 * 8429 * @property {object} controller 8430 * @property {object} controller.number The number of the controller. 8431 * @property {object} controller.name The usual name or function of the controller. 8432 * @property {object} controller.description A user-friendly representation of the 8433 * controller's default function 8434 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8435 * @property {number} value The value expressed as a float between 0 and 1. 8436 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8437 */ 8438 8439 /** 8440 * Event emitted when a **controlchange-resetallcontrollers** MIDI message has been 8441 * received. 8442 * 8443 * @event InputChannel#controlchange-resetallcontrollers 8444 * 8445 * @type {object} 8446 * @property {string} type `controlchange-resetallcontrollers` 8447 * 8448 * @property {InputChannel} target The object that dispatched the event. 8449 * @property {Input} port The `Input` that triggered the event. 8450 * @property {Message} message A [`Message`](Message) object containing information about the 8451 * incoming MIDI message. 8452 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8453 * milliseconds since the navigation start of the document). 8454 * 8455 * @property {object} controller 8456 * @property {object} controller.number The number of the controller. 8457 * @property {object} controller.name The usual name or function of the controller. 8458 * @property {object} controller.description A user-friendly representation of the 8459 * controller's default function 8460 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8461 * @property {number} value The value expressed as a float between 0 and 1. 8462 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8463 */ 8464 8465 /** 8466 * Event emitted when a **controlchange-localcontrol** MIDI message has been 8467 * received. 8468 * 8469 * @event InputChannel#controlchange-localcontrol 8470 * 8471 * @type {object} 8472 * @property {string} type `controlchange-localcontrol` 8473 * 8474 * @property {InputChannel} target The object that dispatched the event. 8475 * @property {Input} port The `Input` that triggered the event. 8476 * @property {Message} message A [`Message`](Message) object containing information about the 8477 * incoming MIDI message. 8478 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8479 * milliseconds since the navigation start of the document). 8480 * 8481 * @property {object} controller 8482 * @property {object} controller.number The number of the controller. 8483 * @property {object} controller.name The usual name or function of the controller. 8484 * @property {object} controller.description A user-friendly representation of the 8485 * controller's default function 8486 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8487 * @property {number} value The value expressed as a float between 0 and 1. 8488 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8489 */ 8490 8491 /** 8492 * Event emitted when a **controlchange-allnotesoff** MIDI message has been 8493 * received. 8494 * 8495 * @event InputChannel#controlchange-allnotesoff 8496 * 8497 * @type {object} 8498 * @property {string} type `controlchange-allnotesoff` 8499 * 8500 * @property {InputChannel} target The object that dispatched the event. 8501 * @property {Input} port The `Input` that triggered the event. 8502 * @property {Message} message A [`Message`](Message) object containing information about the 8503 * incoming MIDI message. 8504 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8505 * milliseconds since the navigation start of the document). 8506 * 8507 * @property {object} controller 8508 * @property {object} controller.number The number of the controller. 8509 * @property {object} controller.name The usual name or function of the controller. 8510 * @property {object} controller.description A user-friendly representation of the 8511 * controller's default function 8512 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8513 * @property {number} value The value expressed as a float between 0 and 1. 8514 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8515 */ 8516 8517 /** 8518 * Event emitted when a **controlchange-omnimodeoff** MIDI message has been 8519 * received. 8520 * 8521 * @event InputChannel#controlchange-omnimodeoff 8522 * 8523 * @type {object} 8524 * @property {string} type `controlchange-omnimodeoff` 8525 * 8526 * @property {InputChannel} target The object that dispatched the event. 8527 * @property {Input} port The `Input` that triggered the event. 8528 * @property {Message} message A [`Message`](Message) object containing information about the 8529 * incoming MIDI message. 8530 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8531 * milliseconds since the navigation start of the document). 8532 * 8533 * @property {object} controller 8534 * @property {object} controller.number The number of the controller. 8535 * @property {object} controller.name The usual name or function of the controller. 8536 * @property {object} controller.description A user-friendly representation of the 8537 * controller's default function 8538 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8539 * @property {number} value The value expressed as a float between 0 and 1. 8540 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8541 */ 8542 8543 /** 8544 * Event emitted when a **controlchange-omnimodeon** MIDI message has been 8545 * received. 8546 * 8547 * @event InputChannel#controlchange-omnimodeon 8548 * 8549 * @type {object} 8550 * @property {string} type `controlchange-omnimodeon` 8551 * 8552 * @property {InputChannel} target The object that dispatched the event. 8553 * @property {Input} port The `Input` that triggered the event. 8554 * @property {Message} message A [`Message`](Message) object containing information about the 8555 * incoming MIDI message. 8556 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8557 * milliseconds since the navigation start of the document). 8558 * 8559 * @property {object} controller 8560 * @property {object} controller.number The number of the controller. 8561 * @property {object} controller.name The usual name or function of the controller. 8562 * @property {object} controller.description A user-friendly representation of the 8563 * controller's default function 8564 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8565 * @property {number} value The value expressed as a float between 0 and 1. 8566 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8567 */ 8568 8569 /** 8570 * Event emitted when a **controlchange-monomodeon** MIDI message has been 8571 * received. 8572 * 8573 * @event InputChannel#controlchange-monomodeon 8574 * 8575 * @type {object} 8576 * @property {string} type `controlchange-monomodeon` 8577 * 8578 * @property {InputChannel} target The object that dispatched the event. 8579 * @property {Input} port The `Input` that triggered the event. 8580 * @property {Message} message A [`Message`](Message) object containing information about the 8581 * incoming MIDI message. 8582 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8583 * milliseconds since the navigation start of the document). 8584 * 8585 * @property {object} controller 8586 * @property {object} controller.number The number of the controller. 8587 * @property {object} controller.name The usual name or function of the controller. 8588 * @property {object} controller.description A user-friendly representation of the 8589 * controller's default function 8590 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8591 * @property {number} value The value expressed as a float between 0 and 1. 8592 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8593 */ 8594 8595 /** 8596 * Event emitted when a **controlchange-polymodeon** MIDI message has been 8597 * received. 8598 * 8599 * @event InputChannel#controlchange-polymodeon 8600 * 8601 * @type {object} 8602 * @property {string} type `controlchange-polymodeon` 8603 * 8604 * @property {InputChannel} target The object that dispatched the event. 8605 * @property {Input} port The `Input` that triggered the event. 8606 * @property {Message} message A [`Message`](Message) object containing information about the 8607 * incoming MIDI message. 8608 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8609 * milliseconds since the navigation start of the document). 8610 * 8611 * @property {object} controller 8612 * @property {object} controller.number The number of the controller. 8613 * @property {object} controller.name The usual name or function of the controller. 8614 * @property {object} controller.description A user-friendly representation of the 8615 * controller's default function 8616 * @property {string} controller.position Whether the controller is meant to be an `msb` or `lsb` 8617 * @property {number} value The value expressed as a float between 0 and 1. 8618 * @property {number} rawValue The value expressed as an integer (between 0 and 127). 8619 */ 8620 8621 const namedEvent = Object.assign({}, event); 8622 namedEvent.type = `${event.type}-` + Enumerations.CONTROL_CHANGE_MESSAGES[data1].name; 8623 delete namedEvent.subtype; 8624 8625 // Dispatch controlchange-"function" events only if the "function" is defined (not the generic 8626 // controllerXXX nomenclature) 8627 if (namedEvent.type.indexOf("controller") !== 0) { 8628 this.emit(namedEvent.type, namedEvent); 8629 } 8630 8631 // Trigger channel mode message events (if appropriate) 8632 if (event.message.dataBytes[0] >= 120) this._parseChannelModeMessage(event); 8633 8634 // Parse the inbound event to see if its part of an RPN/NRPN sequence 8635 if (this.parameterNumberEventsEnabled && this._isRpnOrNrpnController(event.message.dataBytes[0])) { 8636 this._parseEventForParameterNumber(event); 8637 } 8638 } else if (event.type === "programchange") { 8639 /** 8640 * Event emitted when a **program change** MIDI message has been received. 8641 * 8642 * @event InputChannel#programchange 8643 * 8644 * @type {object} 8645 * @property {string} type `programchange` 8646 * 8647 * @property {InputChannel} target The object that dispatched the event. 8648 * @property {Input} port The `Input` that triggered the event. 8649 * @property {Message} message A [`Message`](Message) object containing information about the 8650 * incoming MIDI message. 8651 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8652 * milliseconds since the navigation start of the document). 8653 * 8654 * @property {number} value The value expressed as an integer between 0 and 127. 8655 * @property {number} rawValue The raw MIDI value expressed as an integer between 0 and 127. 8656 */ 8657 event.value = data1; 8658 event.rawValue = event.value; 8659 } else if (event.type === "channelaftertouch") { 8660 /** 8661 * Event emitted when a control change MIDI message has been received. 8662 * 8663 * @event InputChannel#channelaftertouch 8664 * 8665 * @type {object} 8666 * @property {string} type `channelaftertouch` 8667 * 8668 * @property {InputChannel} target The object that dispatched the event. 8669 * @property {Input} port The `Input` that triggered the event. 8670 * @property {Message} message A [`Message`](Message) object containing information about the 8671 * incoming MIDI message. 8672 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8673 * milliseconds since the navigation start of the document). 8674 * 8675 * @property {number} value The value expressed as a float between 0 and 1. 8676 * @property {number} rawValue The raw MIDI value expressed as an integer between 0 and 127. 8677 */ 8678 event.value = Utilities.from7bitToFloat(data1); 8679 event.rawValue = data1; 8680 } else if (event.type === "pitchbend") { 8681 /** 8682 * Event emitted when a pitch bend MIDI message has been received. 8683 * 8684 * @event InputChannel#pitchbend 8685 * 8686 * @type {object} 8687 * @property {string} type `pitchbend` 8688 * 8689 * @property {InputChannel} target The object that dispatched the event. 8690 * @property {Input} port The `Input` that triggered the event. 8691 * @property {Message} message A [`Message`](Message) object containing information about the 8692 * incoming MIDI message. 8693 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8694 * milliseconds since the navigation start of the document). 8695 * 8696 * @property {number} value The value expressed as a float between 0 and 1. 8697 * @property {number} rawValue The raw MIDI value expressed as an integer (between 0 and 8698 * 16383). 8699 */ 8700 event.value = ((data2 << 7) + data1 - 8192) / 8192; 8701 event.rawValue = (data2 << 7) + data1; 8702 } else { 8703 event.type = "unknownmessage"; 8704 } 8705 this.emit(event.type, event); 8706 } 8707 8708 /** 8709 * @param e {Object} 8710 * @private 8711 */ 8712 _parseChannelModeMessage(e) { 8713 // Make a shallow copy of the incoming event so we can use it as the new event. 8714 const event = Object.assign({}, e); 8715 event.type = event.controller.name; 8716 8717 /** 8718 * Event emitted when an "all sound off" channel-mode MIDI message has been received. 8719 * 8720 * @event InputChannel#allsoundoff 8721 * 8722 * @type {object} 8723 * @property {string} type `allsoundoff` 8724 * 8725 * @property {InputChannel} target The object that dispatched the event. 8726 * @property {Input} port The `Input` that triggered the event. 8727 * @property {Message} message A [`Message`](Message) object containing information about the 8728 * incoming MIDI message. 8729 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8730 * milliseconds since the navigation start of the document). 8731 */ 8732 8733 /** 8734 * Event emitted when a "reset all controllers" channel-mode MIDI message has been received. 8735 * 8736 * @event InputChannel#resetallcontrollers 8737 * 8738 * @type {object} 8739 * 8740 * @property {InputChannel} target The object that dispatched the event. 8741 * @property {Input} port The `Input` that triggered the event. 8742 * @property {Message} message A [`Message`](Message) object containing information about the 8743 * incoming MIDI message. 8744 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8745 * milliseconds since the navigation start of the document). 8746 */ 8747 8748 /** 8749 * Event emitted when a "local control" channel-mode MIDI message has been received. The value 8750 * property of the event is set to either `true` (local control on) of `false` (local control 8751 * off). 8752 * 8753 * @event InputChannel#localcontrol 8754 * 8755 * @type {object} 8756 * @property {string} type `localcontrol` 8757 * 8758 * @property {InputChannel} target The object that dispatched the event. 8759 * @property {Input} port The `Input` that triggered the event. 8760 * @property {Message} message A [`Message`](Message) object containing information about the 8761 * incoming MIDI message. 8762 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8763 * milliseconds since the navigation start of the document). 8764 * 8765 * @property {boolean} value For local control on, the value is `true`. For local control off, 8766 * the value is `false`. 8767 * @property {boolean} rawValue For local control on, the value is `127`. For local control off, 8768 * the value is `0`. 8769 */ 8770 if (event.type === "localcontrol") { 8771 event.value = event.message.data[2] === 127 ? true : false; 8772 event.rawValue = event.message.data[2]; 8773 } 8774 8775 /** 8776 * Event emitted when an "all notes off" channel-mode MIDI message has been received. 8777 * 8778 * @event InputChannel#allnotesoff 8779 * 8780 * @type {object} 8781 * @property {string} type `allnotesoff` 8782 * 8783 * @property {InputChannel} target The object that dispatched the event. 8784 * @property {Input} port The `Input` that triggered the event. 8785 * @property {Message} message A [`Message`](Message) object containing information about the 8786 * incoming MIDI message. 8787 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8788 * milliseconds since the navigation start of the document). 8789 */ 8790 8791 /** 8792 * Event emitted when an "omni mode" channel-mode MIDI message has been received. The value 8793 * property of the event is set to either `true` (omni mode on) of `false` (omni mode off). 8794 * 8795 * @event InputChannel#omnimode 8796 * 8797 * @type {object} 8798 * @property {string} type `"omnimode"` 8799 * 8800 * @property {InputChannel} target The object that dispatched the event. 8801 * @property {Input} port The `Input` that triggered the event. 8802 * @property {Message} message A [`Message`](Message) object containing information about the 8803 * incoming MIDI message. 8804 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8805 * milliseconds since the navigation start of the document). 8806 * 8807 * @property {boolean} value The value is `true` for omni mode on and false for omni mode off. 8808 * @property {boolean} rawValue The raw MIDI value 8809 */ 8810 if (event.type === "omnimodeon") { 8811 event.type = "omnimode"; 8812 event.value = true; 8813 event.rawValue = event.message.data[2]; 8814 } else if (event.type === "omnimodeoff") { 8815 event.type = "omnimode"; 8816 event.value = false; 8817 event.rawValue = event.message.data[2]; 8818 } 8819 8820 /** 8821 * Event emitted when a "mono/poly mode" MIDI message has been received. The value property of 8822 * the event is set to either `true` (mono mode on / poly mode off) or `false` (mono mode off / 8823 * poly mode on). 8824 * 8825 * @event InputChannel#monomode 8826 * 8827 * @type {object} 8828 * @property {string} type `monomode` 8829 * 8830 * @property {InputChannel} target The object that dispatched the event. 8831 * @property {Input} port The `Input` that triggered the event. 8832 * @property {Message} message A [`Message`](Message) object containing information about the 8833 * incoming MIDI message. 8834 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8835 * milliseconds since the navigation start of the document). 8836 * 8837 * @property {boolean} value The value is `true` for omni mode on and false for omni mode off. 8838 * @property {boolean} rawValue The raw MIDI value 8839 */ 8840 if (event.type === "monomodeon") { 8841 event.type = "monomode"; 8842 event.value = true; 8843 event.rawValue = event.message.data[2]; 8844 } else if (event.type === "polymodeon") { 8845 event.type = "monomode"; 8846 event.value = false; 8847 event.rawValue = event.message.data[2]; 8848 } 8849 this.emit(event.type, event); 8850 } 8851 8852 /** 8853 * Parses inbound events to identify RPN/NRPN sequences. 8854 * @param e Event 8855 * @private 8856 */ 8857 _parseEventForParameterNumber(event) { 8858 // To make it more legible 8859 const controller = event.message.dataBytes[0]; 8860 const value = event.message.dataBytes[1]; 8861 8862 // A. Check if the message is the start of an RPN (101) or NRPN (99) parameter declaration. 8863 if (controller === 99 || controller === 101) { 8864 this._nrpnBuffer = []; 8865 this._rpnBuffer = []; 8866 if (controller === 99) { 8867 // 99 8868 this._nrpnBuffer = [event.message]; 8869 } else { 8870 // 101 8871 // 127 is a reset so we ignore it 8872 if (value !== 127) this._rpnBuffer = [event.message]; 8873 } 8874 8875 // B. Check if the message is the end of an RPN (100) or NRPN (98) parameter declaration. 8876 } else if (controller === 98 || controller === 100) { 8877 if (controller === 98) { 8878 // 98 8879 8880 // Flush the other buffer (they are mutually exclusive) 8881 this._rpnBuffer = []; 8882 8883 // Check if we are in sequence 8884 if (this._nrpnBuffer.length === 1) { 8885 this._nrpnBuffer.push(event.message); 8886 } else { 8887 this._nrpnBuffer = []; // out of sequence 8888 } 8889 } else { 8890 // 100 8891 8892 // Flush the other buffer (they are mutually exclusive) 8893 this._nrpnBuffer = []; 8894 8895 // 127 is a reset so we ignore it 8896 if (this._rpnBuffer.length === 1 && value !== 127) { 8897 this._rpnBuffer.push(event.message); 8898 } else { 8899 this._rpnBuffer = []; // out of sequence or reset 8900 } 8901 } 8902 8903 // C. Check if the message is for data entry (6, 38, 96 or 97). Those messages trigger events. 8904 } else if (controller === 6 || controller === 38 || controller === 96 || controller === 97) { 8905 if (this._rpnBuffer.length === 2) { 8906 this._dispatchParameterNumberEvent("rpn", this._rpnBuffer[0].dataBytes[1], this._rpnBuffer[1].dataBytes[1], event); 8907 } else if (this._nrpnBuffer.length === 2) { 8908 this._dispatchParameterNumberEvent("nrpn", this._nrpnBuffer[0].dataBytes[1], this._nrpnBuffer[1].dataBytes[1], event); 8909 } else { 8910 this._nrpnBuffer = []; 8911 this._rpnBuffer = []; 8912 } 8913 } 8914 } 8915 8916 /** 8917 * Indicates whether the specified controller can be part of an RPN or NRPN sequence 8918 * @param controller 8919 * @returns {boolean} 8920 * @private 8921 */ 8922 _isRpnOrNrpnController(controller) { 8923 return controller === 6 || controller === 38 || controller === 96 || controller === 97 || controller === 98 || controller === 99 || controller === 100 || controller === 101; 8924 } 8925 8926 /** 8927 * @private 8928 */ 8929 _dispatchParameterNumberEvent(type, paramMsb, paramLsb, e) { 8930 type = type === "nrpn" ? "nrpn" : "rpn"; 8931 8932 /** 8933 * Event emitted when an **RPN data entry coarse** message is received on the input. The 8934 * specific parameter to which the message applies can be found in the event's `parameter` 8935 * property. It is one of the ones defined in 8936 * [`Enumerations.REGISTERED_PARAMETERS`](Enumerations#REGISTERED_PARAMETERS). 8937 * 8938 * @event InputChannel#rpn-dataentrycoarse 8939 * 8940 * @type {object} 8941 * 8942 * @property {string} type `rpn-dataentrycoarse` 8943 * @property {InputChannel} target The object that dispatched the event. 8944 * @property {Input} port The `Input` that triggered the event. 8945 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8946 * milliseconds since the navigation start of the document). 8947 * @property {Message} message A [`Message`](Message) object containing information about the 8948 * incoming MIDI message. 8949 * @property {string} parameter The registered parameter's name 8950 * @property {number} parameterMsb The MSB portion of the registered parameter (0-127) 8951 * @property {number} parameterLsb: The LSB portion of the registered parameter (0-127) 8952 * @property {number} value The received value as a normalized number between 0 and 1. 8953 * @property {number} rawValue The value as received (0-127) 8954 */ 8955 8956 /** 8957 * Event emitted when an **RPN data entry fine** message is received on the input. The 8958 * specific parameter to which the message applies can be found in the event's `parameter` 8959 * property. It is one of the ones defined in 8960 * [`EnumerationsREGISTERED_PARAMETERS`](Enumerations#REGISTERED_PARAMETERS). 8961 * 8962 * @event InputChannel#rpn-dataentryfine 8963 * 8964 * @type {object} 8965 * 8966 * @property {string} type `rpn-dataentryfine` 8967 * @property {InputChannel} target The object that dispatched the event. 8968 * @property {Input} port The `Input` that triggered the event. 8969 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8970 * milliseconds since the navigation start of the document). 8971 * @property {Message} message A [`Message`](Message) object containing information about the 8972 * incoming MIDI message. 8973 * @property {string} parameter The registered parameter's name 8974 * @property {number} parameterMsb The MSB portion of the registered parameter (0-127) 8975 * @property {number} parameterLsb: The LSB portion of the registered parameter (0-127) 8976 * @property {number} value The received value as a normalized number between 0 and 1. 8977 * @property {number} rawValue The value as received (0-127) 8978 */ 8979 8980 /** 8981 * Event emitted when an **RPN data increment** message is received on the input. The specific 8982 * parameter to which the message applies can be found in the event's `parameter` property. It 8983 * is one of the ones defined in 8984 * [`Enumerations.REGISTERED_PARAMETERS`](Enumerations#REGISTERED_PARAMETERS). 8985 * 8986 * @event InputChannel#rpn-dataincrement 8987 * 8988 * @type {object} 8989 * 8990 * @property {string} type `rpn-dataincrement` 8991 * @property {InputChannel} target The object that dispatched the event. 8992 * @property {Input} port The `Input` that triggered the event. 8993 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 8994 * milliseconds since the navigation start of the document). 8995 * @property {Message} message A [`Message`](Message) object containing information about the 8996 * incoming MIDI message. 8997 * @property {string} parameter The registered parameter's name 8998 * @property {number} parameterMsb The MSB portion of the registered parameter (0-127) 8999 * @property {number} parameterLsb: The LSB portion of the registered parameter (0-127) 9000 * @property {number} value The received value as a normalized number between 0 and 1. 9001 * @property {number} rawValue The value as received (0-127) 9002 */ 9003 9004 /** 9005 * Event emitted when an **RPN data decrement** message is received on the input. The specific 9006 * parameter to which the message applies can be found in the event's `parameter` property. It 9007 * is one of the ones defined in 9008 * [`Enumerations.REGISTERED_PARAMETERS`](Enumerations#REGISTERED_PARAMETERS). 9009 * 9010 * @event InputChannel#rpn-datadecrement 9011 * 9012 * @type {object} 9013 * 9014 * @property {string} type `rpn-datadecrement` 9015 * @property {InputChannel} target The object that dispatched the event. 9016 * @property {Input} port The `Input` that triggered the event. 9017 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 9018 * milliseconds since the navigation start of the document). 9019 * @property {Message} message A [`Message`](Message) object containing information about the 9020 * incoming MIDI message. 9021 * @property {string} parameter The registered parameter's name 9022 * @property {number} parameterMsb The MSB portion of the registered parameter (0-127) 9023 * @property {number} parameterLsb: The LSB portion of the registered parameter (0-127) 9024 * @property {number} value The received value as a normalized number between 0 and 1. 9025 * @property {number} rawValue The value as received (0-127) 9026 */ 9027 9028 /** 9029 * Event emitted when an **NRPN data entry coarse** message is received on the input. The 9030 * specific parameter to which the message applies can be found in the event's `parameter` 9031 * property. It is one of the ones defined in 9032 * [`Enumerations.REGISTERED_PARAMETERS`](Enumerations#REGISTERED_PARAMETERS). 9033 * 9034 * @event InputChannel#nrpn-dataentrycoarse 9035 * 9036 * @type {object} 9037 * 9038 * @property {string} type `nrpn-dataentrycoarse` 9039 * @property {InputChannel} target The object that dispatched the event. 9040 * @property {Input} port The `Input` that triggered the event. 9041 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 9042 * milliseconds since the navigation start of the document). 9043 * @property {Message} message A [`Message`](Message) object containing information about the 9044 * incoming MIDI message. 9045 * @property {string} parameter The registered parameter's name 9046 * @property {number} parameterMsb The MSB portion of the registered parameter (0-127) 9047 * @property {number} parameterLsb: The LSB portion of the registered parameter (0-127) 9048 * @property {number} value The received value as a normalized number between 0 and 1. 9049 * @property {number} rawValue The value as received (0-127) 9050 */ 9051 9052 /** 9053 * Event emitted when an **NRPN data entry fine** message is received on the input. The 9054 * specific parameter to which the message applies can be found in the event's `parameter` 9055 * property. It is one of the ones defined in 9056 * [`Enumerations.REGISTERED_PARAMETERS`](Enumerations#REGISTERED_PARAMETERS). 9057 * 9058 * @event InputChannel#nrpn-dataentryfine 9059 * 9060 * @type {object} 9061 * 9062 * @property {string} type `nrpn-dataentryfine` 9063 * @property {InputChannel} target The object that dispatched the event. 9064 * @property {Input} port The `Input` that triggered the event. 9065 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 9066 * milliseconds since the navigation start of the document). 9067 * @property {Message} message A [`Message`](Message) object containing information about the 9068 * incoming MIDI message. 9069 * @property {string} parameter The registered parameter's name 9070 * @property {number} parameterMsb The MSB portion of the registered parameter (0-127) 9071 * @property {number} parameterLsb: The LSB portion of the registered parameter (0-127) 9072 * @property {number} value The received value as a normalized number between 0 and 1. 9073 * @property {number} rawValue The value as received (0-127) 9074 */ 9075 9076 /** 9077 * Event emitted when an **NRPN data increment** message is received on the input. The specific 9078 * parameter to which the message applies can be found in the event's `parameter` property. It 9079 * is one of the ones defined in 9080 * [`Enumerations.REGISTERED_PARAMETERS`](Enumerations#REGISTERED_PARAMETERS). 9081 * 9082 * @event InputChannel#nrpn-dataincrement 9083 * 9084 * @type {object} 9085 * 9086 * @property {string} type `nrpn-dataincrement` 9087 * @property {InputChannel} target The object that dispatched the event. 9088 * @property {Input} port The `Input` that triggered the event. 9089 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 9090 * milliseconds since the navigation start of the document). 9091 * @property {Message} message A [`Message`](Message) object containing information about the 9092 * incoming MIDI message. 9093 * @property {string} parameter The registered parameter's name 9094 * @property {number} parameterMsb The MSB portion of the registered parameter (0-127) 9095 * @property {number} parameterLsb: The LSB portion of the registered parameter (0-127) 9096 * @property {number} value The received value as a normalized number between 0 and 1. 9097 * @property {number} rawValue The value as received (0-127) 9098 */ 9099 9100 /** 9101 * Event emitted when an **NRPN data decrement** message is received on the input. The specific 9102 * parameter to which the message applies can be found in the event's `parameter` property. It 9103 * is one of the ones defined in 9104 * [`Enumerations.REGISTERED_PARAMETERS`](Enumerations#REGISTERED_PARAMETERS). 9105 * 9106 * @event InputChannel#nrpn-datadecrement 9107 * 9108 * @type {object} 9109 * 9110 * @property {string} type `nrpn-datadecrement` 9111 * @property {InputChannel} target The object that dispatched the event. 9112 * @property {Input} port The `Input` that triggered the event. 9113 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 9114 * milliseconds since the navigation start of the document). 9115 * @property {Message} message A [`Message`](Message) object containing information about the 9116 * incoming MIDI message. 9117 * @property {string} parameter The registered parameter's name 9118 * @property {number} parameterMsb The MSB portion of the registered parameter (0-127) 9119 * @property {number} parameterLsb: The LSB portion of the registered parameter (0-127) 9120 * @property {number} value The received value as a normalized number between 0 and 1. 9121 * @property {number} rawValue The value as received (0-127) 9122 */ 9123 9124 const event = { 9125 target: e.target, 9126 timestamp: e.timestamp, 9127 message: e.message, 9128 parameterMsb: paramMsb, 9129 parameterLsb: paramLsb, 9130 value: Utilities.from7bitToFloat(e.message.dataBytes[1]), 9131 rawValue: e.message.dataBytes[1] 9132 }; 9133 9134 // Identify the parameter (by name for RPN and by number for NRPN) 9135 if (type === "rpn") { 9136 event.parameter = Object.keys(Enumerations.REGISTERED_PARAMETERS).find(key => { 9137 return Enumerations.REGISTERED_PARAMETERS[key][0] === paramMsb && Enumerations.REGISTERED_PARAMETERS[key][1] === paramLsb; 9138 }); 9139 } else { 9140 event.parameter = (paramMsb << 7) + paramLsb; 9141 } 9142 9143 // Type and subtype 9144 const subtype = Enumerations.CONTROL_CHANGE_MESSAGES[e.message.dataBytes[0]].name; 9145 9146 // Emit specific event 9147 event.type = `${type}-${subtype}`; 9148 this.emit(event.type, event); 9149 9150 // Begin Legacy Block (remove in v4) 9151 const legacyEvent = Object.assign({}, event); 9152 if (legacyEvent.type === "nrpn-dataincrement") { 9153 legacyEvent.type = "nrpn-databuttonincrement"; 9154 } else if (legacyEvent.type === "nrpn-datadecrement") { 9155 legacyEvent.type = "nrpn-databuttondecrement"; 9156 } else if (legacyEvent.type === "rpn-dataincrement") { 9157 legacyEvent.type = "rpn-databuttonincrement"; 9158 } else if (legacyEvent.type === "rpn-datadecrement") { 9159 legacyEvent.type = "rpn-databuttondecrement"; 9160 } 9161 this.emit(legacyEvent.type, legacyEvent); 9162 // End Legacy Block 9163 9164 /** 9165 * Event emitted when any NRPN message is received on the input. There are four subtypes of NRPN 9166 * messages: 9167 * 9168 * * `nrpn-dataentrycoarse` 9169 * * `nrpn-dataentryfine` 9170 * * `nrpn-dataincrement` 9171 * * `nrpn-datadecrement` 9172 * 9173 * The parameter to which the message applies can be found in the event's `parameter` property. 9174 * 9175 * @event InputChannel#nrpn 9176 * 9177 * @type {object} 9178 * 9179 * @property {string} type `nrpn` 9180 * @property {string} subtype The precise type of NRPN message that was received. 9181 * @property {InputChannel} target The object that dispatched the event. 9182 * @property {Input} port The `Input` that triggered the event. 9183 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 9184 * milliseconds since the navigation start of the document). 9185 * @property {Message} message A [`Message`](Message) object containing information about the 9186 * incoming MIDI message. 9187 * @property {number} parameter The non-registered parameter number (0-16383) 9188 * @property {number} parameterMsb The MSB portion of the non-registered parameter number 9189 * (0-127) 9190 * @property {number} parameterLsb: The LSB portion of the non-registered parameter number 9191 * (0-127) 9192 * @property {number} value The received value as a normalized number between 0 and 1. 9193 * @property {number} rawValue The value as received (0-127) 9194 */ 9195 9196 /** 9197 * Event emitted when any RPN message is received on the input. There are four subtypes of RPN 9198 * messages: 9199 * 9200 * * `rpn-dataentrycoarse` 9201 * * `rpn-dataentryfine` 9202 * * `rpn-dataincrement` 9203 * * `rpn-datadecrement` 9204 * 9205 * The parameter to which the message applies can be found in the event's `parameter` property. 9206 * It is one of the ones defined in 9207 * [`Enumerations.REGISTERED_PARAMETERS`](Enumerations#REGISTERED_PARAMETERS). 9208 * 9209 * @event InputChannel#rpn 9210 * 9211 * @type {object} 9212 * 9213 * @property {string} type `rpn` 9214 * @property {string} subtype The precise type of RPN message that was received. 9215 * @property {InputChannel} target The object that dispatched the event. 9216 * @property {Input} port The `Input` that triggered the event. 9217 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 9218 * milliseconds since the navigation start of the document). 9219 * @property {Message} message A [`Message`](Message) object containing information about the 9220 * incoming MIDI message. 9221 * @property {string} parameter The registered parameter's name 9222 * @property {number} parameterMsb The MSB portion of the registered parameter (0-127) 9223 * @property {number} parameterLsb: The LSB portion of the registered parameter (0-127) 9224 * @property {number} value The received value as a normalized number between 0 and 1. 9225 * @property {number} rawValue The value as received (0-127) 9226 */ 9227 9228 // Emit general event 9229 event.type = type; 9230 event.subtype = subtype; 9231 this.emit(event.type, event); 9232 } 9233 9234 /** 9235 * @deprecated since version 3. 9236 * @private 9237 */ 9238 getChannelModeByNumber(number) { 9239 if (wm.validation) { 9240 console.warn("The 'getChannelModeByNumber()' method has been moved to the 'Utilities' class."); 9241 number = Math.floor(number); 9242 } 9243 return Utilities.getChannelModeByNumber(number); 9244 } 9245 9246 /** 9247 * @deprecated since version 3. 9248 * @private 9249 */ 9250 getCcNameByNumber(number) { 9251 if (wm.validation) { 9252 console.warn("The 'getCcNameByNumber()' method has been moved to the 'Utilities' class."); 9253 number = parseInt(number); 9254 if (!(number >= 0 && number <= 127)) throw new RangeError("Invalid control change number."); 9255 } 9256 return Utilities.getCcNameByNumber(number); 9257 } 9258 9259 /** 9260 * Returns the playing status of the specified note (`true` if the note is currently playing, 9261 * `false` if it is not). The `note` parameter can be an unsigned integer (0-127), a note 9262 * identifier (`"C4"`, `"G#5"`, etc.) or a [`Note`]{@link Note} object. 9263 * 9264 * IF the note is specified using an integer (0-127), no octave offset will be applied. 9265 * 9266 * @param {number|string|Note} note The note to get the state for. The 9267 * [`octaveOffset`](#octaveOffset) (channel, input and global) will be factored in for note 9268 * identifiers and [`Note`]{@link Note} objects. 9269 * @returns {boolean} 9270 * @since version 3.0.0 9271 */ 9272 getNoteState(note) { 9273 // If it's a note object, we simply use the identifier 9274 if (note instanceof Note) note = note.identifier; 9275 const number = Utilities.guessNoteNumber(note, wm.octaveOffset + this.input.octaveOffset + this.octaveOffset); 9276 return this.notesState[number]; 9277 } 9278 9279 /** 9280 * An integer to offset the reported octave of incoming note-specific messages (`noteon`, 9281 * `noteoff` and `keyaftertouch`). By default, middle C (MIDI note number 60) is placed on the 4th 9282 * octave (C4). 9283 * 9284 * If, for example, `octaveOffset` is set to 2, MIDI note number 60 will be reported as C6. If 9285 * `octaveOffset` is set to -1, MIDI note number 60 will be reported as C3. 9286 * 9287 * Note that this value is combined with the global offset value defined by 9288 * [`WebMidi.octaveOffset`](WebMidi#octaveOffset) object and with the value defined on the parent 9289 * input object with [`Input.octaveOffset`](Input#octaveOffset). 9290 * 9291 * @type {number} 9292 * 9293 * @since 3.0 9294 */ 9295 get octaveOffset() { 9296 return this._octaveOffset; 9297 } 9298 set octaveOffset(value) { 9299 if (this.validation) { 9300 value = parseInt(value); 9301 if (isNaN(value)) throw new TypeError("The 'octaveOffset' property must be an integer."); 9302 } 9303 this._octaveOffset = value; 9304 } 9305 9306 /** 9307 * The [`Input`](Input) this channel belongs to. 9308 * @type {Input} 9309 * @since 3.0 9310 */ 9311 get input() { 9312 return this._input; 9313 } 9314 9315 /** 9316 * This channel's MIDI number (1-16). 9317 * @type {number} 9318 * @since 3.0 9319 */ 9320 get number() { 9321 return this._number; 9322 } 9323 9324 /** 9325 * Whether RPN/NRPN events are parsed and dispatched. 9326 * @type {boolean} 9327 * @since 3.0 9328 * @deprecated Use parameterNumberEventsEnabled instead. 9329 * @private 9330 */ 9331 get nrpnEventsEnabled() { 9332 return this.parameterNumberEventsEnabled; 9333 } 9334 set nrpnEventsEnabled(value) { 9335 if (this.validation) { 9336 value = !!value; 9337 } 9338 this.parameterNumberEventsEnabled = value; 9339 } 9340 } 9341 9342 /** 9343 * The `Message` class represents a single MIDI message. It has several properties that make it 9344 * easy to make sense of the binary data it contains. 9345 * 9346 * @license Apache-2.0 9347 * @since 3.0.0 9348 */ 9349 class Message { 9350 /** 9351 * Creates a new `Message` object from raw MIDI data. 9352 * 9353 * @param {Uint8Array} data The raw data of the MIDI message as a 9354 * [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) 9355 * of integers between `0` and `255`. 9356 */ 9357 constructor(data) { 9358 /** 9359 * A 9360 * [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) 9361 * containing the bytes of the MIDI message. Each byte is an integer between `0` and `255`. 9362 * 9363 * @type {Uint8Array} 9364 * @readonly 9365 */ 9366 this.rawData = data; 9367 9368 /** 9369 * An array containing all the bytes of the MIDI message. Each byte is an integer between `0` 9370 * and `255`. 9371 * 9372 * @type {number[]} 9373 * @readonly 9374 */ 9375 this.data = Array.from(this.rawData); 9376 9377 /** 9378 * The MIDI status byte of the message as an integer between `0` and `255`. 9379 * 9380 * @type {number} 9381 * @readonly 9382 */ 9383 this.statusByte = this.rawData[0]; 9384 9385 /** 9386 * A 9387 * [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) 9388 * of the data byte(s) of the MIDI message. When the message is a system exclusive message 9389 * (sysex), `rawDataBytes` explicitly excludes the manufacturer ID and the sysex end byte so 9390 * only the actual data is included. 9391 * 9392 * @type {Uint8Array} 9393 * @readonly 9394 */ 9395 this.rawDataBytes = this.rawData.slice(1); 9396 9397 /** 9398 * An array of the the data byte(s) of the MIDI message (as opposed to the status byte). When 9399 * the message is a system exclusive message (sysex), `dataBytes` explicitly excludes the 9400 * manufacturer ID and the sysex end byte so only the actual data is included. 9401 * 9402 * @type {number[]} 9403 * @readonly 9404 */ 9405 this.dataBytes = this.data.slice(1); 9406 9407 /** 9408 * A boolean indicating whether the MIDI message is a channel-specific message. 9409 * 9410 * @type {boolean} 9411 * @readonly 9412 */ 9413 this.isChannelMessage = false; 9414 9415 /** 9416 * A boolean indicating whether the MIDI message is a system message (not specific to a 9417 * channel). 9418 * 9419 * @type {boolean} 9420 * @readonly 9421 */ 9422 this.isSystemMessage = false; 9423 9424 /** 9425 * An integer identifying the MIDI command. For channel-specific messages, the value is 4-bit 9426 * and will be between `8` and `14`. For system messages, the value will be between `240` and 9427 * `255`. 9428 * 9429 * @type {number} 9430 * @readonly 9431 */ 9432 this.command = undefined; 9433 9434 /** 9435 * The MIDI channel number (`1` - `16`) that the message is targeting. This is only for 9436 * channel-specific messages. For system messages, this will be left `undefined`. 9437 * 9438 * @type {number} 9439 * @readonly 9440 */ 9441 this.channel = undefined; 9442 9443 /** 9444 * When the message is a system exclusive message (sysex), this property contains an array with 9445 * either 1 or 3 entries that identify the manufacturer targeted by the message. 9446 * 9447 * To know how to translate these entries into manufacturer names, check out the official list: 9448 * https://www.midi.org/specifications-old/item/manufacturer-id-numbers 9449 * 9450 * @type {number[]} 9451 * @readonly 9452 */ 9453 this.manufacturerId = undefined; 9454 9455 /** 9456 * The type of message as a string (`"noteon"`, `"controlchange"`, `"sysex"`, etc.) 9457 * 9458 * @type {string} 9459 * @readonly 9460 */ 9461 this.type = undefined; 9462 9463 // Assign values to property that vary according to whether they are channel-specific or system 9464 if (this.statusByte < 240) { 9465 this.isChannelMessage = true; 9466 this.command = this.statusByte >> 4; 9467 this.channel = (this.statusByte & 0b00001111) + 1; 9468 } else { 9469 this.isSystemMessage = true; 9470 this.command = this.statusByte; 9471 } 9472 9473 // Assign type (depending on whether the message is channel-specific or system) 9474 if (this.isChannelMessage) { 9475 this.type = Utilities.getPropertyByValue(Enumerations.CHANNEL_MESSAGES, this.command); 9476 } else if (this.isSystemMessage) { 9477 this.type = Utilities.getPropertyByValue(Enumerations.SYSTEM_MESSAGES, this.command); 9478 } 9479 9480 // When the message is a sysex message, we add a manufacturer property and strip out the id from 9481 // dataBytes and rawDataBytes. 9482 if (this.statusByte === Enumerations.SYSTEM_MESSAGES.sysex) { 9483 if (this.dataBytes[0] === 0) { 9484 this.manufacturerId = this.dataBytes.slice(0, 3); 9485 this.dataBytes = this.dataBytes.slice(3, this.rawDataBytes.length - 1); 9486 this.rawDataBytes = this.rawDataBytes.slice(3, this.rawDataBytes.length - 1); 9487 } else { 9488 this.manufacturerId = [this.dataBytes[0]]; 9489 this.dataBytes = this.dataBytes.slice(1, this.dataBytes.length - 1); 9490 this.rawDataBytes = this.rawDataBytes.slice(1, this.rawDataBytes.length - 1); 9491 } 9492 } 9493 } 9494 } 9495 9496 /** 9497 * The `Input` class represents a single MIDI input port. This object is automatically instantiated 9498 * by the library according to the host's MIDI subsystem and does not need to be directly 9499 * instantiated. Instead, you can access all `Input` objects by referring to the 9500 * [`WebMidi.inputs`](WebMidi#inputs) array. You can also retrieve inputs by using methods such as 9501 * [`WebMidi.getInputByName()`](WebMidi#getInputByName) and 9502 * [`WebMidi.getInputById()`](WebMidi#getInputById). 9503 * 9504 * Note that a single MIDI device may expose several inputs and/or outputs. 9505 * 9506 * **Important**: the `Input` class does not directly fire channel-specific MIDI messages 9507 * (such as [`noteon`](InputChannel#event
9507:noteon) or 9508 * [`controlchange`](InputChannel#event:controlchange), etc.). The [`InputChannel`](InputChannel) 9509 * object does that. However, you can still use the 9510 * [`Input.addListener()`](#addListener) method to listen to channel-specific events on multiple 9511 * [`InputChannel`](InputChannel) objects at once. 9512 * 9513 * @fires Input#opened 9514 * @fires Input#disconnected 9515 * @fires Input#closed 9516 * @fires Input#midimessage 9517 * 9518 * @fires Input#sysex
9519 * @fires Input#timecode 9520 * @fires Input#songposition 9521 * @fires Input#songselect 9522 * @fires Input#tunerequest 9523 * @fires Input#clock 9524 * @fires Input#start 9525 * @fires Input#continue 9526 * @fires Input#stop 9527 * @fires Input#activesensing 9528 * @fires Input#reset 9529 * 9530 * @fires Input#unknownmidimessage 9531 * 9532 * @extends EventEmitter 9533 * @license Apache-2.0 9534 */ 9535 class Input extends EventEmitter { 9536 /** 9537 * Creates an `Input` object. 9538 * 9539 * @param {MIDIInput} midiInput [`MIDIInput`](https://developer.mozilla.org/en-US/docs/Web/API/MIDIInput) 9540 * object as provided by the MIDI subsystem (Web MIDI API). 9541 */ 9542 constructor(midiInput) { 9543 super(); 9544 9545 /** 9546 * Reference to the actual MIDIInput object 9547 * @private 9548 */ 9549 this._midiInput = midiInput; 9550 9551 /** 9552 * @type {number} 9553 * @private 9554 */ 9555 this._octaveOffset = 0; 9556 9557 /** 9558 * Array containing the 16 [`InputChannel`](InputChannel) objects available for this `Input`. The 9559 * channels are numbered 1 through 16. 9560 * 9561 * @type {InputChannel[]} 9562 */ 9563 this.channels = []; 9564 for (let i = 1; i <= 16; i++) this.channels[i] = new InputChannel(this, i); 9565 9566 /** 9567 * @type {Forwarder[]} 9568 * @private 9569 */ 9570 this._forwarders = []; 9571 9572 // Setup listeners 9573 this._midiInput.onstatechange = this._onStateChange.bind(this); 9574 this._midiInput.onmidimessage = this._onMidiMessage.bind(this); 9575 } 9576 9577 /** 9578 * Destroys the `Input` by removing all listeners, emptying the [`channels`](#channels) array and 9579 * unlinking the MIDI subsystem. This is mostly for internal use. 9580 * 9581 * @returns {Promise<void>} 9582 */ 9583 async destroy() { 9584 this.removeListener(); 9585 this.channels.forEach(ch => ch.destroy()); 9586 this.channels = []; 9587 this._forwarders = []; 9588 if (this._midiInput) { 9589 this._midiInput.onstatechange = null; 9590 this._midiInput.onmidimessage = null; 9591 } 9592 await this.close(); 9593 this._midiInput = null; 9594 } 9595 9596 /** 9597 * Executed when a `"statechange"` event occurs. 9598 * 9599 * @param e 9600 * @private 9601 */ 9602 _onStateChange(e) { 9603 let event = { 9604 timestamp: wm.time, 9605 target: this, 9606 port: this // for consistency 9607 }; 9608 9609 if (e.port.connection === "open") { 9610 /** 9611 * Event emitted when the `Input` has been opened by calling the [`open()`]{@link #open} 9612 * method. 9613 * 9614 * @event Input#opened 9615 * @type {object} 9616 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 9617 * milliseconds since the navigation start of the document). 9618 * @property {string} type `opened` 9619 * @property {Input} target The object that dispatched the event. 9620 * @property {Input} port The `Input` that triggered the event. 9621 */ 9622 event.type = "opened"; 9623 this.emit("opened", event); 9624 } else if (e.port.connection === "closed" && e.port.state === "connected") { 9625 /** 9626 * Event emitted when the `Input` has been closed by calling the 9627 * [`close()`]{@link #close} method. 9628 * 9629 * @event Input#closed 9630 * @type {object} 9631 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 9632 * milliseconds since the navigation start of the document). 9633 * @property {string} type `closed` 9634 * @property {Input} target The object that dispatched the event. 9635 * @property {Input} port The `Input` that triggered the event. 9636 */ 9637 event.type = "closed"; 9638 this.emit("closed", event); 9639 } else if (e.port.connection === "closed" && e.port.state === "disconnected") { 9640 /** 9641 * Event emitted when the `Input` becomes unavailable. This event is typically fired 9642 * when the MIDI device is unplugged. 9643 * 9644 * @event Input#disconnected 9645 * @type {object} 9646 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 9647 * milliseconds since the navigation start of the document). 9648 * @property {string} type `disconnected` 9649 * @property {Input} port Object with properties describing the {@link Input} that was 9650 * disconnected. This is not the actual `Input` as it is no longer available. 9651 * @property {Input} target The object that dispatched the event. 9652 */ 9653 event.type = "disconnected"; 9654 event.port = { 9655 connection: e.port.connection, 9656 id: e.port.id, 9657 manufacturer: e.port.manufacturer, 9658 name: e.port.name, 9659 state: e.port.state, 9660 type: e.port.type 9661 };
vendor: 13,045 bytes, lines 9662-9961
9662 this.emit("disconnected", event); 9663 } else if (e.port.connection === "pending" && e.port.state === "disconnected") ; else { 9664 console.warn("This statechange event was not caught: ", e.port.connection, e.port.state); 9665 } 9666 } 9667 9668 /** 9669 * Executed when a `"midimessage"` event is received 9670 * @param e 9671 * @private 9672 */ 9673 _onMidiMessage(e) { 9674 // Create Message object from MIDI data 9675 const message = new Message(e.data); 9676 9677 /** 9678 * Event emitted when any MIDI message is received on an `Input`. 9679 * 9680 * @event Input#midimessage 9681 * 9682 * @type {object} 9683 * 9684 * @property {Input} port The `Input` that triggered the event. 9685 * @property {Input} target The object that dispatched the event. 9686 * @property {Message} message A [`Message`](Message) object containing information about the 9687 * incoming MIDI message. 9688 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 9689 * milliseconds since the navigation start of the document). 9690 * @property {string} type `midimessage` 9691 * 9692 * @since 2.1 9693 */ 9694 const event = { 9695 port: this, 9696 target: this, 9697 message: message, 9698 timestamp: e.timeStamp, 9699 type: "midimessage", 9700 data: message.data, 9701 // @deprecated (will be removed in v4) 9702 rawData: message.data, 9703 // @deprecated (will be removed in v4) 9704 statusByte: message.data[0], 9705 // @deprecated (will be removed in v4) 9706 dataBytes: message.dataBytes // @deprecated (will be removed in v4) 9707 }; 9708 9709 this.emit("midimessage", event); 9710 9711 // Messages are forwarded to InputChannel if they are channel messages or parsed locally for 9712 // system messages. 9713 if (message.isSystemMessage) { 9714 // system messages 9715 this._parseEvent(event); 9716 } else if (message.isChannelMessage) { 9717 // channel messages 9718 this.channels[message.channel]._processMidiMessageEvent(event); 9719 } 9720 9721 // Forward message if forwarders have been defined 9722 this._forwarders.forEach(forwarder => forwarder.forward(message)); 9723 } 9724 9725 /** 9726 * @private 9727 */ 9728 _parseEvent(e) { 9729 // Make a shallow copy of the incoming event so we can use it as the new event. 9730 const event = Object.assign({}, e); 9731 event.type = event.message.type || "unknownmidimessage"; 9732 9733 // Add custom property for 'songselect' 9734 if (event.type === "songselect") { 9735 event.song = e.data[1] + 1; // deprecated 9736 event.value = e.data[1]; 9737 event.rawValue = event.value; 9738 } 9739 9740 // Emit event 9741 this.emit(event.type, event); 9742 } 9743 9744 /** 9745 * Opens the input for usage. This is usually unnecessary as the port is opened automatically when 9746 * WebMidi is enabled. 9747 * 9748 * @returns {Promise<Input>} The promise is fulfilled with the `Input` object. 9749 */ 9750 async open() { 9751 // Explicitly opens the port for usage. This is not mandatory. When the port is not explicitly 9752 // opened, it is implicitly opened (asynchronously) when assigning a listener to the 9753 // `onmidimessage` property of the `MIDIInput`. We do it explicitly so that 'connected' events 9754 // are dispatched immediately and that we are ready to listen. 9755 try { 9756 await this._midiInput.open(); 9757 } catch (err) { 9758 return Promise.reject(err); 9759 } 9760 return Promise.resolve(this); 9761 } 9762 9763 /** 9764 * Closes the input. When an input is closed, it cannot be used to listen to MIDI messages until 9765 * the input is opened again by calling [`Input.open()`](Input#open). 9766 * 9767 * **Note**: if what you want to do is stop events from being dispatched, you should use 9768 * [`eventsSuspended`](#eventsSuspended) instead. 9769 * 9770 * @returns {Promise<Input>} The promise is fulfilled with the `Input` object 9771 */ 9772 async close() { 9773 // We close the port. This triggers a statechange event which, in turn, will emit the 'closed' 9774 // event. 9775 if (!this._midiInput) return Promise.resolve(this); 9776 try { 9777 await this._midiInput.close(); 9778 } catch (err) { 9779 return Promise.reject(err); 9780 } 9781 return Promise.resolve(this); 9782 } 9783 9784 /** 9785 * @private 9786 * @deprecated since v3.0.0 (moved to 'Utilities' class) 9787 */ 9788 getChannelModeByNumber() { 9789 if (wm.validation) { 9790 console.warn("The 'getChannelModeByNumber()' method has been moved to the 'Utilities' class."); 9791 } 9792 } 9793 9794 /** 9795 * Adds an event listener that will trigger a function callback when the specified event is 9796 * dispatched. The event usually is **input-wide** but can also be **channel-specific**. 9797 * 9798 * Input-wide events do not target a specific MIDI channel so it makes sense to listen for them 9799 * at the `Input` level and not at the [`InputChannel`](InputChannel) level. Channel-specific 9800 * events target a specific channel. Usually, in this case, you would add the listener to the 9801 * [`InputChannel`](InputChannel) object. However, as a convenience, you can also listen to 9802 * channel-specific events directly on an `Input`. This allows you to react to a channel-specific 9803 * event no matter which channel it actually came through. 9804 * 9805 * When listening for an event, you simply need to specify the event name and the function to 9806 * execute: 9807 * 9808 * ```javascript 9809 * const listener = WebMidi.inputs[0].addListener("midimessage", e => { 9810 * console.log(e); 9811 * }); 9812 * ``` 9813 * 9814 * Calling the function with an input-wide event (such as 9815 * [`"midimessage"`]{@link #event:midimessage}), will return the [`Listener`](Listener) object 9816 * that was created. 9817 * 9818 * If you call the function with a channel-specific event (such as 9819 * [`"noteon"`]{@link InputChannel#event:noteon}), it will return an array of all 9820 * [`Listener`](Listener) objects that were created (one for each channel): 9821 * 9822 * ```javascript 9823 * const listeners = WebMidi.inputs[0].addListener("noteon", someFunction); 9824 * ``` 9825 * 9826 * You can also specify which channels you want to add the listener to: 9827 * 9828 * ```javascript 9829 * const listeners = WebMidi.inputs[0].addListener("noteon", someFunction, {channels: [1, 2, 3]}); 9830 * ``` 9831 * 9832 * In this case, `listeners` is an array containing 3 [`Listener`](Listener) objects. The order of 9833 * the listeners in the array follows the order the channels were specified in. 9834 * 9835 * Note that, when adding channel-specific listeners, it is the [`InputChannel`](InputChannel) 9836 * instance that actually gets a listener added and not the `Input` instance. You can check that 9837 * by calling [`InputChannel.hasListener()`](InputChannel#hasListener()). 9838 * 9839 * There are 8 families of events you can listen to: 9840 * 9841 * 1. **MIDI System Common** Events (input-wide) 9842 * 9843 * * [`songposition`]{@link Input#event:songposition} 9844 * * [`songselect`]{@link Input#event:songselect} 9845 * * [`sysex`]{@link Input#event:sysex} 9846 * * [`timecode`]{@link Input#event:timecode} 9847 * * [`tunerequest`]{@link Input#event:tunerequest} 9848 * 9849 * 2. **MIDI System Real-Time** Events (input-wide) 9850 * 9851 * * [`clock`]{@link Input#event:clock} 9852 * * [`start`]{@link Input#event:start} 9853 * * [`continue`]{@link Input#event:continue} 9854 * * [`stop`]{@link Input#event:stop} 9855 * * [`activesensing`]{@link Input#event:activesensing} 9856 * * [`reset`]{@link Input#event:reset} 9857 * 9858 * 3. **State Change** Events (input-wide) 9859 * 9860 * * [`opened`]{@link Input#event:opened} 9861 * * [`closed`]{@link Input#event:closed} 9862 * * [`disconnected`]{@link Input#event:disconnected} 9863 * 9864 * 4. **Catch-All** Events (input-wide) 9865 * 9866 * * [`midimessage`]{@link Input#event:midimessage} 9867 * * [`unknownmidimessage`]{@link Input#event:unknownmidimessage} 9868 * 9869 * 5. **Channel Voice** Events (channel-specific) 9870 * 9871 * * [`channelaftertouch`]{@link InputChannel#event:channelaftertouch} 9872 * * [`controlchange`]{@link InputChannel#event:controlchange} 9873 * * [`controlchange-controller0`]{@link InputChannel#event:controlchange-controller0} 9874 * * [`controlchange-controller1`]{@link InputChannel#event:controlchange-controller1} 9875 * * [`controlchange-controller2`]{@link InputChannel#event:controlchange-controller2} 9876 * * (...) 9877 * * [`controlchange-controller127`]{@link InputChannel#event:controlchange-controller127} 9878 * * [`keyaftertouch`]{@link InputChannel#event:keyaftertouch} 9879 * * [`noteoff`]{@link InputChannel#event:noteoff} 9880 * * [`noteon`]{@link InputChannel#event:noteon} 9881 * * [`pitchbend`]{@link InputChannel#event:pitchbend} 9882 * * [`programchange`]{@link InputChannel#event:programchange} 9883 * 9884 * Note: you can listen for a specific control change message by using an event name like this: 9885 * `controlchange-controller23`, `controlchange-controller99`, `controlchange-controller122`, 9886 * etc. 9887 * 9888 * 6. **Channel Mode** Events (channel-specific) 9889 * 9890 * * [`allnotesoff`]{@link InputChannel#event:allnotesoff} 9891 * * [`allsoundoff`]{@link InputChannel#event:allsoundoff} 9892 * * [`localcontrol`]{@link InputChannel#event:localcontrol} 9893 * * [`monomode`]{@link InputChannel#event:monomode} 9894 * * [`omnimode`]{@link InputChannel#event:omnimode} 9895 * * [`resetallcontrollers`]{@link InputChannel#event:resetallcontrollers} 9896 * 9897 * 7. **NRPN** Events (channel-specific) 9898 * 9899 * * [`nrpn`]{@link InputChannel#event:nrpn} 9900 * * [`nrpn-dataentrycoarse`]{@link InputChannel#event:nrpn-dataentrycoarse} 9901 * * [`nrpn-dataentryfine`]{@link InputChannel#event:nrpn-dataentryfine} 9902 * * [`nrpn-dataincrement`]{@link InputChannel#event:nrpn-dataincrement} 9903 * * [`nrpn-datadecrement`]{@link InputChannel#event:nrpn-datadecrement} 9904 * 9905 * 8. **RPN** Events (channel-specific) 9906 * 9907 * * [`rpn`]{@link InputChannel#event:rpn} 9908 * * [`rpn-dataentrycoarse`]{@link InputChannel#event:rpn-dataentrycoarse} 9909 * * [`rpn-dataentryfine`]{@link InputChannel#event:rpn-dataentryfine} 9910 * * [`rpn-dataincrement`]{@link InputChannel#event:rpn-dataincrement} 9911 * * [`rpn-datadecrement`]{@link InputChannel#event:rpn-datadecrement} 9912 * 9913 * @param event {string | EventEmitter.ANY_EVENT} The type of the event. 9914 * 9915 * @param listener {function} A callback function to execute when the specified event is detected. 9916 * This function will receive an event parameter object. For details on this object's properties, 9917 * check out the documentation for the various events (links above). 9918 * 9919 * @param {object} [options={}] 9920 * 9921 * @param {array} [options.arguments] An array of arguments which will be passed separately to the 9922 * callback function. This array is stored in the [`arguments`](Listener#arguments) property of 9923 * the [`Listener`](Listener) object and can be retrieved or modified as desired. 9924 * 9925 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 9926 * An integer between 1 and 16 or an array of such integers representing the MIDI channel(s) to 9927 * listen on. If no channel is specified, all channels will be used. This parameter is ignored for 9928 * input-wide events. 9929 * 9930 * @param {object} [options.context=this] The value of `this` in the callback function. 9931 * 9932 * @param {number} [options.duration=Infinity] The number of milliseconds before the listener 9933 * automatically expires. 9934 * 9935 * @param {boolean} [options.prepend=false] Whether the listener should be added at the beginning 9936 * of the listeners array and thus be triggered before others. 9937 * 9938 * @param {number} [options.remaining=Infinity] The number of times after which the callback 9939 * should automatically be removed. 9940 * 9941 * @returns {Listener|Listener[]} If the event is input-wide, a single [`Listener`](Listener) 9942 * object is returned. If the event is channel-specific, an array of all the 9943 * [`Listener`](Listener) objects is returned (one for each channel). 9944 */ 9945 addListener(event, listener, options = {}) { 9946 if (wm.validation) { 9947 // Legacy compatibility 9948 if (typeof options === "function") { 9949 let channels = listener != undefined ? [].concat(listener) : undefined; // clone 9950 listener = options; 9951 options = { 9952 channels: channels 9953 }; 9954 } 9955 } 9956 9957 // Check if the event is channel-specific or input-wide 9958 if (Enumerations.CHANNEL_EVENTS.includes(event)) { 9959 // If no channel defined, use all. 9960 if (options.channels === undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 9961 let listeners = [];
9962 Utilities.sanitizeChannels(options.channels).forEach(ch => { 9963 listeners.push(this.channels[ch].addListener(event, listener, options)); 9964 }); 9965 return listeners; 9966 } else { 9967 return super.addListener(event, listener, options); 9968 } 9969 } 9970 9971 /** 9972 * Adds a one-time event listener that will trigger a function callback when the specified event 9973 * happens. The event can be **channel-bound** or **input-wide**. Channel-bound events are 9974 * dispatched by [`InputChannel`]{@link InputChannel} objects and are tied to a specific MIDI 9975 * channel while input-wide events are dispatched by the `Input` object itself and are not tied 9976 * to a specific channel. 9977 * 9978 * Calling the function with an input-wide event (such as 9979 * [`"midimessage"`]{@link #event:midimessage}), will return the [`Listener`](Listener) object 9980 * that was created. 9981 * 9982 * If you call the function with a channel-specific event (such as 9983 * [`"noteon"`]{@link InputChannel#event:noteon}), it will return an array of all 9984 * [`Listener`](Listener) objects that were created (one for each channel): 9985 * 9986 * ```javascript 9987 * const listeners = WebMidi.inputs[0].addOneTimeListener("noteon", someFunction); 9988 * ``` 9989 * 9990 * You can also specify which channels you want to add the listener to: 9991 * 9992 * ```javascript 9993 * const listeners = WebMidi.inputs[0].addOneTimeListener("noteon", someFunction, {channels: [1, 2, 3]}); 9994 * ``` 9995 * 9996 * In this case, the `listeners` variable contains an array of 3 [`Listener`](Listener) objects. 9997 * 9998 * The code above will add a listener for the `"noteon"` event and call `someFunction` when the 9999 * event is triggered on MIDI channels `1`, `2` or `3`. 10000 * 10001 * Note that, when adding events to channels, it is the [`InputChannel`](InputChannel) instance 10002 * that actually gets a listener added and not the `Input` instance. 10003 * 10004 * Note: if you want to add a listener to a single MIDI channel you should probably do so directly 10005 * on the [`InputChannel`](InputChannel) object itself. 10006 * 10007 * There are 8 families of events you can listen to: 10008 * 10009 * 1. **MIDI System Common** Events (input-wide) 10010 * 10011 * * [`songposition`]{@link Input#event:songposition} 10012 * * [`songselect`]{@link Input#event:songselect} 10013 * * [`sysex`]{@link Input#event:sysex} 10014 * * [`timecode`]{@link Input#event:timecode} 10015 * * [`tunerequest`]{@link Input#event:tunerequest} 10016 * 10017 * 2. **MIDI System Real-Time** Events (input-wide) 10018 * 10019 * * [`clock`]{@link Input#event:clock} 10020 * * [`start`]{@link Input#event:start} 10021 * * [`continue`]{@link Input#event:continue} 10022 * * [`stop`]{@link Input#event:stop} 10023 * * [`activesensing`]{@link Input#event:activesensing} 10024 * * [`reset`]{@link Input#event:reset} 10025 * 10026 * 3. **State Change** Events (input-wide) 10027 * 10028 * * [`opened`]{@link Input#event:opened} 10029 * * [`closed`]{@link Input#event:closed} 10030 * * [`disconnected`]{@link Input#event:disconnected} 10031 * 10032 * 4. **Catch-All** Events (input-wide) 10033 * 10034 * * [`midimessage`]{@link Input#event:midimessage} 10035 * * [`unknownmidimessage`]{@link Input#event:unknownmidimessage} 10036 * 10037 * 5. **Channel Voice** Events (channel-specific) 10038 * 10039 * * [`channelaftertouch`]{@link InputChannel#event:channelaftertouch} 10040 * * [`controlchange`]{@link InputChannel#event:controlchange} 10041 * * [`controlchange-controller0`]{@link InputChannel#event:controlchange-controller0} 10042 * * [`controlchange-controller1`]{@link InputChannel#event:controlchange-controller1} 10043 * * [`controlchange-controller2`]{@link InputChannel#event:controlchange-controller2} 10044 * * (...) 10045 * * [`controlchange-controller127`]{@link InputChannel#event:controlchange-controller127}
vendor: 13,148 bytes, lines 10045-10379
10045 10046 * * [`keyaftertouch`]{@link InputChannel#event:keyaftertouch} 10047 * * [`noteoff`]{@link InputChannel#event:noteoff} 10048 * * [`noteon`]{@link InputChannel#event:noteon} 10049 * * [`pitchbend`]{@link InputChannel#event:pitchbend} 10050 * * [`programchange`]{@link InputChannel#event:programchange} 10051 * 10052 * Note: you can listen for a specific control change message by using an event name like this: 10053 * `controlchange-controller23`, `controlchange-controller99`, `controlchange-controller122`, 10054 * etc. 10055 * 10056 * 6. **Channel Mode** Events (channel-specific) 10057 * 10058 * * [`allnotesoff`]{@link InputChannel#event:allnotesoff} 10059 * * [`allsoundoff`]{@link InputChannel#event:allsoundoff} 10060 * * [`localcontrol`]{@link InputChannel#event:localcontrol} 10061 * * [`monomode`]{@link InputChannel#event:monomode} 10062 * * [`omnimode`]{@link InputChannel#event:omnimode} 10063 * * [`resetallcontrollers`]{@link InputChannel#event:resetallcontrollers} 10064 * 10065 * 7. **NRPN** Events (channel-specific) 10066 * 10067 * * [`nrpn`]{@link InputChannel#event:nrpn} 10068 * * [`nrpn-dataentrycoarse`]{@link InputChannel#event:nrpn-dataentrycoarse} 10069 * * [`nrpn-dataentryfine`]{@link InputChannel#event:nrpn-dataentryfine} 10070 * * [`nrpn-dataincrement`]{@link InputChannel#event:nrpn-dataincrement} 10071 * * [`nrpn-datadecrement`]{@link InputChannel#event:nrpn-datadecrement} 10072 * 10073 * 8. **RPN** Events (channel-specific) 10074 * 10075 * * [`rpn`]{@link InputChannel#event:rpn} 10076 * * [`rpn-dataentrycoarse`]{@link InputChannel#event:rpn-dataentrycoarse} 10077 * * [`rpn-dataentryfine`]{@link InputChannel#event:rpn-dataentryfine} 10078 * * [`rpn-dataincrement`]{@link InputChannel#event:rpn-dataincrement} 10079 * * [`rpn-datadecrement`]{@link InputChannel#event:rpn-datadecrement} 10080 * 10081 * @param event {string} The type of the event. 10082 * 10083 * @param listener {function} A callback function to execute when the specified event is detected. 10084 * This function will receive an event parameter object. For details on this object's properties, 10085 * check out the documentation for the various events (links above). 10086 * 10087 * @param {object} [options={}] 10088 * 10089 * @param {array} [options.arguments] An array of arguments which will be passed separately to the 10090 * callback function. This array is stored in the [`arguments`](Listener#arguments) property of 10091 * the [`Listener`](Listener) object and can be retrieved or modified as desired. 10092 * 10093 * @param {number|number[]} [options.channels] An integer between 1 and 16 or an array of 10094 * such integers representing the MIDI channel(s) to listen on. This parameter is ignored for 10095 * input-wide events. 10096 * 10097 * @param {object} [options.context=this] The value of `this` in the callback function. 10098 * 10099 * @param {number} [options.duration=Infinity] The number of milliseconds before the listener 10100 * automatically expires. 10101 * 10102 * @param {boolean} [options.prepend=false] Whether the listener should be added at the beginning 10103 * of the listeners array and thus be triggered before others. 10104 * 10105 * @returns {Listener[]} An array of all [`Listener`](Listener) objects that were created. 10106 */ 10107 addOneTimeListener(event, listener, options = {}) { 10108 options.remaining = 1; 10109 return this.addListener(event, listener, options); 10110 } 10111 10112 /** 10113 * This is an alias to the [Input.addListener()]{@link Input#addListener} method. 10114 * @since 2.0.0 10115 * @deprecated since v3.0 10116 * @private 10117 */ 10118 on(event, channel, listener, options) { 10119 return this.addListener(event, channel, listener, options); 10120 } 10121 10122 /** 10123 * Checks if the specified event type is already defined to trigger the specified callback 10124 * function. For channel-specific events, the function will return `true` only if all channels 10125 * have the listener defined. 10126 * 10127 * @param event {string|Symbol} The type of the event. 10128 * 10129 * @param listener {function} The callback function to check for. 10130 * 10131 * @param {object} [options={}] 10132 * 10133 * @param {number|number[]} [options.channels] An integer between 1 and 16 or an array of such 10134 * integers representing the MIDI channel(s) to check. This parameter is ignored for input-wide 10135 * events. 10136 * 10137 * @returns {boolean} Boolean value indicating whether or not the `Input` or 10138 * [`InputChannel`](InputChannel) already has this listener defined. 10139 */ 10140 hasListener(event, listener, options = {}) { 10141 if (wm.validation) { 10142 // Legacy compatibility 10143 if (typeof options === "function") { 10144 let channels = [].concat(listener); // clone 10145 listener = options; 10146 options = { 10147 channels: channels 10148 }; 10149 } 10150 } 10151 if (Enumerations.CHANNEL_EVENTS.includes(event)) { 10152 // If no channel defined, use all. 10153 if (options.channels === undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 10154 return Utilities.sanitizeChannels(options.channels).every(ch => { 10155 return this.channels[ch].hasListener(event, listener); 10156 }); 10157 } else { 10158 return super.hasListener(event, listener); 10159 } 10160 } 10161 10162 /** 10163 * Removes the specified event listener. If no listener is specified, all listeners matching the 10164 * specified event will be removed. If the event is channel-specific, the listener will be removed 10165 * from all [`InputChannel`]{@link InputChannel} objects belonging to that channel. If no event is 10166 * specified, all listeners for the `Input` as well as all listeners for all 10167 * [`InputChannel`]{@link InputChannel} objects belonging to the `Input` will be removed. 10168 * 10169 * By default, channel-specific listeners will be removed from all 10170 * [`InputChannel`]{@link InputChannel} objects unless the `options.channel` narrows it down. 10171 * 10172 * @param [type] {string} The type of the event. 10173 * 10174 * @param [listener] {function} The callback function to check for. 10175 * 10176 * @param {object} [options={}] 10177 * 10178 * @param {number|number[]} [options.channels] An integer between 1 and 16 or an array of 10179 * such integers representing the MIDI channel(s) to match. This parameter is ignored for 10180 * input-wide events. 10181 * 10182 * @param {*} [options.context] Only remove the listeners that have this exact context. 10183 * 10184 * @param {number} [options.remaining] Only remove the listener if it has exactly that many 10185 * remaining times to be executed. 10186 */ 10187 removeListener(event, listener, options = {}) { 10188 if (wm.validation) { 10189 // Legacy compatibility 10190 if (typeof options === "function") { 10191 let channels = [].concat(listener); // clone 10192 listener = options; 10193 options = { 10194 channels: channels 10195 }; 10196 } 10197 } 10198 if (options.channels === undefined) options.channels = Enumerations.MIDI_CHANNEL_NUMBERS; 10199 10200 // If the event is not specified, remove everything (channel-specific and input-wide)! 10201 if (event == undefined) { 10202 Utilities.sanitizeChannels(options.channels).forEach(ch => { 10203 if (this.channels[ch]) this.channels[ch].removeListener(); 10204 }); 10205 return super.removeListener(); 10206 } 10207 10208 // If the event is specified, check if it's channel-specific or input-wide. 10209 if (Enumerations.CHANNEL_EVENTS.includes(event)) { 10210 Utilities.sanitizeChannels(options.channels).forEach(ch => { 10211 this.channels[ch].removeListener(event, listener, options); 10212 }); 10213 } else { 10214 super.removeListener(event, listener, options); 10215 } 10216 } 10217 10218 /** 10219 * Adds a forwarder that will forward all incoming MIDI messages matching the criteria to the 10220 * specified [`Output`](Output) destination(s). This is akin to the hardware MIDI THRU port, with 10221 * the added benefit of being able to filter which data is forwarded. 10222 * 10223 * @param {Output|Output[]|Forwarder} output An [`Output`](Output) object, a 10224 * [`Forwarder`](Forwarder) object or an array of such objects, to forward messages to. 10225 * @param {object} [options={}] 10226 * @param {string|string[]} [options.types=(all messages)] A message type, or an array of such 10227 * types (`noteon`, `controlchange`, etc.), that the message type must match in order to be 10228 * forwarded. If this option is not specified, all types of messages will be forwarded. Valid 10229 * messages are the ones found in either 10230 * [`SYSTEM_MESSAGES`](Enumerations#SYSTEM_MESSAGES) or 10231 * [`CHANNEL_MESSAGES`](Enumerations#CHANNEL_MESSAGES). 10232 * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]] 10233 * A MIDI channel number or an array of channel numbers that the message must match in order to be 10234 * forwarded. By default all MIDI channels are included (`1` to `16`). 10235 * 10236 * @returns {Forwarder} The [`Forwarder`](Forwarder) object created to handle the forwarding. This 10237 * is useful if you wish to manipulate or remove the [`Forwarder`](Forwarder) later on. 10238 */ 10239 addForwarder(output, options = {}) { 10240 let forwarder; 10241 10242 // Unless 'output' is a forwarder, create a new forwarder 10243 if (output instanceof Forwarder) { 10244 forwarder = output; 10245 } else { 10246 forwarder = new Forwarder(output, options); 10247 } 10248 this._forwarders.push(forwarder); 10249 return forwarder; 10250 } 10251 10252 /** 10253 * Removes the specified [`Forwarder`](Forwarder) object from the input. 10254 * 10255 * @param {Forwarder} forwarder The [`Forwarder`](Forwarder) to remove (the 10256 * [`Forwarder`](Forwarder) object is returned when calling `addForwarder()`. 10257 */ 10258 removeForwarder(forwarder) { 10259 this._forwarders = this._forwarders.filter(item => item !== forwarder); 10260 } 10261 10262 /** 10263 * Checks whether the specified [`Forwarder`](Forwarder) object has already been attached to this 10264 * input. 10265 * 10266 * @param {Forwarder} forwarder The [`Forwarder`](Forwarder) to check for (the 10267 * [`Forwarder`](Forwarder) object is returned when calling [`addForwarder()`](#addForwarder). 10268 * @returns {boolean} 10269 */ 10270 hasForwarder(forwarder) { 10271 return this._forwarders.includes(forwarder); 10272 } 10273 10274 /** 10275 * Name of the MIDI input. 10276 * 10277 * @type {string} 10278 * @readonly 10279 */ 10280 get name() { 10281 return this._midiInput.name; 10282 } 10283 10284 /** 10285 * ID string of the MIDI port. The ID is host-specific. Do not expect the same ID on different 10286 * platforms. For example, Google Chrome and the Jazz-Plugin report completely different IDs for 10287 * the same port. 10288 * 10289 * @type {string} 10290 * @readonly 10291 */ 10292 get id() { 10293 return this._midiInput.id; 10294 } 10295 10296 /** 10297 * Input port's connection state: `pending`, `open` or `closed`. 10298 * 10299 * @type {string} 10300 * @readonly 10301 */ 10302 get connection() { 10303 return this._midiInput.connection; 10304 } 10305 10306 /** 10307 * Name of the manufacturer of the device that makes this input port available. 10308 * 10309 * @type {string} 10310 * @readonly 10311 */ 10312 get manufacturer() { 10313 return this._midiInput.manufacturer; 10314 } 10315 10316 /** 10317 * An integer to offset the reported octave of incoming notes. By default, middle C (MIDI note 10318 * number 60) is placed on the 4th octave (C4). 10319 * 10320 * If, for example, `octaveOffset` is set to 2, MIDI note number 60 will be reported as C6. If 10321 * `octaveOffset` is set to -1, MIDI note number 60 will be reported as C3. 10322 * 10323 * Note that this value is combined with the global offset value defined in the 10324 * [`WebMidi.octaveOffset`](WebMidi#octaveOffset) property (if any). 10325 * 10326 * @type {number} 10327 * 10328 * @since 3.0 10329 */ 10330 get octaveOffset() { 10331 return this._octaveOffset; 10332 } 10333 set octaveOffset(value) { 10334 if (this.validation) { 10335 value = parseInt(value); 10336 if (isNaN(value)) throw new TypeError("The 'octaveOffset' property must be an integer."); 10337 } 10338 this._octaveOffset = value; 10339 } 10340 10341 /** 10342 * State of the input port: `connected` or `disconnected`. 10343 * 10344 * @type {string} 10345 * @readonly 10346 */ 10347 get state() { 10348 return this._midiInput.state; 10349 } 10350 10351 /** 10352 * The port type. In the case of the `Input` object, this is always: `input`. 10353 * 10354 * @type {string} 10355 * @readonly 10356 */ 10357 get type() { 10358 return this._midiInput.type; 10359 } 10360 10361 /** 10362 * @type {boolean} 10363 * @private 10364 * @deprecated since v3.0.0 (moved to 'InputChannel' class) 10365 */ 10366 get nrpnEventsEnabled() { 10367 if (wm.validation) { 10368 console.warn("The 'nrpnEventsEnabled' property has been moved to the 'InputChannel' class."); 10369 } 10370 return false; 10371 } 10372 } 10373 10374 // Events that do not have code below them must be placed outside the class definition (?!) 10375 10376 /** 10377 * Input-wide (system) event emitted when a **system exclusive** message has been received. 10378 * You should note that, to receive `sysex` events, you must call the 10379 * [`WebMidi.enable()`](WebMidi#enable
10379()) method with the `sysex` option set to `true`: 10380 * 10381 * ```js 10382 * WebMidi.enable({sysex: true}) 10383 * .then(() => console.log("WebMidi has been enabled with sysex support.")) 10384 * ``` 10385 * 10386 * @event Input#sysex 10387 * 10388 * @type {object} 10389 * 10390 * @property {Input} port The `Input` that triggered the event. 10391 * @property {Input} target The object that dispatched the event. 10392 * @property {Message} message A [`Message`](Message) object containing information about the 10393 * incoming MIDI message. 10394 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 10395 * milliseconds since the navigation start of the document). 10396 * @property {string} type `sysex` 10397 * 10398 */ 10399 10400 /** 10401 * Input-wide (system) event emitted when a **time code quarter frame** message has been 10402 * received. 10403 * 10404 * @event Input#timecode 10405 * 10406 * @type {object} 10407 * 10408 * @property {Input} port The `Input` that triggered the event. 10409 * @property {Input} target The object that dispatched the event. 10410 * @property {Message} message A [`Message`](Message) object containing information about the 10411 * incoming MIDI message. 10412 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 10413 * milliseconds since the navigation start of the document). 10414 * @property {string} type `timecode` 10415 * 10416 * @since 2.1 10417 */ 10418 10419 /** 10420 * Input-wide (system) event emitted when a **song position** message has been received. 10421 * 10422 * @event Input#songposition 10423 * 10424 * @type {object} 10425 * 10426 * @property {Input} port The `Input` that triggered the event. 10427 * @property {Input} target The object that dispatched the event. 10428 * @property {Message} message A [`Message`](Message) object containing information about the 10429 * incoming MIDI message. 10430 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 10431 * milliseconds since the navigation start of the document). 10432 * @property {string} type `songposition` 10433 * 10434 * @since 2.1 10435 */ 10436 10437 /** 10438 * Input-wide (system) event emitted when a **song select** message has been received. 10439 * 10440 * @event Input#songselect 10441 * 10442 * @type {object} 10443 * 10444 * @property {Input} port The `Input` that triggered the event. 10445 * @property {Input} target The object that dispatched the event. 10446 * @property {Message} message A [`Message`](Message) object containing information about the 10447 * incoming MIDI message. 10448 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 10449 * milliseconds since the navigation start of the document). 10450 * @property {string} value Song (or sequence) number to select (0-127) 10451 * @property {string} rawValue Song (or sequence) number to select (0-127) 10452 * 10453 * @since 2.1 10454 */ 10455 10456 /** 10457 * Input-wide (system) event emitted when a **tune request** message has been received. 10458 * 10459 * @event Input#tunerequest 10460 * 10461 * @type {object} 10462 * 10463 * @property {Input} port The `Input` that triggered the event. 10464 * @property {Input} target The object that dispatched the event. 10465 * @property {Message} message A [`Message`](Message) object containing information about the 10466 * incoming MIDI message. 10467 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 10468 * milliseconds since the navigation start of the document). 10469 * @property {string} type `tunerequest` 10470 * 10471 * @since 2.1 10472 */ 10473 10474 /** 10475 * Input-wide (system) event emitted when a **timing clock** message has been received. 10476 * 10477 * @event Input#clock 10478 * 10479 * @type {object} 10480 * 10481 * @property {Input} port The `Input` that triggered the event. 10482 * @property {Input} target The object that dispatched the event. 10483 * @property {Message} message A [`Message`](Message) object containing information about the 10484 * incoming MIDI message. 10485 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 10486 * milliseconds since the navigation start of the document). 10487 * @property {string} type `clock` 10488 * 10489 * @since 2.1 10490 */ 10491 10492 /** 10493 * Input-wide (system) event emitted when a **start** message has been received. 10494 * 10495 * @event Input#start 10496 * 10497 * @type {object} 10498 * 10499 * @property {Input} port The `Input` that triggered the event. 10500 * @property {Input} target The object that dispatched the event. 10501 * @property {Message}
10501 message A [`Message`](Message) object containing information about the 10502 * incoming MIDI message. 10503 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 10504 * milliseconds since the navigation start of the document). 10505 * @property {string} type `start` 10506 * 10507 * @since 2.1 10508 */ 10509 10510 /** 10511 * Input-wide (system) event emitted when a **continue** message has been received. 10512 * 10513 * @event Input#continue 10514 * 10515 * @type {object} 10516 * 10517 * @property {Input} port The `Input` that triggered the event. 10518 * @property {Input} target The object that dispatched the event. 10519 * @property {Message} message A [`Message`](Message) object containing information about the 10520 * incoming MIDI message. 10521 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 10522 * milliseconds since the navigation start of the document). 10523 * @property {string} type `continue` 10524 * 10525 * @since 2.1 10526 */ 10527 10528 /** 10529 * Input-wide (system) event emitted when a **stop** message has been received. 10530 * 10531 * @event Input#stop 10532 * 10533 * @type {object} 10534 * 10535 * @property {Input} port The `Input` that triggered the event. 10536 * @property {Input} target The object that dispatched the event. 10537 * @property {Message} message A [`Message`](Message) object containing information about the 10538 * incoming MIDI message. 10539 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 10540 * milliseconds since the navigation start of the document). 10541 * @property {string} type `stop` 10542 * 10543 * @since 2.1 10544 */ 10545 10546 /** 10547 * Input-wide (system) event emitted when an **active sensing** message has been received. 10548 * 10549 * @event Input#activesensing 10550 * 10551 * @type {object} 10552 * 10553 * @property {Input} port The `Input` that triggered the event. 10554 * @property {Input} target The object that dispatched the event. 10555 * @property {Message} message A [`Message`](Message) object containing information about the 10556 * incoming MIDI message. 10557 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 10558 * milliseconds since the navigation start of the document). 10559 * @property {string} type `activesensing` 10560 * 10561 * @since 2.1 10562 */ 10563 10564 /** 10565 * Input-wide (system) event emitted when a **reset** message has been received. 10566 * 10567 * @event Input#reset 10568 * 10569 * @type {object} 10570 * 10571 * @property {Input} port The `Input` that triggered the event. 10572 * @property {Input} target The object that dispatched the event. 10573 * @property {Message} message A [`Message`](Message) object containing information about the 10574 * incoming MIDI message. 10575 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 10576 * milliseconds since the navigation start of the document). 10577 * @property {string} type `reset` 10578 * 10579 * @since 2.1 10580 */ 10581 10582 /** 10583 * Input-wide (system) event emitted when an unknown MIDI message has been received. It could 10584 * be, for example, one of the undefined/reserved messages. 10585 * 10586 * @event Input#unknownmessage 10587 * 10588 * @type {Object} 10589 * 10590 * @property {Input} port The `Input` that triggered the event. 10591 * @property {Input} target The object that dispatched the event. 10592 * @property {Message} message A [`Message`](Message) object containing information about the 10593 * incoming MIDI message. 10594 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred (in 10595 * milliseconds since the navigation start of the document). 10596 * @property {string} type `unknownmessage` 10597 * 10598 * @since 2.1 10599 */ 10600 10601 /** 10602 * The `WebMidi` object makes it easier to work with the low-level Web MIDI API. Basically, it 10603 * simplifies sending outgoing MIDI messages and reacting to incoming MIDI messages. 10604 * 10605 * When using the WebMidi.js library, you should know that the `WebMidi` class has already been 10606 * instantiated. You cannot instantiate it yourself. If you use the **IIFE** version, you should 10607 * simply use the global object called `WebMidi`. If you use the **CJS** (CommonJS) or **ESM** (ES6
10608 * module) version, you get an already-instantiated object when you import the module. 10609 * 10610 * @fires WebMidi#connected 10611 * @fires WebMidi#disabled 10612 * @fires WebMidi#disconnected 10613 * @fires WebMidi#enabled 10614 * @fires WebMidi#error 10615 * @fires WebMidi#midiaccessgranted 10616 * @fires WebMidi#portschanged 10617 * 10618 * @extends EventEmitter 10619 * @license Apache-2.0 10620 */ 10621 class WebMidi extends EventEmitter { 10622 /** 10623 * The WebMidi class is a singleton and you cannot instantiate it directly. It has already been 10624 * instantiated for you. 10625 */ 10626 constructor() { 10627 super(); 10628 10629 /** 10630 * Object containing system-wide default values that can be changed to customize how the library 10631 * works. 10632 * 10633 * @type {object} 10634 * 10635 * @property {object} defaults.note - Default values relating to note 10636 * @property {number} defaults.note.attack - A number between 0 and 127 representing the 10637 * default attack velocity of notes. Initial value is 64. 10638 * @property {number} defaults.note.release - A number between 0 and 127 representing the 10639 * default release velocity of notes. Initial value is 64. 10640 * @property {number} defaults.note.duration - A number representing the default duration of 10641 * notes (in seconds). Initial value is Infinity. 10642 */ 10643 this.defaults = { 10644 note: { 10645 attack: Utilities.from7bitToFloat(64), 10646 release: Utilities.from7bitToFloat(64), 10647 duration: Infinity 10648 } 10649 }; 10650 10651 /** 10652 * The [`MIDIAccess`](https://developer.mozilla.org/en-US/docs/Web/API/MIDIAccess) 10653 * instance used to talk to the lower-level Web MIDI API. This should not be used directly 10654 * unless you know what you are doing. 10655 * 10656 * @type {MIDIAccess} 10657 * @readonly 10658 */ 10659 this.interface = null; 10660 10661 /** 10662 * Indicates whether argument validation and backwards-compatibility checks are performed 10663 * throughout the WebMidi.js library for object methods and property setters. 10664 * 10665 * This is an advanced setting that should be used carefully. Setting `validation` to `false` 10666 * improves performance but should only be done once the project has been thoroughly tested with 10667 * `validation` turned on. 10668 * 10669 * @type {boolean} 10670 */ 10671 this.validation = true; 10672 10673 /** 10674 * Array of all (Input) objects 10675 * @type {Input[]} 10676 * @private 10677 */ 10678 this._inputs = []; 10679 10680 /** 10681 * Array of disconnected [`Input`](Input) objects. This is used when input
10681s are plugged back in 10682 * to retain their previous state. 10683 * @type {Input[]} 10684 * @private 10685 */ 10686 this._disconnectedInputs = []; 10687 10688 /** 10689 * Array of all [`Output`](Output) objects 10690 * @type {Output[]} 10691 * @private 10692 */ 10693 this._outputs = []; 10694 10695 /** 10696 * Array of disconnected [`Output`](Output) objects. This is used when outputs are plugged back 10697 * in to retain their previous state. 10698 * @type {Output[]} 10699 * @private 10700 */ 10701 this._disconnectedOutputs = []; 10702 10703 /** 10704 * Array of statechange events to process. These events must be parsed synchronously so they do 10705 * not override each other. 10706 * 10707 * @type {string[]} 10708 * @private 10709 */ 10710 this._stateChangeQueue = []; 10711 10712 /** 10713 * @type {number} 10714 * @private 10715 */ 10716 this._octaveOffset = 0; 10717 } 10718 10719 /** 10720 * Checks if the Web MIDI API is available in the current environment and then tries to connect to 10721 * the host's MIDI subsystem. This is an asynchronous operation and it causes a security prompt to 10722 * be displayed to the user. 10723 * 10724 * To enable the use of MIDI system exclusive messages, the `sysex` option should be set to 10725 * `true`. However, under some environments (e.g. Jazz-Plugin), the `sysex` option is ignored 10726 * and system exclusive messages are always enabled. You can check the 10727 * [`sysexEnabled`](#sysexEnabled) property to confirm. 10728 * 10729 * To enable access to software synthesizers available on the host, you would set the `software` 10730 * option to `true`. However, this option is only there to future-proof the library as support for 10731 * software synths has not yet been implemented in any browser (as of September 2021). 10732 * 10733 * By the way, if you call the [`enable()`](#enable) method while WebMidi.js is already enabled, 10734 * the callback function will be executed (if any), the promise will resolve but the events 10735 * ([`"midiaccessgranted"`](#event:midiaccessgranted), [`"connected"`](#event:connected) and 10736 * [`"enabled"`](#event:enabled)) will not be fired. 10737 * 10738 * There are 3 ways to execute code after `WebMidi` has been enabled: 10739 * 10740 * - Pass a callback function in the `options` 10741 * - Listen to the [`"enabled"`](#event:enabled) event 10742 * - Wait for the promise to resolve 10743 * 10744 * In order, this is what happens towards the end of the enabling process: 10745 * 10746 * 1. [`"midiaccessgranted"`](#event:midiaccessgranted) event is triggered once the user has 10747 * granted access to use MIDI. 10748 * 2. [`"connected"`](#event:connected) events are triggered (for each available input and output) 10749 * 3. [`"enabled"`](#event:
vendor: 27,429 bytes, lines 10749-11447
10749enabled) event is triggered when WebMidi.js is fully ready 10750 * 4. specified callback (if any) is executed 10751 * 5. promise is resolved and fulfilled with the `WebMidi` object. 10752 * 10753 * **Important note**: starting with Chrome v77, a page using Web MIDI API must be hosted on a 10754 * secure origin (`https://`, `localhost` or `file:///`) and the user will always be prompted to 10755 * authorize the operation (no matter if the `sysex` option is `true` or not). 10756 * 10757 * ##### Example 10758 * ```js 10759 * // Enabling WebMidi and using the promise 10760 * WebMidi.enable().then(() => { 10761 * console.log("WebMidi.js has been enabled!"); 10762 * }) 10763 * ``` 10764 * 10765 * @param [options] {object} 10766 * 10767 * @param [options.callback] {function} A function to execute once the operation completes. This 10768 * function will receive an `Error` object if enabling the Web MIDI API failed. 10769 * 10770 * @param [options.sysex=false] {boolean} Whether to enable MIDI system exclusive messages or not. 10771 * 10772 * @param [options.validation=true] {boolean} Whether to enable library-wide validation of method 10773 * arguments and setter values. This is an advanced setting that should be used carefully. Setting 10774 * [`validation`](#validation) to `false` improves performance but should only be done once the 10775 * project has been thoroughly tested with [`validation`](#validation) turned on. 10776 * 10777 * @param [options.software=false] {boolean} Whether to request access to software synthesizers on 10778 * the host system. This is part of the spec but has not yet been implemented by most browsers as 10779 * of April 2020. 10780 * 10781 * @param [options.requestMIDIAccessFunction] {function} A custom function to use to return 10782 * the MIDIAccess object. This is useful if you want to use a polyfill for the Web MIDI API 10783 * or if you want to use a custom implementation of the Web MIDI API - probably for testing 10784 * purposes. 10785 * 10786 * @async 10787 * 10788 * @returns {Promise.<WebMidi>} The promise is fulfilled with the `WebMidi` object for 10789 * chainability 10790 * 10791 * @throws {Error} The Web MIDI API is not supported in your environment. 10792 * @throws {Error} Jazz-Plugin must be installed to use WebMIDIAPIShim. 10793 */ 10794 async enable(options = {}, legacy = false) { 10795 this.validation = options.validation !== false; 10796 if (this.validation) { 10797 // Backwards-compatibility. Previous syntax was: enable(callback, sysex) 10798 if (typeof options === "function") options = { 10799 callback: options, 10800 sysex: legacy 10801 }; 10802 if (legacy) options.sysex = true; 10803 } 10804 10805 // If already enabled, trigger callback and resolve promise but do not dispatch events. 10806 if (this.enabled) { 10807 if (typeof options.callback === "function") options.callback(); 10808 return Promise.resolve(); 10809 } 10810 10811 /** 10812 * Event emitted when an error occurs trying to enable `WebMidi` 10813 * 10814 * @event WebMidi#error 10815 * @type {object} 10816 * @property {DOMHighResTimeStamp} timestamp The moment when the event occurred (in 10817 * milliseconds since the navigation start of the document). 10818 * @property {WebMidi} target The object that triggered the event 10819 * @property {string} type `error` 10820 * @property {*} error Actual error that occurred 10821 */ 10822 const errorEvent = { 10823 timestamp: this.time, 10824 target: this, 10825 type: "error", 10826 error: undefined 10827 }; 10828 10829 /** 10830 * Event emitted once the MIDI interface has been successfully created (which implies user has 10831 * granted access to MIDI). 10832 * 10833 * @event WebMidi#midiaccessgranted 10834 * @type {object} 10835 * @property {DOMHighResTimeStamp} timestamp The moment when the event occurred (in milliseconds 10836 * since the navigation start of the document). 10837 * @property {WebMidi} target The object that triggered the event 10838 * @property {string} type `midiaccessgranted` 10839 */ 10840 const midiAccessGrantedEvent = { 10841 timestamp: this.time, 10842 target: this, 10843 type: "midiaccessgranted" 10844 }; 10845 10846 /** 10847 * Event emitted once `WebMidi` has been fully enabled 10848 * 10849 * @event WebMidi#enabled 10850 * @type {object} 10851 * @property {DOMHighResTimeStamp} timestamp The moment when the event occurred (in milliseconds 10852 * since the navigation start of the document). 10853 * @property {WebMidi} target The object that triggered the event 10854 * @property {string} type `"enabled"` 10855 */ 10856 const enabledEvent = { 10857 timestamp: this.time, 10858 target: this, 10859 type: "enabled" 10860 }; 10861 10862 // Request MIDI access (this is where the prompt will appear) 10863 try { 10864 if (typeof options.requestMIDIAccessFunction === "function") { 10865 this.interface = await options.requestMIDIAccessFunction({ 10866 sysex: options.sysex, 10867 software: options.software 10868 }); 10869 } else { 10870 this.interface = await navigator.requestMIDIAccess({ 10871 sysex: options.sysex, 10872 software: options.software 10873 }); 10874 } 10875 } catch (err) { 10876 errorEvent.error = err; 10877 this.emit("error", errorEvent); 10878 if (typeof options.callback === "function") options.callback(err); 10879 return Promise.reject(err); 10880 } 10881 10882 // Now that the Web MIDI API interface has been created, we trigger the 'midiaccessgranted' 10883 // event. This allows the developer an occasion to assign listeners on 'connected' events. 10884 this.emit("midiaccessgranted", midiAccessGrantedEvent); 10885 10886 // We setup the state change listener before creating the ports so that it properly catches the 10887 // the ports' `connected` events 10888 this.interface.onstatechange = this._onInterfaceStateChange.bind(this); 10889 10890 // Update inputs and outputs (this is where `Input` and `Output` objects are created). 10891 try { 10892 await this._updateInputsAndOutputs(); 10893 } catch (err) { 10894 errorEvent.error = err; 10895 this.emit("error", errorEvent); 10896 if (typeof options.callback === "function") options.callback(err); 10897 return Promise.reject(err); 10898 } 10899 10900 // If we make it here, the ports have been successfully created, so we trigger the 'enabled' 10901 // event. 10902 this.emit("enabled", enabledEvent); 10903 10904 // Execute the callback (if any) and resolve the promise with 'this' (for chainability) 10905 if (typeof options.callback === "function") options.callback(); 10906 return Promise.resolve(this); 10907 } 10908 10909 /** 10910 * Completely disables **WebMidi.js** by unlinking the MIDI subsystem's interface and closing all 10911 * [`Input`](Input) and [`Output`](Output) objects that may have been opened. This also means that 10912 * listeners added to [`Input`](Input) objects, [`Output`](Output) objects or to `WebMidi` itself 10913 * are also destroyed. 10914 * 10915 * @async 10916 * @returns {Promise<Array>} 10917 * 10918 * @throws {Error} The Web MIDI API is not supported by your environment. 10919 * 10920 * @since 2.0.0 10921 */ 10922 async disable() { 10923 // This needs to be done right away to prevent racing conditions in listeners while the inputs 10924 // are being destroyed. 10925 if (this.interface) this.interface.onstatechange = undefined; 10926 return this._destroyInputsAndOutputs().then(() => { 10927 if (navigator && typeof navigator.close === "function") navigator.close(); // jzz 10928 10929 this.interface = null; // also resets enabled, sysexEnabled 10930 10931 /** 10932 * Event emitted once `WebMidi` has been successfully disabled. 10933 * 10934 * @event WebMidi#disabled 10935 * @type {object} 10936 * @property {DOMHighResTimeStamp} timestamp The moment when the event occurred (in 10937 * milliseconds since the navigation start of the document). 10938 * @property {WebMidi} target The object that triggered the event 10939 * @property {string} type `"disabled"` 10940 */ 10941 let event = { 10942 timestamp: this.time, 10943 target: this, 10944 type: "disabled" 10945 }; 10946 10947 // Finally, trigger the 'disabled' event and then remove all listeners. 10948 this.emit("disabled", event); 10949 this.removeListener(); 10950 }); 10951 } 10952 /** 10953 * Returns the [`Input`](Input) object that matches the specified ID string or `false` if no 10954 * matching input is found. As per the Web MIDI API specification, IDs are strings (not integers). 10955 * 10956 * Please note that IDs change from one host to another. For example, Chrome does not use the same 10957 * kind of IDs as Jazz-Plugin. 10958 * 10959 * @param id {string} The ID string of the input. IDs can be viewed by looking at the 10960 * [`WebMidi.inputs`](WebMidi#inputs) array. Even though they sometimes look like integers, IDs 10961 * are strings. 10962 * @param [options] {object} 10963 * @param [options.disconnected] {boolean} Whether to retrieve a disconnected input 10964 * 10965 * @returns {Input} An [`Input`](Input) object matching the specified ID string or `undefined` 10966 * if no matching input can be found. 10967 * 10968 * @throws {Error} WebMidi is not enabled. 10969 * 10970 * @since 2.0.0 10971 */ 10972 getInputById(id, options = { 10973 disconnected: false 10974 }) { 10975 if (this.validation) { 10976 if (!this.enabled) throw new Error("WebMidi is not enabled."); 10977 if (!id) return; 10978 } 10979 if (options.disconnected) { 10980 for (let i = 0; i < this._disconnectedInputs.length; i++) { 10981 if (this._disconnectedInputs[i]._midiInput && this._disconnectedInputs[i].id === id.toString()) return this._disconnectedInputs[i]; 10982 } 10983 } else { 10984 for (let i = 0; i < this.inputs.length; i++) { 10985 if (this.inputs[i]._midiInput && this.inputs[i].id === id.toString()) return this.inputs[i]; 10986 } 10987 } 10988 } 10989 /** 10990 * Returns the first [`Input`](Input) object whose name **contains** the specified string. Note 10991 * that the port names change from one environment to another. For example, Chrome does not report 10992 * input names in the same way as the Jazz-Plugin does. 10993 * 10994 * @param name {string} The non-empty string to look for within the name of MIDI inputs (such as 10995 * those visible in the [inputs](WebMidi#inputs) array). 10996 * 10997 * @returns {Input} The [`Input`](Input) that was found or `undefined` if no input contained the 10998 * specified name. 10999 * @param [options] {object} 11000 * @param [options.disconnected] {boolean} Whether to retrieve a disconnected input 11001 * 11002 * @throws {Error} WebMidi is not enabled. 11003 * 11004 * @since 2.0.0 11005 */ 11006 getInputByName(name, options = { 11007 disconnected: false 11008 }) { 11009 if (this.validation) { 11010 if (!this.enabled) throw new Error("WebMidi is not enabled."); 11011 if (!name) return; 11012 name = name.toString(); 11013 } 11014 if (options.disconnected) { 11015 for (let i = 0; i < this._disconnectedInputs.length; i++) { 11016 if (~this._disconnectedInputs[i].name.indexOf(name)) return this._disconnectedInputs[i]; 11017 } 11018 } else { 11019 for (let i = 0; i < this.inputs.length; i++) { 11020 if (~this.inputs[i].name.indexOf(name)) return this.inputs[i]; 11021 } 11022 } 11023 } 11024 /** 11025 * Returns the first [`Output`](Output) object whose name **contains** the specified string. Note 11026 * that the port names change from one environment to another. For example, Chrome does not report 11027 * input names in the same way as the Jazz-Plugin does. 11028 * 11029 * @param name {string} The non-empty string to look for within the name of MIDI inputs (such as 11030 * those visible in the [`outputs`](#outputs) array). 11031 * @param [options] {object} 11032 * @param [options.disconnected] {boolean} Whether to retrieve a disconnected output 11033 * 11034 * @returns {Output} The [`Output`](Output) that was found or `undefined` if no output matched 11035 * the specified name. 11036 * 11037 * @throws {Error} WebMidi is not enabled. 11038 * 11039 * @since 2.0.0 11040 */ 11041 getOutputByName(name, options = { 11042 disconnected: false 11043 }) { 11044 if (this.validation) { 11045 if (!this.enabled) throw new Error("WebMidi is not enabled."); 11046 if (!name) return; 11047 name = name.toString(); 11048 } 11049 if (options.disconnected) { 11050 for (let i = 0; i < this._disconnectedOutputs.length; i++) { 11051 if (~this._disconnectedOutputs[i].name.indexOf(name)) return this._disconnectedOutputs[i]; 11052 } 11053 } else { 11054 for (let i = 0; i < this.outputs.length; i++) { 11055 if (~this.outputs[i].name.indexOf(name)) return this.outputs[i]; 11056 } 11057 } 11058 } 11059 /** 11060 * Returns the [`Output`](Output) object that matches the specified ID string or `false` if no 11061 * matching output is found. As per the Web MIDI API specification, IDs are strings (not 11062 * integers). 11063 * 11064 * Please note that IDs change from one host to another. For example, Chrome does not use the same 11065 * kind of IDs as Jazz-Plugin. 11066 * 11067 * @param id {string} The ID string of the port. IDs can be viewed by looking at the 11068 * [`WebMidi.outputs`](WebMidi#outputs) array. 11069 * @param [options] {object} 11070 * @param [options.disconnected] {boolean} Whether to retrieve a disconnected output 11071 * 11072 * @returns {Output} An [`Output`](Output) object matching the specified ID string. If no 11073 * matching output can be found, the method returns `undefined`. 11074 * 11075 * @throws {Error} WebMidi is not enabled. 11076 * 11077 * @since 2.0.0 11078 */ 11079 getOutputById(id, options = { 11080 disconnected: false 11081 }) { 11082 if (this.validation) { 11083 if (!this.enabled) throw new Error("WebMidi is not enabled."); 11084 if (!id) return; 11085 } 11086 if (options.disconnected) { 11087 for (let i = 0; i < this._disconnectedOutputs.length; i++) { 11088 if (this._disconnectedOutputs[i]._midiOutput && this._disconnectedOutputs[i].id === id.toString()) return this._disconnectedOutputs[i]; 11089 } 11090 } else { 11091 for (let i = 0; i < this.outputs.length; i++) { 11092 if (this.outputs[i]._midiOutput && this.outputs[i].id === id.toString()) return this.outputs[i]; 11093 } 11094 } 11095 } 11096 /** 11097 * @private 11098 * @deprecated since version 3.0.0, use Utilities.toNoteNumber() instead. 11099 */ 11100 noteNameToNumber(name) { 11101 if (this.validation) { 11102 console.warn("The noteNameToNumber() method is deprecated. Use " + "Utilities.toNoteNumber() instead."); 11103 } 11104 return Utilities.toNoteNumber(name, this.octaveOffset); 11105 } 11106 11107 /** 11108 * @private 11109 * @deprecated since 3.0.0, use Utilities.getNoteDetails() instead. 11110 */ 11111 getOctave(number) { 11112 if (this.validation) { 11113 console.warn("The getOctave()is deprecated. Use Utilities.getNoteDetails() instead"); 11114 number = parseInt(number); 11115 } 11116 if (!isNaN(number) && number >= 0 && number <= 127) { 11117 return Utilities.getNoteDetails(Utilities.offsetNumber(number, this.octaveOffset)).octave; 11118 } else { 11119 return false; 11120 } 11121 } 11122 11123 /** 11124 * @private 11125 * @deprecated since 3.0.0, use Utilities.sanitizeChannels() instead. 11126 */ 11127 sanitizeChannels(channel) { 11128 if (this.validation) { 11129 console.warn("The sanitizeChannels() method has been moved to the utilities class."); 11130 } 11131 return Utilities.sanitizeChannels(channel); 11132 } 11133 11134 /** 11135 * @private 11136 * @deprecated since version 3.0.0, use Utilities.sanitizeChannels() instead. 11137 */ 11138 toMIDIChannels(channel) { 11139 if (this.validation) { 11140 console.warn("The toMIDIChannels() method has been deprecated. Use Utilities.sanitizeChannels() instead."); 11141 } 11142 return Utilities.sanitizeChannels(channel); 11143 } 11144 11145 /** 11146 * @private 11147 * @deprecated since version 3.0.0, use Utilities.guessNoteNumber() instead. 11148 */ 11149 guessNoteNumber(input) { 11150 if (this.validation) { 11151 console.warn("The guessNoteNumber() method has been deprecated. Use Utilities.guessNoteNumber() instead."); 11152 } 11153 return Utilities.guessNoteNumber(input, this.octaveOffset); 11154 } 11155 11156 /** 11157 * @private 11158 * @deprecated since version 3.0.0, use Utilities.buildNoteArray() instead. 11159 */ 11160 getValidNoteArray(notes, options = {}) { 11161 if (this.validation) { 11162 console.warn("The getValidNoteArray() method has been moved to the Utilities.buildNoteArray()"); 11163 } 11164 return Utilities.buildNoteArray(notes, options); 11165 } 11166 11167 /** 11168 * @private 11169 * @deprecated since version 3.0.0, use Utilities.toTimestamp() instead. 11170 */ 11171 convertToTimestamp(time) { 11172 if (this.validation) { 11173 console.warn("The convertToTimestamp() method has been moved to Utilities.toTimestamp()."); 11174 } 11175 return Utilities.toTimestamp(time); 11176 } 11177 11178 /** 11179 * @return {Promise<void>} 11180 * @private 11181 */ 11182 async _destroyInputsAndOutputs() { 11183 let promises = []; 11184 this.inputs.forEach(input => promises.push(input.destroy())); 11185 this.outputs.forEach(output => promises.push(output.destroy())); 11186 return Promise.all(promises).then(() => { 11187 this._inputs = []; 11188 this._outputs = []; 11189 }); 11190 } 11191 11192 /** 11193 * @private 11194 */ 11195 _onInterfaceStateChange(e) { 11196 // If WebMidi is no longer enabled, silently ignore the event. Methods like getOutputById() 11197 // and getInputById() throw when WebMidi is disabled, and since this handler is triggered by a 11198 // browser event, client code cannot catch those errors â causing unrecoverable crashes (#546). 11199 if (!this.enabled) return; 11200 this._updateInputsAndOutputs(); 11201 11202 /** 11203 * Event emitted when an [`Input`](Input) or [`Output`](Output) port is connected or 11204 * disconnected. This event is typically fired whenever a MIDI device is plugged in or 11205 * unplugged. Please note that it may fire several times if a device possesses multiple inputs 11206 * and/or outputs (which is often the case). 11207 * 11208 * @event WebMidi#portschanged 11209 * @type {object} 11210 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred 11211 * (in milliseconds since the navigation start of the document). 11212 * @property {string} type `portschanged` 11213 * @property {WebMidi} target The object to which the listener was originally added (`WebMidi`) 11214 * @property {Input|Output} port The [`Input`](Input) or [`Output`](Output) object that 11215 * triggered the event. 11216 * 11217 * @since 3.0.2 11218 */ 11219 11220 /** 11221 * Event emitted when an [`Input`](Input) or [`Output`](Output) becomes available. This event is 11222 * typically fired whenever a MIDI device is plugged in. Please note that it may fire several 11223 * times if a device possesses multiple inputs and/or outputs (which is often the case). 11224 * 11225 * @event WebMidi#connected 11226 * @type {object} 11227 * @property {number} timestamp The moment (DOMHighResTimeStamp) when the event occurred 11228 * (in milliseconds since the navigation start of the document). 11229 * @property {string} type `connected` 11230 * @property {WebMidi} target The object to which the listener was originally added (`WebMidi`) 11231 * @property {Input|Output} port The [`Input`](Input) or [`Output`](Output) object that 11232 * triggered the event. 11233 */ 11234 11235 /** 11236 * Event emitted when an [`Input`](Input) or [`Output`](Output) becomes unavailable. This event 11237 * is typically fired whenever a MIDI device is unplugged. Please note that it may fire several 11238 * times if a device possesses multiple inputs and/or outputs (which is often the case). 11239 * 11240 * @event WebMidi#disconnected 11241 * @type {object} 11242 * @property {DOMHighResTimeStamp} timestamp The moment when the event occurred (in milliseconds 11243 * since the navigation start of the document). 11244 * @property {string} type `disconnected` 11245 * @property {WebMidi} target The object to which the listener was originally added (`WebMidi`) 11246 * @property {Input|Output} port The [`Input`](Input) or [`Output`](Output) object that 11247 * triggered the event. 11248 */ 11249 let event = { 11250 timestamp: e.timeStamp, 11251 type: e.port.state, 11252 target: this 11253 }; 11254 11255 // We check if "connection" is "open" because connected events are also triggered with 11256 // "connection=closed" 11257 if (e.port.state === "connected" && e.port.connection === "open") { 11258 if (e.port.type === "output") { 11259 event.port = this.getOutputById(e.port.id); 11260 } else if (e.port.type === "input") { 11261 event.port = this.getInputById(e.port.id); 11262 } 11263 11264 // Emit "connected" event 11265 this.emit(e.port.state, event); 11266 11267 // Make a shallow copy of the event so we can use it for the "portschanged" event 11268 const portsChangedEvent = Object.assign({}, event); 11269 portsChangedEvent.type = "portschanged"; 11270 this.emit(portsChangedEvent.type, portsChangedEvent); 11271 11272 // We check if "connection" is "pending" because we do not always get the "closed" event 11273 } else if (e.port.state === "disconnected" && e.port.connection === "pending") { 11274 if (e.port.type === "input") { 11275 event.port = this.getInputById(e.port.id, { 11276 disconnected: true 11277 }); 11278 } else if (e.port.type === "output") { 11279 event.port = this.getOutputById(e.port.id, { 11280 disconnected: true 11281 }); 11282 } 11283 11284 // Emit "disconnected" event 11285 this.emit(e.port.state, event); 11286 11287 // Make a shallow copy of the event so we can use it for the "portschanged" event 11288 const portsChangedEvent = Object.assign({}, event); 11289 portsChangedEvent.type = "portschanged"; 11290 this.emit(portsChangedEvent.type, portsChangedEvent); 11291 } 11292 } 11293 /** 11294 * @private 11295 */ 11296 async _updateInputsAndOutputs() { 11297 return Promise.all([this._updateInputs(), this._updateOutputs()]); 11298 } 11299 /** 11300 * @private 11301 */ 11302 async _updateInputs() { 11303 // We must check for the existence of this.interface because it might have been closed via 11304 // WebMidi.disable(). 11305 if (!this.interface) return; 11306 11307 // Check for items to remove from the existing array (because they are no longer being reported 11308 // by the MIDI back-end). 11309 for (let i = this._inputs.length - 1; i >= 0; i--) { 11310 const current = this._inputs[i]; 11311 const inputs = Array.from(this.interface.inputs.values()); 11312 if (!inputs.find(input => input === current._midiInput)) { 11313 // Instead of destroying removed inputs, we stash them in case they come back (which is the 11314 // case when the computer goes to sleep and is later brought back online). 11315 this._disconnectedInputs.push(current); 11316 this._inputs.splice(i, 1); 11317 } 11318 } 11319 11320 // Array to hold pending promises from trying to open all input ports 11321 let promises = []; 11322 11323 // Add new inputs (if not already present) 11324 this.interface.inputs.forEach(nInput => { 11325 // Check if the input is currently absent from the 'inputs' array. 11326 if (!this._inputs.find(input => input._midiInput === nInput)) { 11327 // If the input has previously been stashed away, reuse it. If not, create a new one. 11328 let input = this._disconnectedInputs.find(input => input._midiInput === nInput); 11329 if (!input) input = new Input(nInput); 11330 this._inputs.push(input); 11331 promises.push(input.open()); 11332 } 11333 }); 11334 11335 // Return a promise that resolves when all promises have resolved 11336 return Promise.all(promises); 11337 } 11338 /** 11339 * @private 11340 */ 11341 async _updateOutputs() { 11342 // We must check for the existence of this.interface because it might have been closed via 11343 // WebMidi.disable(). 11344 if (!this.interface) return; 11345 11346 // Check for items to remove from the existing array (because they are no longer being reported 11347 // by the MIDI back-end). 11348 for (let i = this._outputs.length - 1; i >= 0; i--) { 11349 const current = this._outputs[i]; 11350 const outputs = Array.from(this.interface.outputs.values()); 11351 if (!outputs.find(output => output === current._midiOutput)) { 11352 // Instead of destroying removed inputs, we stash them in case they come back (which is the 11353 // case when the computer goes to sleep and is later brought back online). 11354 this._disconnectedOutputs.push(current); 11355 this._outputs.splice(i, 1); 11356 } 11357 } 11358 11359 // Array to hold pending promises from trying to open all output ports 11360 let promises = []; 11361 11362 // Add new outputs (if not already present) 11363 this.interface.outputs.forEach(nOutput => { 11364 // Check if the output is currently absent from the 'outputs' array. 11365 if (!this._outputs.find(output => output._midiOutput === nOutput)) { 11366 // If the output has previously been stashed away, reuse it. If not, create a new one. 11367 let output = this._disconnectedOutputs.find(output => output._midiOutput === nOutput); 11368 if (!output) output = new Output(nOutput); 11369 this._outputs.push(output); 11370 promises.push(output.open()); 11371 } 11372 }); 11373 11374 // Return a promise that resolves when all sub-promises have resolved 11375 return Promise.all(promises); 11376 } 11377 // injectPluginMarkup(parent) { 11378 // 11379 // // Silently ignore on Node.js 11380 // if (Utilities.isNode) return; 11381 // 11382 // // Default to <body> if no parent is specified 11383 // if (!(parent instanceof Element) && !(parent instanceof HTMLDocument)) { 11384 // parent = document.body; 11385 // } 11386 // 11387 // // IE10 needs this: 11388 // // <meta http-equiv="X-UA-Compatible" content="requiresActiveX=true"/> 11389 // 11390 // // Create markup and add to parent 11391 // const obj = document.createElement("object"); 11392 // obj.classid = "CLSID:1ACE1618-1C7D-4561-AEE1-34842AA85E90"; // IE 11393 // if (!obj.isJazz) obj.type = "audio/x-jazz"; // Standards-compliant 11394 // obj.style.visibility = "hidden"; 11395 // obj.style.width = obj.style.height = "0px"; 11396 // parent.appendChild(obj); 11397 // 11398 // } 11399 11400 /** 11401 * Indicates whether access to the host's MIDI subsystem is active or not. 11402 * 11403 * @readonly 11404 * @type {boolean} 11405 */ 11406 get enabled() { 11407 return this.interface !== null; 11408 } 11409 11410 /** 11411 * An array of all currently available MIDI inputs. 11412 * 11413 * @readonly 11414 * @type {Input[]} 11415 */ 11416 get inputs() { 11417 return this._inputs; 11418 } 11419 11420 /** 11421 * @private 11422 * @deprecated 11423 */ 11424 get isNode() { 11425 if (this.validation) { 11426 console.warn("WebMidi.isNode has been deprecated. Use Utilities.isNode instead."); 11427 } 11428 return Utilities.isNode; 11429 } 11430 11431 /** 11432 * @private 11433 * @deprecated 11434 */ 11435 get isBrowser() { 11436 if (this.validation) { 11437 console.warn("WebMidi.isBrowser has been deprecated. Use Utilities.isBrowser instead."); 11438 } 11439 return Utilities.isBrowser; 11440 } 11441 11442 /** 11443 * An integer to offset the octave of notes received from external devices or sent to external 11444 * devices. 11445 * 11446 * When a MIDI message comes in on an input channel the reported note name will be offset. For 11447 * example, if the `octaveOffset` is set to `-1` and a [`"noteon"`](InputChannel#event
11447:noteon) 11448 * message with MIDI number 60 comes in, the note will be reported as C3 (instead of C4). 11449 * 11450 * By the same token, when [`OutputChannel.playNote()`](OutputChannel#playNote) is called, the 11451 * MIDI note number being sent will be offset. If `octaveOffset` is set to `-1`, the MIDI note 11452 * number sent will be 72 (instead of 60). 11453 * 11454 * @type {number} 11455 * 11456 * @since 2.1 11457 */ 11458 get octaveOffset() { 11459 return this._octaveOffset; 11460 } 11461 set octaveOffset(value) { 11462 if (this.validation) { 11463 value = parseInt(value); 11464 if (isNaN(value)) throw new TypeError("The 'octaveOffset' property must be an integer."); 11465 } 11466 this._octaveOffset = value; 11467 } 11468 11469 /** 11470 * An array of all currently available MIDI outputs as [`Output`](Output) objects. 11471 * 11472 * @readonly 11473 * @type {Output[]} 11474 */ 11475 get outputs() { 11476 return this._outputs; 11477 } 11478 11479 /** 11480 * Indicates whether the environment provides support for the Web MIDI API or not. 11481 * 11482 * **Note**: in environments that do not offer built-in MIDI support, this will report `true` if 11483 * the 11484 * [`navigator.requestMIDIAccess`](https://developer.mozilla.org/en-US/docs/Web/API/MIDIAccess) 11485 * function is available. For example, if you have installed WebMIDIAPIShim.js but no plugin, this 11486 * property will be `true` even though actual support might not be there. 11487 * 11488 * @readonly 11489 * @type {boolean} 11490 */ 11491 get supported() { 11492 return typeof navigator !== "undefined" && !!navigator.requestMIDIAccess; 11493 } 11494 11495 /** 11496 * Indicates whether MIDI system exclusive messages have been activated when WebMidi.js was 11497 * enabled via the [`enable()`](#enable) method. 11498 * 11499 * @readonly 11500 * @type boolean 11501 */ 11502 get sysexEnabled() { 11503 return !!(this.interface && this.interface.sysexEnabled); 11504 } 11505 11506 /** 11507 * The elapsed time, in milliseconds, since the time 11508 * [origin](https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp#The_time_origin). 11509 * Said simply, it is the number of milliseconds that passed since the page was loaded. Being a 11510 * floating-point number, it has sub-millisecond accuracy. According to the 11511 * [documentation](https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp), the 11512 * time should be accurate to 5 µs (microseconds). However, due to various constraints, the 11513 * browser might only be accurate to one millisecond. 11514 * 11515 * Note: `WebMidi.time` is simply an alias to `performance.now()`. 11516 * 11517 * @type {DOMHighResTimeStamp} 11518 * @readonly 11519 */ 11520 get time() { 11521 return performance.now(); 11522 } 11523 11524 /** 11525 * The version of the library as a [semver](https://semver.org/) string. 11526 * 11527 * @readonly 11528 * @type string 11529 */ 11530 get version() { 11531 return "3.1.16"; 11532 } 11533 11534 /** 11535 * The flavour of the library. Can be one of: 11536 * 11537 * * `esm`: ECMAScript Module 11538 * * `cjs`: CommonJS Module 11539 * * `iife`: Immediately-Invoked Function Expression 11540 * 11541 * @readonly 11542 * @type string 11543 * @since 3.0.25 11544 */ 11545 get flavour() { 11546 return "iife"; // will be replaced during bundling by the correct identifier 11547 } 11548 11549 /** 11550 * @private 11551 * @deprecated since 3.0.0. Use Enumerations.CHANNEL_EVENTS instead. 11552 */ 11553 get CHANNEL_EVENTS() { 11554 if (this.validation) { 11555 console.warn("The CHANNEL_EVENTS enum has been moved to Enumerations.CHANNEL_EVENTS."); 11556 } 11557 return Enumerations.CHANNEL_EVENTS; 11558 } 11559 11560 /** 11561 * @private 11562 * @deprecated since 3.0.0. Use Enumerations.SYSTEM_MESSAGES instead. 11563 */ 11564 get MIDI_SYSTEM_MESSAGES() { 11565 if (this.validation) { 11566 console.warn("The MIDI_SYSTEM_MESSAGES enum has been moved to " + "Enumerations.SYSTEM_MESSAGES."); 11567 } 11568 return Enumerations.SYSTEM_MESSAGES; 11569 } 11570 11571 /** 11572 * @private 11573 * @deprecated since 3.0.0. Use Enumerations.CHANNEL_MODE_MESSAGES instead 11574 */ 11575 get MIDI_CHANNEL_MODE_MESSAGES() { 11576 if (this.validation) { 11577 console.warn("The MIDI_CHANNEL_MODE_MESSAGES enum has been moved to " + "Enumerations.CHANNEL_MODE_MESSAGES."); 11578 } 11579 return Enumerations.CHANNEL_MODE_MESSAGES; 11580 } 11581 11582 /** 11583 * @private 11584 * @deprecated since 3.0.0. Use Enumerations.CONTROL_CHANGE_MESSAGES instead. 11585 */ 11586 get MIDI_CONTROL_CHANGE_MESSAGES() { 11587 if (this.validation) { 11588 console.warn("The MIDI_CONTROL_CHANGE_MESSAGES enum has been replaced by the " + "Enumerations.CONTROL_CHANGE_MESSAGES array."); 11589 } 11590 return Enumerations.MIDI_CONTROL_CHANGE_MESSAGES; 11591 } 11592 11593 /** 11594 * @deprecated since 3.0.0. Use Enumerations.REGISTERED_PARAMETERS instead. 11595 * @private 11596 */ 11597 get MIDI_REGISTERED_PARAMETER() { 11598 if (this.validation) { 11599 console.warn("The MIDI_REGISTERED_PARAMETER enum has been moved to " + "Enumerations.REGISTERED_PARAMETERS."); 11600 } 11601 return Enumerations.REGISTERED_PARAMETERS; 11602 } 11603 11604 /** 11605 * @deprecated since 3.0.0. 11606 * @private 11607 */ 11608 get NOTES() { 11609 if (this.validation) { 11610 console.warn("The NOTES enum has been deprecated."); 11611 } 11612 return ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; 11613 } 11614 } 11615 11616 // Export singleton instance of WebMidi class. The 'constructor' is nulled so that it cannot be used 11617 // to instantiate a new WebMidi object or extend it. However, it is not freezed so it remains 11618 // extensible (properties can be added at will).
11619 const wm = new WebMidi(); 11620 wm.constructor = null; 11621 11622 exports.Enumerations = Enumerations; 11623 exports.Forwarder = Forwarder; 11624 exports.Input = Input; 11625 exports.InputChannel = InputChannel; 11626 exports.Message = Message; 11627 exports.Note = Note; 11628 exports.Output = Output; 11629 exports.OutputChannel = OutputChannel; 11630 exports.Utilities = Utilities; 11631 exports.WebMidi = wm; 11632 11633}(this.window = this.window || {}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.