PageSourceSearch

https://tulip.computer/run/webmidi.iife.js

js tulip.computer collected 2026-10-07 11:30:46 UTC 516,444 bytes, 11,633 lines download raw bytes

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.