| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`event`**] | string Symbol |(any event)|The event to check|
|[**`callback`**] | function Listener |(any callback)|The actual function that was added to the event or the [Listener](Listener) object returned by `addListener()`.|
**Return Value**
> Returns: `boolean``
The promise is fulfilled with the `Output` object.
### `.playNote(...)` {#playNote}
Plays a note or an array of notes on one or more channels of this output. If you intend to play
notes on a single channel, you should probably use
[`OutputChannel.playNote()`](OutputChannel#playNote) instead.
The first parameter is the note to play. It can be a single value or an array of the following
valid values:
- A MIDI note number (integer between `0` and `127`)
- A note identifier (e.g. `"C3"`, `"G#4"`, `"F-1"`, `"Db7"`)
- A [`Note`](Note) object
The `playNote()` method sends a **note on** MIDI message for all specified notes on all
specified channels. If no channel is specified, it will send to all channels. If a `duration`
is set in the `options` parameter or in the [`Note`](Note) object's
[`duration`](Note#duration) property, it will also schedule a **note off** message to end
the note after said duration. If no `duration` is set, the note will simply play until a
matching **note off** message is sent with [`stopNote()`](#stopNote).
The execution of the **note on** command can be delayed by using the `time` property of the
`options` parameter.
When using [`Note`](Note) objects, the durations and velocities defined in the
[`Note`](Note) objects have precedence over the ones specified via the method's `options`
parameter.
**Note**: As per the MIDI standard, a **note on** message with an attack velocity of `0` is
functionally equivalent to a **note off** message.
**Parameters**
> Signature: `playNote(note, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`note`** | number string Note Array.<number> Array.<string> Array.<Note> ||The note(s) to play. The notes can be specified by using a MIDI note number (0-127), a note identifier (e.g. C3, G#4, F-1, Db7), a [`Note`](Note) object or an array of the previous types. When using a note identifier, octave range must be between -1 and 9. The lowest note is C-1 (MIDI note number `0`) and the highest note is G9 (MIDI note number `127`).|
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.duration`**] | number ||The number of milliseconds after which a **note off** message will be scheduled. If left undefined, only a **note on** message is sent.|
|[**`options.attack`**] | number |0.5|The velocity at which to play the note (between `0` and `1`). If the `rawAttack` option is also defined, it will have priority. An invalid velocity value will silently trigger the default of `0.5`.|
|[**`options.rawAttack`**] | number |64|The attack velocity at which to play the note (between `0` and `127`). This has priority over the `attack` property. An invalid velocity value will silently trigger the default of 64.|
|[**`options.release`**] | number |0.5|The velocity at which to release the note (between `0` and `1`). If the `rawRelease` option is also defined, it will have priority. An invalid velocity value will silently trigger the default of `0.5`. This is only used with the **note off** event triggered when `options.duration` is set.|
|[**`options.rawRelease`**] | number |64|The velocity at which to release the note (between `0` and `127`). This has priority over the `release` property. An invalid velocity value will silently trigger the default of 64. This is only used with the **note off** event triggered when `options.duration` is set.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.removeListener(...)` {#removeListener}
Removes all the listeners that were added to the object upon which the method is called and
that match the specified criterias. If no parameters are passed, all listeners added to this
object will be removed. If only the `event` parameter is passed, all listeners for that event
will be removed from that object. You can remove global listeners by using
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) as the first parameter.
To use more granular options, you must at least define the `event`. Then, you can specify the
callback to match or one or more of the additional options.
**Parameters**
> Signature: `removeListener([event], [callback], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`event`**] | string ||The event name.|
|[**`callback`**] | EventEmitter~callback ||Only remove the listeners that match this exact callback function.|
|[**`options`**] | Object |||
|[**`options.context`**] | * ||Only remove the listeners that have this exact context.|
|[**`options.remaining`**] | number ||Only remove the listener if it has exactly that many remaining times to be executed.|
### `.send(...)` {#send}
Sends a MIDI message on the MIDI output port. If no time is specified, the message will be
sent immediately. The message should be an array of 8 bit unsigned integers (0-225), a
[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array)
object or a [`Message`](Message) object.
It is usually not necessary to use this method directly as you can use one of the simpler
helper methods such as [`playNote()`](#playNote), [`stopNote()`](#stopNote),
[`sendControlChange()`](#sendControlChange), etc.
Details on the format of MIDI messages are available in the summary of
[MIDI messages](https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message)
from the MIDI Manufacturers Association.
**Parameters**
> Signature: `send(message, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`message`** | Array.<number> Uint8Array Message ||An array of 8bit unsigned integers, a `Uint8Array` object (not available in Node.js) containing the message bytes or a `Message` object.|
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
**Throws**:
* `RangeError` : The first byte (status) must be an integer between 128 and 255.
### `.sendActiveSensing(...)` {#sendActiveSensing}
Sends an **active sensing** real-time message. This tells the device connected to this port
that the connection is still good. Active sensing messages are often sent every 300 ms if there
was no other activity on the MIDI port.
**Parameters**
> Signature: `sendActiveSensing([options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendAllNotesOff(...)` {#sendAllNotesOff}
**Since**: 3.0.0
Sends an **all notes off** channel mode message. This will make all currently playing notes
fade out just as if their key had been released. This is different from the
[`sendAllSoundOff()`](#sendAllSoundOff) method which mutes all sounds immediately.
**Parameters**
> Signature: `sendAllNotesOff([options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
### `.sendAllSoundOff(...)` {#sendAllSoundOff}
**Since**: 3.0.0
Sends an **all sound off** channel mode message. This will silence all sounds playing on that
channel but will not prevent new sounds from being triggered.
**Parameters**
> Signature: `sendAllSoundOff([options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
### `.sendChannelAftertouch(...)` {#sendChannelAftertouch}
**Since**: 3.0.0
Sends a MIDI **channel aftertouch** message to the specified channel(s). For key-specific
aftertouch, you should instead use [`setKeyAftertouch()`](#setKeyAftertouch).
**Parameters**
> Signature: `sendChannelAftertouch([pressure], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`pressure`**] | number |0.5|The pressure level (between `0` and `1`). An invalid pressure value will silently trigger the default behaviour. If the `rawValue` option is set to `true`, the pressure can be defined by using an integer between `0` and `127`.|
|[**`options`**] | object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.rawValue`**] | boolean |false|A boolean indicating whether the value should be considered a float between `0` and `1.0` (default) or a raw integer between `0` and `127`.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendChannelMode(...)` {#sendChannelMode}
Sends a MIDI **channel mode** message to the specified channel(s). The channel mode message to
send can be specified numerically or by using one of the following common names:
| Type |Number| Shortcut Method |
| ---------------------|------|-------------------------------------------------------------- |
| `allsoundoff` | 120 | [`sendAllSoundOff()`](#sendAllSoundOff) |
| `resetallcontrollers`| 121 | [`sendResetAllControllers()`](#sendResetAllControllers) |
| `localcontrol` | 122 | [`sendLocalControl()`](#sendLocalControl) |
| `allnotesoff` | 123 | [`sendAllNotesOff()`](#sendAllNotesOff) |
| `omnimodeoff` | 124 | [`sendOmniMode(false)`](#sendOmniMode) |
| `omnimodeon` | 125 | [`sendOmniMode(true)`](#sendOmniMode) |
| `monomodeon` | 126 | [`sendPolyphonicMode("mono")`](#sendPolyphonicMode) |
| `polymodeon` | 127 | [`sendPolyphonicMode("poly")`](#sendPolyphonicMode) |
Note: as you can see above, to make it easier, all channel mode messages also have a matching
helper method.
It should also be noted that, per the MIDI specification, only `localcontrol` and `monomodeon`
may require a value that's not zero. For that reason, the `value` parameter is optional and
defaults to 0.
**Parameters**
> Signature: `sendChannelMode(command, [value], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`command`** | number string ||The numerical identifier of the channel mode message (integer between 120-127) or its name as a string.|
|[**`value`**] | number |0|The value to send (integer between 0-127).|
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
**Throws**:
* `TypeError` : Invalid channel mode message name.
* `RangeError` : Channel mode controller numbers must be between 120 and 127.
* `RangeError` : Value must be an integer between 0 and 127.
### `.sendClock(...)` {#sendClock}
Sends a MIDI **clock** real-time message. According to the standard, there are 24 MIDI clocks
for every quarter note.
**Parameters**
> Signature: `sendClock([options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendContinue(...)` {#sendContinue}
Sends a **continue** real-time message. This resumes song playback where it was previously
stopped or where it was last cued with a song position message. To start playback from the
start, use the [`sendStart()`](#Output+sendStart)` method.
**Parameters**
> Signature: `sendContinue([options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendControlChange(...)` {#sendControlChange}
Sends a MIDI **control change** message to the specified channel(s) at the scheduled time. The
control change message to send can be specified numerically (0-127) or by using one of the
following common names:
| Number | Name |
|--------|-------------------------------|
| 0 |`bankselectcoarse` |
| 1 |`modulationwheelcoarse` |
| 2 |`breathcontrollercoarse` |
| 4 |`footcontrollercoarse` |
| 5 |`portamentotimecoarse` |
| 6 |`dataentrycoarse` |
| 7 |`volumecoarse` |
| 8 |`balancecoarse` |
| 10 |`pancoarse` |
| 11 |`expressioncoarse` |
| 12 |`effectcontrol1coarse` |
| 13 |`effectcontrol2coarse` |
| 18 |`generalpurposeslider3` |
| 19 |`generalpurposeslider4` |
| 32 |`bankselectfine` |
| 33 |`modulationwheelfine` |
| 34 |`breathcontrollerfine` |
| 36 |`footcontrollerfine` |
| 37 |`portamentotimefine` |
| 38 |`dataentryfine` |
| 39 |`volumefine` |
| 40 |`balancefine` |
| 42 |`panfine` |
| 43 |`expressionfine` |
| 44 |`effectcontrol1fine` |
| 45 |`effectcontrol2fine` |
| 64 |`holdpedal` |
| 65 |`portamento` |
| 66 |`sustenutopedal` |
| 67 |`softpedal` |
| 68 |`legatopedal` |
| 69 |`hold2pedal` |
| 70 |`soundvariation` |
| 71 |`resonance` |
| 72 |`soundreleasetime` |
| 73 |`soundattacktime` |
| 74 |`brightness` |
| 75 |`soundcontrol6` |
| 76 |`soundcontrol7` |
| 77 |`soundcontrol8` |
| 78 |`soundcontrol9` |
| 79 |`soundcontrol10` |
| 80 |`generalpurposebutton1` |
| 81 |`generalpurposebutton2` |
| 82 |`generalpurposebutton3` |
| 83 |`generalpurposebutton4` |
| 91 |`reverblevel` |
| 92 |`tremololevel` |
| 93 |`choruslevel` |
| 94 |`celestelevel` |
| 95 |`phaserlevel` |
| 96 |`dataincrement` |
| 97 |`datadecrement` |
| 98 |`nonregisteredparametercoarse` |
| 99 |`nonregisteredparameterfine` |
| 100 |`registeredparametercoarse` |
| 101 |`registeredparameterfine` |
| 120 |`allsoundoff` |
| 121 |`resetallcontrollers` |
| 122 |`localcontrol` |
| 123 |`allnotesoff` |
| 124 |`omnimodeoff` |
| 125 |`omnimodeon` |
| 126 |`monomodeon` |
| 127 |`polymodeon` |
Note: as you can see above, not all control change message have a matching name. This does not
mean you cannot use the others. It simply means you will need to use their number (`0` - `127`)
instead of their name. While you can still use them, numbers `120` to `127` are usually
reserved for *channel mode* messages. See [`sendChannelMode()`](#sendChannelMode) method
for more info.
To view a list of all available **control change** messages, please consult [Table 3 - Control
Change Messages](https://www.midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2)
from the MIDI specification.
**Parameters**
> Signature: `sendControlChange(controller, [value], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`controller`** | number string ||The MIDI controller name or number (0-127).|
|[**`value`**] | number |0|The value to send (0-127).|
|[**`options`**] | object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
**Throws**:
* `RangeError` : Controller numbers must be between 0 and 127.
* `RangeError` : Invalid controller name.
### `.sendKeyAftertouch(...)` {#sendKeyAftertouch}
**Since**: 3.0.0
Sends a MIDI **key aftertouch** message to the specified channel(s) at the scheduled time. This
is a key-specific aftertouch. For a channel-wide aftertouch message, use
[`setChannelAftertouch()`](#setChannelAftertouch).
**Parameters**
> Signature: `sendKeyAftertouch(note, [pressure], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`note`** | number Note string Array.<number> Array.<Note> Array.<string> ||The note(s) for which you are sending an aftertouch value. The notes can be specified by using a MIDI note number (`0` - `127`), a [`Note`](Note) object, a note identifier (e.g. `C3`, `G#4`, `F-1`, `Db7`) or an array of the previous types. When using a note identifier, octave range must be between `-1` and `9`. The lowest note is `C-1` (MIDI note number `0`) and the highest note is `G9` (MIDI note number `127`).|
|[**`pressure`**] | number |0.5|The pressure level (between 0 and 1). An invalid pressure value will silently trigger the default behaviour. If the `rawValue` option is set to `true`, the pressure can be defined by using an integer between 0 and 127.|
|[**`options`**] | object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.rawValue`**] | boolean |false|A boolean indicating whether the value should be considered a float between `0` and `1.0` (default) or a raw integer between `0` and `127`.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendLocalControl(...)` {#sendLocalControl}
**Since**: 3.0.0
Turns local control on or off. Local control is usually enabled by default. If you disable it,
the instrument will no longer trigger its own sounds. It will only send the MIDI messages to
its out port.
**Parameters**
> Signature: `sendLocalControl([state], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`state`**] | boolean |false|Whether to activate local control (`true`) or disable it (`false`).|
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendMasterTuning(...)` {#sendMasterTuning}
**Since**: 3.0.0
Sends a master tuning message to the specified channel(s). The value is decimal and must be
larger than `-65` semitones and smaller than `64` semitones.
Because of the way the MIDI specification works, the decimal portion of the value will be
encoded with a resolution of 14bit. The integer portion must be between -64 and 63
inclusively. This function actually generates two MIDI messages: a **Master Coarse Tuning** and
a **Master Fine Tuning** RPN messages.
**Parameters**
> Signature: `sendMasterTuning([value], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`value`**] | number |0.0|The desired decimal adjustment value in semitones (-65 < x < 64)|
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
**Throws**:
* `RangeError` : The value must be a decimal number between larger than -65 and smaller
than 64.
### `.sendModulationRange(...)` {#sendModulationRange}
**Since**: 3.0.0
Sends a **modulation depth range** message to the specified channel(s) so that they adjust the
depth of their modulation wheel's range. The range can be specified with the `semitones`
parameter, the `cents` parameter or by specifying both parameters at the same time.
**Parameters**
> Signature: `sendModulationRange([semitones], [cents], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`semitones`**] | number |0|The desired adjustment value in semitones (integer between 0 and 127).|
|[**`cents`**] | number |0|The desired adjustment value in cents (integer between 0 and 127).|
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
**Throws**:
* `RangeError` : The msb value must be between 0 and 127
* `RangeError` : The lsb value must be between 0 and 127
### `.sendNoteOff(...)` {#sendNoteOff}
Sends a **note off** message for the specified MIDI note number on the specified channel(s).
The first parameter is the note to stop. It can be a single value or an array of the following
valid values:
- A MIDI note number (integer between `0` and `127`)
- A note identifier (e.g. `"C3"`, `"G#4"`, `"F-1"`, `"Db7"`)
- A [`Note`](Note) object
The execution of the **note off** command can be delayed by using the `time` property of the
`options` parameter.
**Parameters**
> Signature: `sendNoteOff(note, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`note`** | number Note string Array.<number> Array.<Note> Array.<string> ||The note(s) to stop. The notes can be specified by using a MIDI note number (`0` - `127`), a note identifier (e.g. `C3`, `G#4`, `F-1`, `Db7`) or an array of the previous types. When using a note identifier, octave range must be between `-1` and `9`. The lowest note is `C-1` (MIDI note number `0`) and the highest note is `G9` (MIDI note number `127`).|
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.release`**] | number |0.5|The velocity at which to release the note (between `0` and `1`). If the `rawRelease` option is also defined, `rawRelease` will have priority. An invalid velocity value will silently trigger the default of `0.5`.|
|[**`options.rawRelease`**] | number |64|The velocity at which to release the note (between `0` and `127`). If the `release` option is also defined, `rawRelease` will have priority. An invalid velocity value will silently trigger the default of `64`.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendNoteOn(...)` {#sendNoteOn}
Sends a **note on** message for the specified MIDI note number on the specified channel(s). The
first parameter is the number. It can be a single value or an array of the following valid
values:
- A MIDI note number (integer between `0` and `127`)
- A note identifier (e.g. `"C3"`, `"G#4"`, `"F-1"`, `"Db7"`)
- A [`Note`](Note) object
The execution of the **note on** command can be delayed by using the `time` property of the
`options` parameter.
**Note**: As per the MIDI standard, a **note on** message with an attack velocity of `0` is
functionally equivalent to a **note off** message.
**Parameters**
> Signature: `sendNoteOn(note, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`note`** | number Note string Array.<number> Array.<Note> Array.<string> ||The note(s) to stop. The notes can be specified by using a MIDI note number (`0` - `127`), a note identifier (e.g. `C3`, `G#4`, `F-1`, `Db7`) or an array of the previous types. When using a note identifier, octave range must be between `-1` and `9`. The lowest note is `C-1` (MIDI note number `0`) and the highest note is `G9` (MIDI note number `127`).|
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.attack`**] | number |0.5|The velocity at which to play the note (between `0` and `1`). If the `rawAttack` option is also defined, `rawAttack` will have priority. An invalid velocity value will silently trigger the default of `0.5`.|
|[**`options.rawAttack`**] | number |64|The velocity at which to release the note (between `0` and `127`). If the `attack` option is also defined, `rawAttack` will have priority. An invalid velocity value will silently trigger the default of `64`.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendNrpnValue(...)` {#sendNrpnValue}
Sets a non-registered parameter to the specified value. The NRPN is selected by passing a
two-position array specifying the values of the two control bytes. The value is specified by
passing a single integer (most cases) or an array of two integers.
NRPNs are not standardized in any way. Each manufacturer is free to implement them any way
they see fit. For example, according to the Roland GS specification, you can control the
**vibrato rate** using NRPN (`1`, `8`). Therefore, to set the **vibrato rate** value to `123`
you would use:
```js
WebMidi.outputs[0].sendNrpnValue([1, 8], 123);
```
You probably want to should select a channel so the message is not sent to all channels. For
instance, to send to channel `1` of the first output port, you would use:
```js
WebMidi.outputs[0].sendNrpnValue([1, 8], 123, 1);
```
In some rarer cases, you need to send two values with your NRPN messages. In such cases, you
would use a 2-position array. For example, for its **ClockBPM** parameter (`2`, `63`), Novation
uses a 14-bit value that combines an MSB and an LSB (7-bit values). So, for example, if the
value to send was `10`, you could use:
```js
WebMidi.outputs[0].sendNrpnValue([2, 63], [0, 10], 1);
```
For further implementation details, refer to the manufacturer's documentation.
**Parameters**
> Signature: `sendNrpnValue(parameter, [data], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`parameter`** | Array.<number> ||A two-position array specifying the two control bytes (`0x63`, `0x62`) that identify the non-registered parameter.|
|[**`data`**] | number Array.<number> |[]|An integer or an array of integers with a length of 1 or 2 specifying the desired data.|
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
**Throws**:
* `RangeError` : The control value must be between 0 and 127.
* `RangeError` : The msb value must be between 0 and 127
### `.sendOmniMode(...)` {#sendOmniMode}
**Since**: 3.0.0
Sets OMNI mode to **on** or **off** for the specified channel(s). MIDI's OMNI mode causes the
instrument to respond to messages from all channels.
It should be noted that support for OMNI mode is not as common as it used to be.
**Parameters**
> Signature: `sendOmniMode([state], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`state`**] | boolean ||Whether to activate OMNI mode (`true`) or not (`false`).|
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
**Throws**:
* `TypeError` : Invalid channel mode message name.
* `RangeError` : Channel mode controller numbers must be between 120 and 127.
* `RangeError` : Value must be an integer between 0 and 127.
### `.sendPitchBend(...)` {#sendPitchBend}
**Since**: 3.0.0
Sends a MIDI **pitch bend** message to the specified channel(s) at the scheduled time.
The resulting bend is relative to the pitch bend range that has been defined. The range can be
set with [`sendPitchBendRange()`](#sendPitchBendRange). So, for example, if the pitch
bend range has been set to 12 semitones, using a bend value of `-1` will bend the note 1 octave
below its nominal value.
**Parameters**
> Signature: `sendPitchBend(value, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`value`** | number Array.<number> ||The intensity of the bend (between `-1.0` and `1.0`). A value of `0` means no bend. If an invalid value is specified, the nearest valid value will be used instead. If the `rawValue` option is set to `true`, the intensity of the bend can be defined by either using a single integer between `0` and `127` (MSB) or an array of two integers between `0` and `127` representing, respectively, the MSB (most significant byte) and the LSB (least significant byte). The MSB is expressed in semitones with `64` meaning no bend. A value lower than `64` bends downwards while a value higher than `64` bends upwards. The LSB is expressed in cents (1/100 of a semitone). An LSB of `64` also means no bend.|
|[**`options`**] | object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.rawValue`**] | boolean |false|A boolean indicating whether the value should be considered as a float between `-1.0` and `1.0` (default) or as raw integer between `0` and 127` (or an array of 2 integers if using both MSB and LSB).|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendPitchBendRange(...)` {#sendPitchBendRange}
**Since**: 3.0.0
Sends a **pitch bend range** message to the specified channel(s) at the scheduled time so that
they adjust the range used by their pitch bend lever. The range is specified by using the
`semitones` and `cents` parameters. For example, setting the `semitones` parameter to `12`
means that the pitch bend range will be 12 semitones above and below the nominal pitch.
**Parameters**
> Signature: `sendPitchBendRange([semitones], [cents], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`semitones`**] | number |0|The desired adjustment value in semitones (between `0` and `127`). While nothing imposes that in the specification, it is very common for manufacturers to limit the range to 2 octaves (-12 semitones to 12 semitones).|
|[**`cents`**] | number |0|The desired adjustment value in cents (integer between `0` and `127`).|
|[**`options`**] | object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
**Throws**:
* `RangeError` : The msb value must be between 0 and 127.
* `RangeError` : The lsb value must be between 0 and 127.
### `.sendPolyphonicMode(...)` {#sendPolyphonicMode}
**Since**: 3.0.0
Sets the polyphonic mode. In `poly` mode (usually the default), multiple notes can be played
and heard at the same time. In `mono` mode, only one note will be heard at once even if
multiple notes are being played.
**Parameters**
> Signature: `sendPolyphonicMode(mode, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`mode`** | string ||The mode to use: `mono` or `poly`.|
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendProgramChange(...)` {#sendProgramChange}
**Since**: 3.0.0
Sends a MIDI **program change** message to the specified channel(s) at the scheduled time.
**Parameters**
> Signature: `sendProgramChange([program], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`program`**] | number |0|The MIDI patch (program) number (integer between `0` and `127`).|
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
**Throws**:
* `TypeError` : Failed to execute 'send' on 'MIDIOutput': The value at index 1 is greater
than 0xFF.
### `.sendReset(...)` {#sendReset}
Sends a **reset** real-time message. This tells the device connected to this output that it
should reset itself to a default state.
**Parameters**
> Signature: `sendReset([options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendResetAllControllers(...)` {#sendResetAllControllers}
Sends a **reset all controllers** channel mode message. This resets all controllers, such as
the pitch bend, to their default value.
**Parameters**
> Signature: `sendResetAllControllers([options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
### `.sendRpnDecrement(...)` {#sendRpnDecrement}
Decrements the specified MIDI registered parameter by 1. Here is the full list of parameter
names that can be used with this method:
* Pitchbend Range (0x00, 0x00): `"pitchbendrange"`
* Channel Fine Tuning (0x00, 0x01): `"channelfinetuning"`
* Channel Coarse Tuning (0x00, 0x02): `"channelcoarsetuning"`
* Tuning Program (0x00, 0x03): `"tuningprogram"`
* Tuning Bank (0x00, 0x04): `"tuningbank"`
* Modulation Range (0x00, 0x05): `"modulationrange"`
* Azimuth Angle (0x3D, 0x00): `"azimuthangle"`
* Elevation Angle (0x3D, 0x01): `"elevationangle"`
* Gain (0x3D, 0x02): `"gain"`
* Distance Ratio (0x3D, 0x03): `"distanceratio"`
* Maximum Distance (0x3D, 0x04): `"maximumdistance"`
* Maximum Distance Gain (0x3D, 0x05): `"maximumdistancegain"`
* Reference Distance Ratio (0x3D, 0x06): `"referencedistanceratio"`
* Pan Spread Angle (0x3D, 0x07): `"panspreadangle"`
* Roll Angle (0x3D, 0x08): `"rollangle"`
**Parameters**
> Signature: `sendRpnDecrement(parameter, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`parameter`** | String Array.<number> ||A string identifying the parameter's name (see above) or a two-position array specifying the two control bytes (0x65, 0x64) that identify the registered parameter.|
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
**Throws**:
* TypeError The specified parameter is not available.
### `.sendRpnIncrement(...)` {#sendRpnIncrement}
Increments the specified MIDI registered parameter by 1. Here is the full list of parameter
names that can be used with this method:
* Pitchbend Range (0x00, 0x00): `"pitchbendrange"`
* Channel Fine Tuning (0x00, 0x01): `"channelfinetuning"`
* Channel Coarse Tuning (0x00, 0x02): `"channelcoarsetuning"`
* Tuning Program (0x00, 0x03): `"tuningprogram"`
* Tuning Bank (0x00, 0x04): `"tuningbank"`
* Modulation Range (0x00, 0x05): `"modulationrange"`
* Azimuth Angle (0x3D, 0x00): `"azimuthangle"`
* Elevation Angle (0x3D, 0x01): `"elevationangle"`
* Gain (0x3D, 0x02): `"gain"`
* Distance Ratio (0x3D, 0x03): `"distanceratio"`
* Maximum Distance (0x3D, 0x04): `"maximumdistance"`
* Maximum Distance Gain (0x3D, 0x05): `"maximumdistancegain"`
* Reference Distance Ratio (0x3D, 0x06): `"referencedistanceratio"`
* Pan Spread Angle (0x3D, 0x07): `"panspreadangle"`
* Roll Angle (0x3D, 0x08): `"rollangle"`
**Parameters**
> Signature: `sendRpnIncrement(parameter, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`parameter`** | String Array.<number> ||A string identifying the parameter's name (see above) or a two-position array specifying the two control bytes (0x65, 0x64) that identify the registered parameter.|
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendRpnValue(...)` {#sendRpnValue}
Sets the specified MIDI registered parameter to the desired value. The value is defined with
up to two bytes of data (msb, lsb) that each can go from `0` to `127`.
MIDI
[registered parameters](https://www.midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2)
extend the original list of control change messages. The MIDI 1.0 specification lists only a
limited number of them:
| Numbers | Function |
|--------------|--------------------------|
| (0x00, 0x00) | `pitchbendrange` |
| (0x00, 0x01) | `channelfinetuning` |
| (0x00, 0x02) | `channelcoarsetuning` |
| (0x00, 0x03) | `tuningprogram` |
| (0x00, 0x04) | `tuningbank` |
| (0x00, 0x05) | `modulationrange` |
| (0x3D, 0x00) | `azimuthangle` |
| (0x3D, 0x01) | `elevationangle` |
| (0x3D, 0x02) | `gain` |
| (0x3D, 0x03) | `distanceratio` |
| (0x3D, 0x04) | `maximumdistance` |
| (0x3D, 0x05) | `maximumdistancegain` |
| (0x3D, 0x06) | `referencedistanceratio` |
| (0x3D, 0x07) | `panspreadangle` |
| (0x3D, 0x08) | `rollangle` |
Note that the `tuningprogram` and `tuningbank` parameters are part of the *MIDI Tuning
Standard*, which is not widely implemented.
**Parameters**
> Signature: `sendRpnValue(parameter, [data], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`parameter`** | string Array.<number> ||A string identifying the parameter's name (see above) or a two-position array specifying the two control bytes (e.g. `[0x65, 0x64]`) that identify the registered parameter.|
|[**`data`**] | number Array.<number> |[]|A single integer or an array of integers with a maximum length of 2 specifying the desired data.|
|[**`options`**] | object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendSongPosition(...)` {#sendSongPosition}
**Since**: 3.0.0
Sends a **song position** MIDI message. The value is expressed in MIDI beats (between `0` and
`16383`) which are 16th note. Position `0` is always the start of the song.
**Parameters**
> Signature: `sendSongPosition([value], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`value`**] | number |0|The MIDI beat to cue to (integer between `0` and `16383`).|
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendSongSelect(...)` {#sendSongSelect}
**Since**: 3.0.0
Sends a **song select** MIDI message.
**Parameters**
> Signature: `sendSongSelect([value], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`value`**] | number |0|The number of the song to select (integer between `0` and `127`).|
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
**Throws**:
* The song number must be between 0 and 127.
### `.sendStart(...)` {#sendStart}
Sends a **start** real-time message. A MIDI Start message starts the playback of the current
song at beat 0. To start playback elsewhere in the song, use the
[`sendContinue()`](#sendContinue) method.
**Parameters**
> Signature: `sendStart([options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendStop(...)` {#sendStop}
Sends a **stop** real-time message. This tells the device connected to this output to stop
playback immediately (or at the scheduled time, if specified).
**Parameters**
> Signature: `sendStop([options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendSysex(...)` {#sendSysex}
Sends a MIDI [**system exclusive**](https://www.midi.org/specifications-old/item/table-4-universal-system-exclusive-messages)
(*sysex*) message. There are two categories of system exclusive messages: manufacturer-specific
messages and universal messages. Universal messages are further divided into three subtypes:
* Universal non-commercial (for research and testing): `0x7D`
* Universal non-realtime: `0x7E`
* Universal realtime: `0x7F`
The method's first parameter (`identification`) identifies the type of message. If the value of
`identification` is `0x7D` (125), `0x7E` (126) or `0x7F` (127), the message will be identified
as a **universal non-commercial**, **universal non-realtime** or **universal realtime** message
(respectively).
If the `identification` value is an array or an integer between 0 and 124, it will be used to
identify the manufacturer targeted by the message. The *MIDI Manufacturers Association*
maintains a full list of
[Manufacturer ID Numbers](https://www.midi.org/specifications-old/item/manufacturer-id-numbers).
The `data` parameter should only contain the data of the message. When sending out the actual
MIDI message, WEBMIDI.js will automatically prepend the data with the **sysex byte** (`0xF0`)
and the identification byte(s). It will also automatically terminate the message with the
**sysex end byte** (`0xF7`).
To use the `sendSysex()` method, system exclusive message support must have been enabled. To
do so, you must set the `sysex` option to `true` when calling
[`WebMidi.enable()`](WebMidi#enable):
```js
WebMidi.enable({sysex: true})
.then(() => console.log("System exclusive messages are enabled");
```
##### Examples of manufacturer-specific system exclusive messages
If you want to send a sysex message to a Korg device connected to the first output, you would
use the following code:
```js
WebMidi.outputs[0].sendSysex(0x42, [0x1, 0x2, 0x3, 0x4, 0x5]);
```
In this case `0x42` is the ID of the manufacturer (Korg) and `[0x1, 0x2, 0x3, 0x4, 0x5]` is the
data being sent.
The parameters can be specified using any number notation (decimal, hex, binary, etc.).
Therefore, the code above is equivalent to this code:
```js
WebMidi.outputs[0].sendSysex(66, [1, 2, 3, 4, 5]);
```
Some manufacturers are identified using 3 bytes. In this case, you would use a 3-position array
as the first parameter. For example, to send the same sysex message to a
*Native Instruments* device:
```js
WebMidi.outputs[0].sendSysex([0x00, 0x21, 0x09], [0x1, 0x2, 0x3, 0x4, 0x5]);
```
There is no limit for the length of the data array. However, it is generally suggested to keep
system exclusive messages to 64Kb or less.
##### Example of universal system exclusive message
If you want to send a universal sysex message, simply assign the correct identification number
in the first parameter. Number `0x7D` (125) is for non-commercial, `0x7E` (126) is for
non-realtime and `0x7F` (127) is for realtime.
So, for example, if you wanted to send an identity request non-realtime message (`0x7E`), you
could use the following:
```js
WebMidi.outputs[0].sendSysex(0x7E, [0x7F, 0x06, 0x01]);
```
For more details on the format of universal messages, consult the list of
[universal sysex messages](https://www.midi.org/specifications-old/item/table-4-universal-system-exclusive-messages).
**Parameters**
> Signature: `sendSysex(identification, [data], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`identification`** | number Array.<number> ||An unsigned integer or an array of three unsigned integers between `0` and `127` that either identify the manufacturer or sets the message to be a **universal non-commercial message** (`0x7D`), a **universal non-realtime message** (`0x7E`) or a **universal realtime message** (`0x7F`). The *MIDI Manufacturers Association* maintains a full list of [Manufacturer ID Numbers](https://www.midi.org/specifications-old/item/manufacturer-id-numbers).|
|[**`data`**] | Array.<number> Uint8Array ||A `Uint8Array` or an array of unsigned integers between `0` and `127`. This is the data you wish to transfer.|
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
**Throws**:
* `DOMException` : Failed to execute 'send' on 'MIDIOutput': System exclusive message is
not allowed.
* `TypeError` : Failed to execute 'send' on 'MIDIOutput': The value at index x is greater
than 0xFF.
### `.sendTimecodeQuarterFrame(...)` {#sendTimecodeQuarterFrame}
Sends a MIDI **timecode quarter frame** message. Please note that no processing is being done
on the data. It is up to the developer to format the data according to the
[MIDI Timecode](https://en.wikipedia.org/wiki/MIDI_timecode) format.
**Parameters**
> Signature: `sendTimecodeQuarterFrame(value, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`value`** | number ||The quarter frame message content (integer between 0 and 127).|
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendTuneRequest(...)` {#sendTuneRequest}
**Since**: 3.0.0
Sends a MIDI **tune request** real-time message.
**Parameters**
> Signature: `sendTuneRequest([options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.sendTuningBank(...)` {#sendTuningBank}
**Since**: 3.0.0
Sets the MIDI tuning bank to use. Note that the **Tuning Bank** parameter is part of the
*MIDI Tuning Standard*, which is not widely implemented.
**Parameters**
> Signature: `sendTuningBank([value], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`value`**] | number |0|The desired tuning bank (integer between `0` and `127`).|
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
**Throws**:
* `RangeError` : The bank value must be between 0 and 127.
### `.sendTuningProgram(...)` {#sendTuningProgram}
**Since**: 3.0.0
Sets the MIDI tuning program to use. Note that the **Tuning Program** parameter is part of the
*MIDI Tuning Standard*, which is not widely implemented.
**Parameters**
> Signature: `sendTuningProgram(value, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`value`** | number ||The desired tuning program (integer between `0` and `127`).|
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
**Throws**:
* `RangeError` : The program value must be between 0 and 127.
### `.stopNote(...)` {#stopNote}
Sends a **note off** message for the specified MIDI note number on the specified channel(s).
The first parameter is the note to stop. It can be a single value or an array of the following
valid values:
- A MIDI note number (integer between `0` and `127`)
- A note identifier (e.g. `"C3"`, `"G#4"`, `"F-1"`, `"Db7"`)
- A [`Note`](Note) object
The execution of the **note off** command can be delayed by using the `time` property of the
`options` parameter.
**Parameters**
> Signature: `stopNote(note, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`note`** | number Note string Array.<number> Array.<Note> Array.<string> ||The note(s) to stop. The notes can be specified by using a MIDI note number (`0` - `127`), a note identifier (e.g. `C3`, `G#4`, `F-1`, `Db7`) or an array of the previous types. When using a note identifier, octave range must be between `-1` and `9`. The lowest note is `C-1` (MIDI note number `0`) and the highest note is `G9` (MIDI note number `127`).|
|[**`options`**] | Object |{}||
|[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|The MIDI channel number (between `1` and `16`) or an array of channel numbers to use. If no channel is specified, all channels will be used.|
|[**`options.release`**] | number |0.5|The velocity at which to release the note (between `0` and `1`). If the `rawRelease` option is also defined, `rawRelease` will have priority. An invalid velocity value will silently trigger the default of `0.5`.|
|[**`options.rawRelease`**] | number |64|The velocity at which to release the note (between `0` and `127`). If the `release` option is also defined, `rawRelease` will have priority. An invalid velocity value will silently trigger the default of `64`.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.suspendEvent(...)` {#suspendEvent}
Suspends execution of all callbacks functions registered for the specified event type.
You can suspend execution of callbacks registered with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) by passing
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) to `suspendEvent()`. Beware that this
will not suspend all callbacks but only those registered with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT). While this may seem counter-intuitive
at first glance, it allows the selective suspension of global listeners while leaving other
listeners alone. If you truly want to suspends all callbacks for a specific
[`EventEmitter`](EventEmitter), simply set its `eventsSuspended` property to `true`.
**Parameters**
> Signature: `suspendEvent(event)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event name (or `EventEmitter.ANY_EVENT`) for which to suspend execution of all callback functions.|
### `.unsuspendEvent(...)` {#unsuspendEvent}
Resumes execution of all suspended callback functions registered for the specified event type.
You can resume execution of callbacks registered with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) by passing
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) to `unsuspendEvent()`. Beware that
this will not resume all callbacks but only those registered with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT). While this may seem
counter-intuitive, it allows the selective unsuspension of global listeners while leaving other
callbacks alone.
**Parameters**
> Signature: `unsuspendEvent(event)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event name (or `EventEmitter.ANY_EVENT`) for which to resume execution of all callback functions.|
### `.waitFor(...)` {#waitFor}
**Attributes**: async
The `waitFor()` method is an async function which returns a promise. The promise is fulfilled
when the specified event occurs. The event can be a regular event or
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) (if you want to resolve as soon as any
event is emitted).
If the `duration` option is set, the promise will only be fulfilled if the event is emitted
within the specified duration. If the event has not been fulfilled after the specified
duration, the promise is rejected. This makes it super easy to wait for an event and timeout
after a certain time if the event is not triggered.
**Parameters**
> Signature: `waitFor(event, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event to wait for|
|[**`options`**] | Object |{}||
|[**`options.duration`**] | number |Infinity|The number of milliseconds to wait before the promise is automatically rejected.|
***
## Events
### `closed` {#event-closed}
Event emitted when the [Output](Output) has been closed by calling the
[close()](Output#close) method.
**Event Properties**
| Property | Type | Description |
| ------------------------ | ------------------------ | ------------------------ |
|**`timestamp`** |number|The moment (DOMHighResTimeStamp) when the event occurred (in milliseconds since the navigation start of the document).|
|**`type`** |string|`"closed"`|
|**`target`** |Output|The object to which the listener was originally added (`Output`).|
|**`port`** |Output|The port that was closed|
### `disconnected` {#event-disconnected}
Event emitted when the [Output](Output) becomes unavailable. This event is typically fired
when the MIDI device is unplugged.
**Event Properties**
| Property | Type | Description |
| ------------------------ | ------------------------ | ------------------------ |
|**`timestamp`** |number|The moment (DOMHighResTimeStamp0 when the event occurred (in milliseconds since the navigation start of the document).|
|**`type`** |string|`"disconnected"`|
|**`target`** |Output|The object to which the listener was originally added (`Output`).|
|**`port`** |object|Object with properties describing the [Output](Output) that was disconnected. This is not the actual `Output` as it is no longer available.|
### `opened` {#event-opened}
Event emitted when the [Output](Output) has been opened by calling the
[open()](Output#open) method.
**Event Properties**
| Property | Type | Description |
| ------------------------ | ------------------------ | ------------------------ |
|**`timestamp`** |number|The moment (DOMHighResTimeStamp) when the event occurred (in milliseconds since the navigation start of the document).|
|**`type`** |string|`"opened"`|
|**`target`** |Output|The object to which the listener was originally added (`Output`).|
|**`port`** |Output|The port that was opened|
================================================
FILE: website/api/classes/OutputChannel.md
================================================
# OutputChannel
The `OutputChannel` class represents a single output MIDI channel. `OutputChannel` objects are
provided by an [`Output`](Output) port which, itself, is made available by a device. The
`OutputChannel` object is derived from the host's MIDI subsystem and should not be instantiated
directly.
All 16 `OutputChannel` objects can be found inside the parent output's
[`channels`](Output#channels) property.
**Since**: 3.0.0
**Extends**: [`EventEmitter`](EventEmitter)
### `Constructor`
Creates an `OutputChannel` object.
**Parameters**
> `new OutputChannel(output, number)`
| Parameter | Type | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`output`** | Output ||The [`Output`](Output) this channel belongs to.|
|**`number`** | number ||The MIDI channel number (`1` - `16`).|
***
## Properties
### `.eventCount` {#eventCount}
**Type**: number
**Attributes**: read-only
The number of unique events that have registered listeners.
Note: this excludes global events registered with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) because they are not tied to a
specific event.
### `.eventMap` {#eventMap}
**Type**: Object
**Attributes**: read-only
An object containing a property for each event with at least one registered listener. Each
event property contains an array of all the [`Listener`](Listener) objects registered
for the event.
### `.eventNames` {#eventNames}
**Type**: Array.<string>
**Attributes**: read-only
An array of all the unique event names for which the emitter has at least one registered
listener.
Note: this excludes global events registered with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) because they are not tied to a
specific event.
### `.eventsSuspended` {#eventsSuspended}
**Type**: boolean
Whether or not the execution of callbacks is currently suspended for this emitter.
### `.number` {#number}
**Since**: 3.0
**Type**: number
This channel's MIDI number (`1` - `16`).
### `.octaveOffset` {#octaveOffset}
**Since**: 3.0
**Type**: number
An integer to offset the reported octave of outgoing note-specific messages (`noteon`,
`noteoff` and `keyaftertouch`). By default, middle C (MIDI note number 60) is placed on the 4th
octave (C4).
Note that this value is combined with the global offset value defined in
[`WebMidi.octaveOffset`](WebMidi#octaveOffset) and with the parent value defined in
[`Output.octaveOffset`](Output#octaveOffset).
### `.output` {#output}
**Since**: 3.0
**Type**: Output
The parent [`Output`](Output) this channel belongs to.
***
## Methods
### `.addListener(...)` {#addListener}
Adds a listener for the specified event. It returns the [`Listener`](Listener) object
that was created and attached to the event.
To attach a global listener that will be triggered for any events, use
[`EventEmitter.ANY_EVENT`](#ANY_EVENT) as the first parameter. Note that a global
listener will also be triggered by non-registered events.
**Parameters**
> Signature: `addListener(event, callback, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event to listen to.|
|**`callback`** | EventEmitter~callback ||The callback function to execute when the event occurs.|
|[**`options`**] | Object |{}||
|[**`options.context`**] | Object |this|The value of `this` in the callback function.|
|[**`options.prepend`**] | boolean |false|Whether the listener should be added at the beginning of the listeners array and thus executed first.|
|[**`options.duration`**] | number |Infinity|The number of milliseconds before the listener automatically expires.|
|[**`options.remaining`**] | number |Infinity|The number of times after which the callback should automatically be removed.|
|[**`options.arguments`**] | array ||An array of arguments which will be passed separately to the callback function. This array is stored in the [`arguments`](Listener#arguments) property of the [`Listener`](Listener) object and can be retrieved or modified as desired.|
**Return Value**
> Returns: `Listener`
The newly created [`Listener`](Listener) object.
**Throws**:
* `TypeError` : The `event` parameter must be a string or
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT).
* `TypeError` : The `callback` parameter must be a function.
### `.addOneTimeListener(...)` {#addOneTimeListener}
Adds a one-time listener for the specified event. The listener will be executed once and then
destroyed. It returns the [`Listener`](Listener) object that was created and attached
to the event.
To attach a global listener that will be triggered for any events, use
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) as the first parameter. Note that a
global listener will also be triggered by non-registered events.
**Parameters**
> Signature: `addOneTimeListener(event, callback, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event to listen to|
|**`callback`** | EventEmitter~callback ||The callback function to execute when the event occurs|
|[**`options`**] | Object |{}||
|[**`options.context`**] | Object |this|The context to invoke the callback function in.|
|[**`options.prepend`**] | boolean |false|Whether the listener should be added at the beginning of the listeners array and thus executed first.|
|[**`options.duration`**] | number |Infinity|The number of milliseconds before the listener automatically expires.|
|[**`options.arguments`**] | array ||An array of arguments which will be passed separately to the callback function. This array is stored in the [`arguments`](Listener#arguments) property of the [`Listener`](Listener) object and can be retrieved or modified as desired.|
**Return Value**
> Returns: `Listener`
The newly created [`Listener`](Listener) object.
**Throws**:
* `TypeError` : The `event` parameter must be a string or
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT).
* `TypeError` : The `callback` parameter must be a function.
### `.emit(...)` {#emit}
Executes the callback function of all the [`Listener`](Listener) objects registered for
a given event. The callback functions are passed the additional arguments passed to `emit()`
(if any) followed by the arguments present in the [`arguments`](Listener#arguments) property of
the [`Listener`](Listener) object (if any).
If the [`eventsSuspended`](#eventsSuspended) property is `true` or the
[`Listener.suspended`](Listener#suspended) property is `true`, the callback functions
will not be executed.
This function returns an array containing the return values of each of the callbacks.
It should be noted that the regular listeners are triggered first followed by the global
listeners (those added with [`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT)).
**Parameters**
> Signature: `emit(event, ...args)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string ||The event|
|**`args`** | * ||Arbitrary number of arguments to pass along to the callback functions|
**Return Value**
> Returns: `Array`
An array containing the return value of each of the executed listener
functions.
**Throws**:
* `TypeError` : The `event` parameter must be a string.
### `.getListenerCount(...)` {#getListenerCount}
Returns the number of listeners registered for a specific event.
Please note that global events (those added with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT)) do not count towards the remaining
number for a "regular" event. To get the number of global listeners, specifically use
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) as the parameter.
**Parameters**
> Signature: `getListenerCount(event)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event which is usually a string but can also be the special [`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) symbol.|
**Return Value**
> Returns: `number`
An integer representing the number of listeners registered for the specified
event.
### `.getListeners(...)` {#getListeners}
Returns an array of all the [`Listener`](Listener) objects that have been registered for
a specific event.
Please note that global events (those added with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT)) are not returned for "regular"
events. To get the list of global listeners, specifically use
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) as the parameter.
**Parameters**
> Signature: `getListeners(event)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event to get listeners for.|
**Return Value**
> Returns: `Array.`
An array of [`Listener`](Listener) objects.
### `.hasListener(...)` {#hasListener}
Returns `true` if the specified event has at least one registered listener. If no event is
specified, the method returns `true` if any event has at least one listener registered (this
includes global listeners registered to
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT)).
Note: to specifically check for global listeners added with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT), use
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) as the parameter.
**Parameters**
> Signature: `hasListener([event], [callback])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`event`**] | string Symbol |(any event)|The event to check|
|[**`callback`**] | function Listener |(any callback)|The actual function that was added to the event or the [Listener](Listener) object returned by `addListener()`.|
**Return Value**
> Returns: `boolean`
### `.playNote(...)` {#playNote}
Plays a note or an array of notes on the channel. The first parameter is the note to play. It
can be a single value or an array of the following valid values:
- A [`Note`](Note) object
- A MIDI note number (integer between `0` and `127`)
- A note name, followed by the octave (e.g. `"C3"`, `"G#4"`, `"F-1"`, `"Db7"`)
The `playNote()` method sends a **note on** MIDI message for all specified notes. If a
`duration` is set in the `options` parameter or in the [`Note`](Note) object's
[`duration`](Note#duration) property, it will also schedule a **note off** message
to end the note after said duration. If no `duration` is set, the note will simply play until
a matching **note off** message is sent with [`stopNote()`](#OutputChannel+stopNote) or
[`sendNoteOff()`](#OutputChannel+sendNoteOff).
The execution of the **note on** command can be delayed by using the `time` property of the
`options` parameter.
When using [`Note`](Note) objects, the durations and velocities defined in the
[`Note`](Note) objects have precedence over the ones specified via the method's `options`
parameter.
**Note**: per the MIDI standard, a **note on** message with an attack velocity of `0` is
functionally equivalent to a **note off** message.
**Parameters**
> Signature: `playNote(note, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`note`** | number string Note Array.<number> Array.<string> Array.<Note> ||The note(s) to play. The notes can be specified by using a MIDI note number (`0` - `127`), a note identifier (e.g. `C3`, `G#4`, `F-1`, `Db7`), a [`Note`](Note) object or an array of the previous types. When using a note identifier, the octave range must be between `-1` and `9`. The lowest note is `C-1` (MIDI note number `0`) and the highest note is `G9` (MIDI note number `127`).|
|[**`options`**] | object |{}||
|[**`options.duration`**] | number ||A positive decimal number larger than `0` representing the number of milliseconds to wait before sending a **note off** message. If invalid or left undefined, only a **note on** message will be sent.|
|[**`options.attack`**] | number |0.5|The velocity at which to play the note (between `0` and `1`). If the `rawAttack` option is also defined, it will have priority. An invalid velocity value will silently trigger the default of `0.5`.|
|[**`options.rawAttack`**] | number |64|The attack velocity at which to play the note (between `0` and `127`). This has priority over the `attack` property. An invalid velocity value will silently trigger the default of 64.|
|[**`options.release`**] | number |0.5|The velocity at which to release the note (between `0` and `1`). If the `rawRelease` option is also defined, it will have priority. An invalid velocity value will silently trigger the default of `0.5`. This is only used with the **note off** event triggered when `options.duration` is set.|
|[**`options.rawRelease`**] | number |64|The velocity at which to release the note (between `0` and `127`). This has priority over the `release` property. An invalid velocity value will silently trigger the default of 64. This is only used with the **note off** event triggered when `options.duration` is set.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
### `.removeListener(...)` {#removeListener}
Removes all the listeners that were added to the object upon which the method is called and
that match the specified criterias. If no parameters are passed, all listeners added to this
object will be removed. If only the `event` parameter is passed, all listeners for that event
will be removed from that object. You can remove global listeners by using
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) as the first parameter.
To use more granular options, you must at least define the `event`. Then, you can specify the
callback to match or one or more of the additional options.
**Parameters**
> Signature: `removeListener([event], [callback], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`event`**] | string ||The event name.|
|[**`callback`**] | EventEmitter~callback ||Only remove the listeners that match this exact callback function.|
|[**`options`**] | Object |||
|[**`options.context`**] | * ||Only remove the listeners that have this exact context.|
|[**`options.remaining`**] | number ||Only remove the listener if it has exactly that many remaining times to be executed.|
### `.send(...)` {#send}
Sends a MIDI message on the MIDI output port. If no time is specified, the message will be
sent immediately. The message should be an array of 8-bit unsigned integers (`0` - `225`),
a
[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array)
object or a [`Message`](Message) object.
It is usually not necessary to use this method directly as you can use one of the simpler
helper methods such as [`playNote()`](#playNote), [`stopNote()`](#stopNote),
[`sendControlChange()`](#sendControlChange), etc.
Details on the format of MIDI messages are available in the summary of
[MIDI messages](https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message)
from the MIDI Manufacturers Association.
**Parameters**
> Signature: `send(message, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`message`** | Array.<number> Uint8Array Message ||A `Message` object, an array of 8-bit unsigned integers or a `Uint8Array` object (not available in Node.js) containing the message bytes.|
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
**Throws**:
* `RangeError` : The first byte (status) must be an integer between 128 and 255.
* `RangeError` : Data bytes must be integers between 0 and 255.
### `.sendAllNotesOff(...)` {#sendAllNotesOff}
Sends an **all notes off** channel mode message. This will make all currently playing notes
fade out just as if their key had been released. This is different from the
[`sendAllSoundOff()`](#sendAllSoundOff) method which mutes all sounds immediately.
**Parameters**
> Signature: `sendAllNotesOff([options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`options`**] | Object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
### `.sendAllSoundOff(...)` {#sendAllSoundOff}
Sends an **all sound off** channel mode message. This will silence all sounds playing on that
channel but will not prevent new sounds from being triggered.
**Parameters**
> Signature: `sendAllSoundOff([options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`options`**] | Object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
### `.sendChannelAftertouch(...)` {#sendChannelAftertouch}
Sends a MIDI **channel aftertouch** message. For key-specific aftertouch, you should instead
use [`sendKeyAftertouch()`](#sendKeyAftertouch).
**Parameters**
> Signature: `sendChannelAftertouch([pressure], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`pressure`**] | number ||The pressure level (between `0` and `1`). If the `rawValue` option is set to `true`, the pressure can be defined by using an integer between `0` and `127`.|
|[**`options`**] | object |{}||
|[**`options.rawValue`**] | boolean |false|A boolean indicating whether the value should be considered a float between `0` and `1.0` (default) or a raw integer between `0` and `127`.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
**Throws**:
* RangeError Invalid channel aftertouch value.
### `.sendChannelMode(...)` {#sendChannelMode}
Sends a MIDI **channel mode** message. The channel mode message to send can be specified
numerically or by using one of the following common names:
| Type |Number| Shortcut Method |
| ---------------------|------|-------------------------------------------------------------- |
| `allsoundoff` | 120 | [`sendAllSoundOff()`](#sendAllSoundOff) |
| `resetallcontrollers`| 121 | [`sendResetAllControllers()`](#sendResetAllControllers) |
| `localcontrol` | 122 | [`sendLocalControl()`](#sendLocalControl) |
| `allnotesoff` | 123 | [`sendAllNotesOff()`](#sendAllNotesOff) |
| `omnimodeoff` | 124 | [`sendOmniMode(false)`](#sendOmniMode) |
| `omnimodeon` | 125 | [`sendOmniMode(true)`](#sendOmniMode) |
| `monomodeon` | 126 | [`sendPolyphonicMode("mono")`](#sendPolyphonicMode) |
| `polymodeon` | 127 | [`sendPolyphonicMode("poly")`](#sendPolyphonicMode) |
**Note**: as you can see above, to make it easier, all channel mode messages also have a matching
helper method.
It should be noted that, per the MIDI specification, only `localcontrol` and `monomodeon` may
require a value that's not zero. For that reason, the `value` parameter is optional and
defaults to 0.
**Parameters**
> Signature: `sendChannelMode(command, [value], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`command`** | number string ||The numerical identifier of the channel mode message (integer between `120` and `127`) or its name as a string.|
|[**`value`**] | number |0|The value to send (integer between `0` - `127`).|
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
### `.sendControlChange(...)` {#sendControlChange}
**Since**: 3.0.0
Sends a MIDI **control change** message to the channel at the scheduled time. The control
change message to send can be specified numerically (`0` to `127`) or by using one of the
following common names:
| Number | Name |
|--------|-------------------------------|
| 0 |`bankselectcoarse` |
| 1 |`modulationwheelcoarse` |
| 2 |`breathcontrollercoarse` |
| 4 |`footcontrollercoarse` |
| 5 |`portamentotimecoarse` |
| 6 |`dataentrycoarse` |
| 7 |`volumecoarse` |
| 8 |`balancecoarse` |
| 10 |`pancoarse` |
| 11 |`expressioncoarse` |
| 12 |`effectcontrol1coarse` |
| 13 |`effectcontrol2coarse` |
| 18 |`generalpurposeslider3` |
| 19 |`generalpurposeslider4` |
| 32 |`bankselectfine` |
| 33 |`modulationwheelfine` |
| 34 |`breathcontrollerfine` |
| 36 |`footcontrollerfine` |
| 37 |`portamentotimefine` |
| 38 |`dataentryfine` |
| 39 |`volumefine` |
| 40 |`balancefine` |
| 42 |`panfine` |
| 43 |`expressionfine` |
| 44 |`effectcontrol1fine` |
| 45 |`effectcontrol2fine` |
| 64 |`holdpedal` |
| 65 |`portamento` |
| 66 |`sustenutopedal` |
| 67 |`softpedal` |
| 68 |`legatopedal` |
| 69 |`hold2pedal` |
| 70 |`soundvariation` |
| 71 |`resonance` |
| 72 |`soundreleasetime` |
| 73 |`soundattacktime` |
| 74 |`brightness` |
| 75 |`soundcontrol6` |
| 76 |`soundcontrol7` |
| 77 |`soundcontrol8` |
| 78 |`soundcontrol9` |
| 79 |`soundcontrol10` |
| 80 |`generalpurposebutton1` |
| 81 |`generalpurposebutton2` |
| 82 |`generalpurposebutton3` |
| 83 |`generalpurposebutton4` |
| 91 |`reverblevel` |
| 92 |`tremololevel` |
| 93 |`choruslevel` |
| 94 |`celestelevel` |
| 95 |`phaserlevel` |
| 96 |`dataincrement` |
| 97 |`datadecrement` |
| 98 |`nonregisteredparametercoarse` |
| 99 |`nonregisteredparameterfine` |
| 100 |`registeredparametercoarse` |
| 101 |`registeredparameterfine` |
| 120 |`allsoundoff` |
| 121 |`resetallcontrollers` |
| 122 |`localcontrol` |
| 123 |`allnotesoff` |
| 124 |`omnimodeoff` |
| 125 |`omnimodeon` |
| 126 |`monomodeon` |
| 127 |`polymodeon` |
As you can see above, not all control change message have a matching name. This does not mean
you cannot use the others. It simply means you will need to use their number
(`0` to `127`) instead of their name. While you can still use them, numbers `120` to `127` are
usually reserved for *channel mode* messages. See
[`sendChannelMode()`](#OutputChannel+sendChannelMode) method for more info.
To view a detailed list of all available **control change** messages, please consult "Table 3 -
Control Change Messages" from the [MIDI Messages](
https://www.midi.org/specifications/item/table-3-control-change-messages-data-bytes-2)
specification.
**Note**: messages #0-31 (MSB) are paired with messages #32-63 (LSB). For example, message #1
(`modulationwheelcoarse`) can be accompanied by a second control change message for
`modulationwheelfine` to achieve a greater level of precision. if you want to specify both MSB
and LSB for messages between `0` and `31`, you can do so by passing a 2-value array as the
second parameter.
**Parameters**
> Signature: `sendControlChange(controller, value, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`controller`** | number string ||The MIDI controller name or number (`0` - `127`).|
|**`value`** | number Array.<number> ||The value to send (0-127). You can also use a two-position array for controllers 0 to 31. In this scenario, the first value will be sent as usual and the second value will be sent to the matching LSB controller (which is obtained by adding 32 to the first controller)|
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
**Throws**:
* `RangeError` : Controller numbers must be between 0 and 127.
* `RangeError` : Invalid controller name.
* `TypeError` : The value array must have a length of 2.
### `.sendKeyAftertouch(...)` {#sendKeyAftertouch}
Sends a MIDI **key aftertouch** message at the scheduled time. This is a key-specific
aftertouch. For a channel-wide aftertouch message, use
[`sendChannelAftertouch()`](#sendChannelAftertouch).
**Parameters**
> Signature: `sendKeyAftertouch(target, [pressure], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`target`** | number Note string Array.<number> Array.<Note> Array.<string> ||The note(s) for which you are sending an aftertouch value. The notes can be specified by using a MIDI note number (`0` - `127`), a [`Note`](Note) object, a note identifier (e.g. `C3`, `G#4`, `F-1`, `Db7`) or an array of the previous types. When using a note identifier, octave range must be between `-1` and `9`. The lowest note is `C-1` (MIDI note number `0`) and the highest note is `G9` (MIDI note number `127`). When using a note identifier, the octave value will be offset by the local [`octaveOffset`](#octaveOffset) and by [`Output.octaveOffset`](Output#octaveOffset) and [`WebMidi.octaveOffset`](WebMidi#octaveOffset) (if those values are not `0`). When using a key number, `octaveOffset` values are ignored.|
|[**`pressure`**] | number |0.5|The pressure level (between `0` and `1`). An invalid pressure value will silently trigger the default behaviour. If the `rawValue` option is set to `true`, the pressure is defined by using an integer between `0` and `127`.|
|[**`options`**] | object |{}||
|[**`options.rawValue`**] | boolean |false|A boolean indicating whether the value should be considered a float between `0` and `1.0` (default) or a raw integer between `0` and `127`.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
**Throws**:
* RangeError Invalid key aftertouch value.
### `.sendLocalControl(...)` {#sendLocalControl}
Turns local control on or off. Local control is usually enabled by default. If you disable it,
the instrument will no longer trigger its own sounds. It will only send the MIDI messages to
its out port.
**Parameters**
> Signature: `sendLocalControl([state], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`state`**] | boolean |false|Whether to activate local control (`true`) or disable it (`false`).|
|[**`options`**] | Object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
### `.sendMasterTuning(...)` {#sendMasterTuning}
Sends a **master tuning** message. The value is decimal and must be larger than -65 semitones
and smaller than 64 semitones.
Because of the way the MIDI specification works, the decimal portion of the value will be
encoded with a resolution of 14bit. The integer portion must be between -64 and 63
inclusively. This function actually generates two MIDI messages: a **Master Coarse Tuning** and
a **Master Fine Tuning** RPN messages.
**Parameters**
> Signature: `sendMasterTuning([value], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`value`**] | number |0.0|The desired decimal adjustment value in semitones (-65 < x < 64)|
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
**Throws**:
* `RangeError` : The value must be a decimal number between larger than -65 and smaller
than 64.
### `.sendModulationRange(...)` {#sendModulationRange}
Sends a **modulation depth range** message to adjust the depth of the modulation wheel's range.
The range can be specified with the `semitones` parameter, the `cents` parameter or by
specifying both parameters at the same time.
**Parameters**
> Signature: `sendModulationRange(semitones, [cents], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`semitones`** | number ||The desired adjustment value in semitones (integer between 0 and 127).|
|[**`cents`**] | number |0|The desired adjustment value in cents (integer between 0 and 127).|
|[**`options`**] | Object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
### `.sendNoteOff(...)` {#sendNoteOff}
Sends a **note off** message for the specified notes on the channel. The first parameter is the
note. It can be a single value or an array of the following valid values:
- A MIDI note number (integer between `0` and `127`)
- A note name, followed by the octave (e.g. `"C3"`, `"G#4"`, `"F-1"`, `"Db7"`)
- A [`Note`](Note) object
The execution of the **note off** command can be delayed by using the `time` property of the
`options` parameter.
When using [`Note`](Note) objects, the release velocity defined in the
[`Note`](Note) objects has precedence over the one specified via the method's `options`
parameter.
**Parameters**
> Signature: `sendNoteOff(note, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`note`** | number string Note Array.<number> Array.<string> Array.<Note> ||The note(s) to stop. The notes can be specified by using a MIDI note number (0-127), a note identifier (e.g. C3, G#4, F-1, Db7), a [`Note`](Note) object or an array of the previous types. When using a note name, octave range must be between -1 and 9. The lowest note is C-1 (MIDI note number 0) and the highest note is G9 (MIDI note number 127).|
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
|[**`options.release`**] | number |0.5|The velocity at which to release the note (between `0` and `1`). If the `rawRelease` option is also defined, `rawRelease` will have priority. An invalid velocity value will silently trigger the default of `0.5`.|
|[**`options.rawRelease`**] | number |64|The velocity at which to release the note (between `0` and `127`). If the `release` option is also defined, `rawRelease` will have priority. An invalid velocity value will silently trigger the default of `64`.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
### `.sendNoteOn(...)` {#sendNoteOn}
Sends a **note on** message for the specified note(s) on the channel. The first parameter is
the note. It can be a single value or an array of the following valid values:
- A [`Note`](Note) object
- A MIDI note number (integer between `0` and `127`)
- A note identifier (e.g. `"C3"`, `"G#4"`, `"F-1"`, `"Db7"`)
When passing a [`Note`](Note)object or a note name, the `octaveOffset` will be applied.
This is not the case when using a note number. In this case, we assume you know exactly which
MIDI note number should be sent out.
The execution of the **note on** command can be delayed by using the `time` property of the
`options` parameter.
When using [`Note`](Note) objects, the attack velocity defined in the
[`Note`](Note) objects has precedence over the one specified via the method's `options`
parameter. Also, the `duration` is ignored. If you want to also send a **note off** message,
use the [`playNote()`](#playNote) method instead.
**Note**: As per the MIDI standard, a **note on** message with an attack velocity of `0` is
functionally equivalent to a **note off** message.
**Parameters**
> Signature: `sendNoteOn(note, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`note`** | number string Note Array.<number> Array.<string> Array.<Note> ||The note(s) to play. The notes can be specified by using a MIDI note number (0-127), a note identifier (e.g. C3, G#4, F-1, Db7), a [`Note`](Note) object or an array of the previous types.|
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
|[**`options.attack`**] | number |0.5|The velocity at which to play the note (between `0` and `1`). If the `rawAttack` option is also defined, `rawAttack` will have priority. An invalid velocity value will silently trigger the default of `0.5`.|
|[**`options.rawAttack`**] | number |64|The velocity at which to release the note (between `0` and `127`). If the `attack` option is also defined, `rawAttack` will have priority. An invalid velocity value will silently trigger the default of `64`.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
### `.sendNrpnValue(...)` {#sendNrpnValue}
Sets a non-registered parameter (NRPN) to the specified value. The NRPN is selected by passing
in a two-position array specifying the values of the two control bytes. The value is specified
by passing in a single integer (most cases) or an array of two integers.
NRPNs are not standardized in any way. Each manufacturer is free to implement them any way
they see fit. For example, according to the Roland GS specification, you can control the
**vibrato rate** using NRPN (1, 8). Therefore, to set the **vibrato rate** value to **123** you
would use:
```js
WebMidi.outputs[0].channels[0].sendNrpnValue([1, 8], 123);
```
In some rarer cases, you need to send two values with your NRPN messages. In such cases, you
would use a 2-position array. For example, for its **ClockBPM** parameter (2, 63), Novation
uses a 14-bit value that combines an MSB and an LSB (7-bit values). So, for example, if the
value to send was 10, you could use:
```js
WebMidi.outputs[0].channels[0].sendNrpnValue([2, 63], [0, 10]);
```
For further implementation details, refer to the manufacturer's documentation.
**Parameters**
> Signature: `sendNrpnValue(nrpn, [data], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`nrpn`** | Array.<number> ||A two-position array specifying the two control bytes (0x63, 0x62) that identify the non-registered parameter.|
|[**`data`**] | number Array.<number> |[]|An integer or an array of integers with a length of 1 or 2 specifying the desired data.|
|[**`options`**] | Object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
**Throws**:
* `RangeError` : The control value must be between 0 and 127.
* `RangeError` : The msb value must be between 0 and 127
### `.sendOmniMode(...)` {#sendOmniMode}
Sets OMNI mode to `"on"` or `"off"`. MIDI's OMNI mode causes the instrument to respond to
messages from all channels.
It should be noted that support for OMNI mode is not as common as it used to be.
**Parameters**
> Signature: `sendOmniMode([state], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`state`**] | boolean |true|Whether to activate OMNI mode (`true`) or not (`false`).|
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
**Throws**:
* `TypeError` : Invalid channel mode message name.
* `RangeError` : Channel mode controller numbers must be between 120 and 127.
* `RangeError` : Value must be an integer between 0 and 127.
### `.sendPitchBend(...)` {#sendPitchBend}
Sends a MIDI **pitch bend** message at the scheduled time. The resulting bend is relative to
the pitch bend range that has been defined. The range can be set with
[`sendPitchBendRange()`](#sendPitchBendRange). So, for example, if the pitch
bend range has been set to 12 semitones, using a bend value of -1 will bend the note 1 octave
below its nominal value.
**Parameters**
> Signature: `sendPitchBend([value], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`value`**] | number Array.<number> ||The intensity of the bend (between -1.0 and 1.0). A value of zero means no bend. If the `rawValue` option is set to `true`, the intensity of the bend can be defined by either using a single integer between 0 and 127 (MSB) or an array of two integers between 0 and 127 representing, respectively, the MSB (most significant byte) and the LSB (least significant byte). The MSB is expressed in semitones with `64` meaning no bend. A value lower than `64` bends downwards while a value higher than `64` bends upwards. The LSB is expressed in cents (1/100 of a semitone). An LSB of `64` also means no bend.|
|[**`options`**] | Object |{}||
|[**`options.rawValue`**] | boolean |false|A boolean indicating whether the value should be considered as a float between -1.0 and 1.0 (default) or as raw integer between 0 and 127 (or an array of 2 integers if using both MSB and LSB).|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
### `.sendPitchBendRange(...)` {#sendPitchBendRange}
Sends a **pitch bend range** message at the scheduled time to adjust the range used by the
pitch bend lever. The range is specified by using the `semitones` and `cents` parameters. For
example, setting the `semitones` parameter to `12` means that the pitch bend range will be 12
semitones above and below the nominal pitch.
**Parameters**
> Signature: `sendPitchBendRange(semitones, [cents], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`semitones`** | number ||The desired adjustment value in semitones (between 0 and 127). While nothing imposes that in the specification, it is very common for manufacturers to limit the range to 2 octaves (-12 semitones to 12 semitones).|
|[**`cents`**] | number |0|The desired adjustment value in cents (integer between 0-127).|
|[**`options`**] | Object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
**Throws**:
* `RangeError` : The semitones value must be an integer between 0 and 127.
* `RangeError` : The cents value must be an integer between 0 and 127.
### `.sendPolyphonicMode(...)` {#sendPolyphonicMode}
Sets the polyphonic mode. In `"poly"` mode (usually the default), multiple notes can be played
and heard at the same time. In `"mono"` mode, only one note will be heard at once even if
multiple notes are being played.
**Parameters**
> Signature: `sendPolyphonicMode([mode], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`mode`**] | string |poly|The mode to use: `"mono"` or `"poly"`.|
|[**`options`**] | Object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
### `.sendProgramChange(...)` {#sendProgramChange}
Sends a MIDI **program change** message at the scheduled time.
**Parameters**
> Signature: `sendProgramChange([program], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`program`**] | number |1|The MIDI patch (program) number (integer between `0` and `127`).|
|[**`options`**] | Object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
**Throws**:
* `TypeError` : Failed to execute 'send' on 'MIDIOutput': The value at index 1 is greater
than 0xFF.
### `.sendResetAllControllers(...)` {#sendResetAllControllers}
Sends a **reset all controllers** channel mode message. This resets all controllers, such as
the pitch bend, to their default value.
**Parameters**
> Signature: `sendResetAllControllers([options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`options`**] | Object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
### `.sendRpnDecrement(...)` {#sendRpnDecrement}
Decrements the specified MIDI registered parameter by 1. Here is the full list of parameter
names that can be used with this function:
* Pitchbend Range (0x00, 0x00): `"pitchbendrange"`
* Channel Fine Tuning (0x00, 0x01): `"channelfinetuning"`
* Channel Coarse Tuning (0x00, 0x02): `"channelcoarsetuning"`
* Tuning Program (0x00, 0x03): `"tuningprogram"`
* Tuning Bank (0x00, 0x04): `"tuningbank"`
* Modulation Range (0x00, 0x05): `"modulationrange"`
* Azimuth Angle (0x3D, 0x00): `"azimuthangle"`
* Elevation Angle (0x3D, 0x01): `"elevationangle"`
* Gain (0x3D, 0x02): `"gain"`
* Distance Ratio (0x3D, 0x03): `"distanceratio"`
* Maximum Distance (0x3D, 0x04): `"maximumdistance"`
* Maximum Distance Gain (0x3D, 0x05): `"maximumdistancegain"`
* Reference Distance Ratio (0x3D, 0x06): `"referencedistanceratio"`
* Pan Spread Angle (0x3D, 0x07): `"panspreadangle"`
* Roll Angle (0x3D, 0x08): `"rollangle"`
**Parameters**
> Signature: `sendRpnDecrement(parameter, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`parameter`** | String Array.<number> ||A string identifying the parameter's name (see above) or a two-position array specifying the two control bytes (0x65, 0x64) that identify the registered parameter.|
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
**Throws**:
* TypeError The specified registered parameter is invalid.
### `.sendRpnIncrement(...)` {#sendRpnIncrement}
Increments the specified MIDI registered parameter by 1. Here is the full list of parameter
names that can be used with this function:
* Pitchbend Range (0x00, 0x00): `"pitchbendrange"`
* Channel Fine Tuning (0x00, 0x01): `"channelfinetuning"`
* Channel Coarse Tuning (0x00, 0x02): `"channelcoarsetuning"`
* Tuning Program (0x00, 0x03): `"tuningprogram"`
* Tuning Bank (0x00, 0x04): `"tuningbank"`
* Modulation Range (0x00, 0x05): `"modulationrange"`
* Azimuth Angle (0x3D, 0x00): `"azimuthangle"`
* Elevation Angle (0x3D, 0x01): `"elevationangle"`
* Gain (0x3D, 0x02): `"gain"`
* Distance Ratio (0x3D, 0x03): `"distanceratio"`
* Maximum Distance (0x3D, 0x04): `"maximumdistance"`
* Maximum Distance Gain (0x3D, 0x05): `"maximumdistancegain"`
* Reference Distance Ratio (0x3D, 0x06): `"referencedistanceratio"`
* Pan Spread Angle (0x3D, 0x07): `"panspreadangle"`
* Roll Angle (0x3D, 0x08): `"rollangle"`
**Parameters**
> Signature: `sendRpnIncrement(parameter, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`parameter`** | String Array.<number> ||A string identifying the parameter's name (see above) or a two-position array specifying the two control bytes (0x65, 0x64) that identify the registered parameter.|
|[**`options`**] | object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
**Throws**:
* TypeError The specified registered parameter is invalid.
### `.sendRpnValue(...)` {#sendRpnValue}
Sets the specified MIDI registered parameter to the desired value. The value is defined with
up to two bytes of data (msb, lsb) that each can go from 0 to 127.
MIDI
[registered parameters](https://www.midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2)
extend the original list of control change messages. The MIDI 1.0 specification lists only a
limited number of them:
| Numbers | Function |
|--------------|--------------------------|
| (0x00, 0x00) | `pitchbendrange` |
| (0x00, 0x01) | `channelfinetuning` |
| (0x00, 0x02) | `channelcoarsetuning` |
| (0x00, 0x03) | `tuningprogram` |
| (0x00, 0x04) | `tuningbank` |
| (0x00, 0x05) | `modulationrange` |
| (0x3D, 0x00) | `azimuthangle` |
| (0x3D, 0x01) | `elevationangle` |
| (0x3D, 0x02) | `gain` |
| (0x3D, 0x03) | `distanceratio` |
| (0x3D, 0x04) | `maximumdistance` |
| (0x3D, 0x05) | `maximumdistancegain` |
| (0x3D, 0x06) | `referencedistanceratio` |
| (0x3D, 0x07) | `panspreadangle` |
| (0x3D, 0x08) | `rollangle` |
Note that the **Tuning Program** and **Tuning Bank** parameters are part of the *MIDI Tuning
Standard*, which is not widely implemented.
**Parameters**
> Signature: `sendRpnValue(rpn, [data], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`rpn`** | string Array.<number> ||A string identifying the parameter's name (see above) or a two-position array specifying the two control bytes (e.g. `[0x65, 0x64]`) that identify the registered parameter.|
|[**`data`**] | number Array.<number> |[]|An single integer or an array of integers with a maximum length of 2 specifying the desired data.|
|[**`options`**] | Object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
### `.sendTuningBank(...)` {#sendTuningBank}
Sets the MIDI tuning bank to use. Note that the **Tuning Bank** parameter is part of the
*MIDI Tuning Standard*, which is not widely implemented.
**Parameters**
> Signature: `sendTuningBank(value, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`value`** | number ||The desired tuning bank (integer between `0` and `127`).|
|[**`options`**] | Object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
**Throws**:
* `RangeError` : The bank value must be between 0 and 127.
### `.sendTuningProgram(...)` {#sendTuningProgram}
Sets the MIDI tuning program to use. Note that the **Tuning Program** parameter is part of the
*MIDI Tuning Standard*, which is not widely implemented.
**Parameters**
> Signature: `sendTuningProgram(value, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`value`** | number ||The desired tuning program (integer between `0` and `127`).|
|[**`options`**] | Object |{}||
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `OutputChannel`
Returns the `OutputChannel` object so methods can be chained.
**Throws**:
* `RangeError` : The program value must be between 0 and 127.
### `.stopNote(...)` {#stopNote}
Sends a **note off** message for the specified MIDI note number. The first parameter is the
note to stop. It can be a single value or an array of the following valid values:
- A MIDI note number (integer between `0` and `127`)
- A note identifier (e.g. `"C3"`, `"G#4"`, `"F-1"`, `"Db7"`)
- A [`Note`](Note) object
The execution of the **note off** command can be delayed by using the `time` property of the
`options` parameter.
**Parameters**
> Signature: `stopNote(note, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`note`** | number Note string Array.<number> Array.<Note> Array.<string> ||The note(s) to stop. The notes can be specified by using a MIDI note number (`0` - `127`), a note identifier (e.g. `C3`, `G#4`, `F-1`, `Db7`) or an array of the previous types. When using a note identifier, octave range must be between `-1` and `9`. The lowest note is `C-1` (MIDI note number `0`) and the highest note is `G9` (MIDI note number `127`).|
|[**`options`**] | Object |{}||
|[**`options.release`**] | number |0.5|The velocity at which to release the note (between `0` and `1`). If the `rawRelease` option is also defined, `rawRelease` will have priority. An invalid velocity value will silently trigger the default of `0.5`.|
|[**`options.rawRelease`**] | number |64|The velocity at which to release the note (between `0` and `127`). If the `release` option is also defined, `rawRelease` will have priority. An invalid velocity value will silently trigger the default of `64`.|
|[**`options.time`**] | number string |(now)|If `time` is a string prefixed with `"+"` and followed by a number, the message will be delayed by that many milliseconds. If the value is a positive number ([`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp)), the operation will be scheduled for that time. The current time can be retrieved with [`WebMidi.time`](WebMidi#time). If `options.time` is omitted, or in the past, the operation will be carried out as soon as possible.|
**Return Value**
> Returns: `Output`
Returns the `Output` object so methods can be chained.
### `.suspendEvent(...)` {#suspendEvent}
Suspends execution of all callbacks functions registered for the specified event type.
You can suspend execution of callbacks registered with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) by passing
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) to `suspendEvent()`. Beware that this
will not suspend all callbacks but only those registered with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT). While this may seem counter-intuitive
at first glance, it allows the selective suspension of global listeners while leaving other
listeners alone. If you truly want to suspends all callbacks for a specific
[`EventEmitter`](EventEmitter), simply set its `eventsSuspended` property to `true`.
**Parameters**
> Signature: `suspendEvent(event)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event name (or `EventEmitter.ANY_EVENT`) for which to suspend execution of all callback functions.|
### `.unsuspendEvent(...)` {#unsuspendEvent}
Resumes execution of all suspended callback functions registered for the specified event type.
You can resume execution of callbacks registered with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) by passing
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) to `unsuspendEvent()`. Beware that
this will not resume all callbacks but only those registered with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT). While this may seem
counter-intuitive, it allows the selective unsuspension of global listeners while leaving other
callbacks alone.
**Parameters**
> Signature: `unsuspendEvent(event)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event name (or `EventEmitter.ANY_EVENT`) for which to resume execution of all callback functions.|
### `.waitFor(...)` {#waitFor}
**Attributes**: async
The `waitFor()` method is an async function which returns a promise. The promise is fulfilled
when the specified event occurs. The event can be a regular event or
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) (if you want to resolve as soon as any
event is emitted).
If the `duration` option is set, the promise will only be fulfilled if the event is emitted
within the specified duration. If the event has not been fulfilled after the specified
duration, the promise is rejected. This makes it super easy to wait for an event and timeout
after a certain time if the event is not triggered.
**Parameters**
> Signature: `waitFor(event, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event to wait for|
|[**`options`**] | Object |{}||
|[**`options.duration`**] | number |Infinity|The number of milliseconds to wait before the promise is automatically rejected.|
================================================
FILE: website/api/classes/Utilities.md
================================================
# Utilities
The `Utilities` class contains general-purpose utility methods. All methods are static and
should be called using the class name. For example: `Utilities.getNoteDetails("C4")`.
**Since**: 3.0.0
***
## Properties
### `.isBrowser` {#isBrowser}
**Type**: boolean
Indicates whether the execution environment is a browser (`true`) or not (`false`)
### `.isNode` {#isNode}
**Type**: boolean
Indicates whether the execution environment is Node.js (`true`) or not (`false`)
***
## Methods
### `.buildNote(...)` {#buildNote}
**Since**: version 3.0.0
Converts the `input` parameter to a valid [`Note`](Note) object. The input usually is an
unsigned integer (0-127) or a note identifier (`"C4"`, `"G#5"`, etc.). If the input is a
[`Note`](Note) object, it will be returned as is.
If the input is a note number or identifier, it is possible to specify options by providing the
`options` parameter.
**Parameters**
> Signature: `buildNote([input], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`input`**] | number string Note |||
|[**`options`**] | object |{}||
|[**`options.duration`**] | number |Infinity|The number of milliseconds before the note should be explicitly stopped.|
|[**`options.attack`**] | number |0.5|The note's attack velocity as a float between 0 and 1. If you wish to use an integer between 0 and 127, use the `rawAttack` option instead. If both `attack` and `rawAttack` are specified, the latter has precedence.|
|[**`options.release`**] | number |0.5|The note's release velocity as a float between 0 and 1. If you wish to use an integer between 0 and 127, use the `rawRelease` option instead. If both `release` and `rawRelease` are specified, the latter has precedence.|
|[**`options.rawAttack`**] | number |64|The note's attack velocity as an integer between 0 and 127. If you wish to use a float between 0 and 1, use the `release` option instead. If both `attack` and `rawAttack` are specified, the latter has precedence.|
|[**`options.rawRelease`**] | number |64|The note's release velocity as an integer between 0 and 127. If you wish to use a float between 0 and 1, use the `release` option instead. If both `release` and `rawRelease` are specified, the latter has precedence.|
|[**`options.octaveOffset`**] | number |0|An integer to offset the octave by. **This is only used when the input value is a note identifier.**|
**Return Value**
> Returns: `Note`
**Attributes**: static
**Throws**:
* TypeError The input could not be parsed to a note
### `.buildNoteArray(...)` {#buildNoteArray}
**Since**: 3.0.0
Converts an input value, which can be an unsigned integer (0-127), a note identifier, a
[`Note`](Note) object or an array of the previous types, to an array of
[`Note`](Note) objects.
[`Note`](Note) objects are returned as is. For note numbers and identifiers, a
[`Note`](Note) object is created with the options specified. An error will be thrown when
encountering invalid input.
Note: if both the `attack` and `rawAttack` options are specified, the later has priority. The
same goes for `release` and `rawRelease`.
**Parameters**
> Signature: `buildNoteArray([notes], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`notes`**] | number string Note Array.<number> Array.<string> Array.<Note> |||
|[**`options`**] | object |{}||
|[**`options.duration`**] | number |Infinity|The number of milliseconds before the note should be explicitly stopped.|
|[**`options.attack`**] | number |0.5|The note's attack velocity as a float between 0 and 1. If you wish to use an integer between 0 and 127, use the `rawAttack` option instead. If both `attack` and `rawAttack` are specified, the latter has precedence.|
|[**`options.release`**] | number |0.5|The note's release velocity as a float between 0 and 1. If you wish to use an integer between 0 and 127, use the `rawRelease` option instead. If both `release` and `rawRelease` are specified, the latter has precedence.|
|[**`options.rawAttack`**] | number |64|The note's attack velocity as an integer between 0 and 127. If you wish to use a float between 0 and 1, use the `release` option instead. If both `attack` and `rawAttack` are specified, the latter has precedence.|
|[**`options.rawRelease`**] | number |64|The note's release velocity as an integer between 0 and 127. If you wish to use a float between 0 and 1, use the `release` option instead. If both `release` and `rawRelease` are specified, the latter has precedence.|
|[**`options.octaveOffset`**] | number |0|An integer to offset the octave by. **This is only used when the input value is a note identifier.**|
**Return Value**
> Returns: `Array.`
**Attributes**: static
**Throws**:
* TypeError An element could not be parsed as a note.
### `.from7bitToFloat(...)` {#from7bitToFloat}
Returns a number between 0 and 1 representing the ratio of the input value divided by 127 (7
bit). The returned value is restricted between 0 and 1 even if the input is greater than 127 or
smaller than 0.
Passing `Infinity` will return `1` and passing `-Infinity` will return `0`. Otherwise, when the
input value cannot be converted to an integer, the method returns 0.
**Parameters**
> Signature: `from7bitToFloat(value)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`value`** | number ||A positive integer between 0 and 127 (inclusive)|
**Return Value**
> Returns: `number`
A number between 0 and 1 (inclusive)
**Attributes**: static
### `.fromFloatTo7Bit(...)` {#fromFloatTo7Bit}
Returns an integer between 0 and 127 which is the result of multiplying the input value by
127. The input value should be a number between 0 and 1 (inclusively). The returned value is
restricted between 0 and 127 even if the input is greater than 1 or smaller than 0.
Passing `Infinity` will return `127` and passing `-Infinity` will return `0`. Otherwise, when
the input value cannot be converted to a number, the method returns 0.
**Parameters**
> Signature: `fromFloatTo7Bit(value)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`value`** | number ||A positive float between 0 and 1 (inclusive)|
**Return Value**
> Returns: `number`
A number between 0 and 127 (inclusive)
**Attributes**: static
### `.fromFloatToMsbLsb(...)` {#fromFloatToMsbLsb}
Extracts 7bit MSB and LSB values from the supplied float.
**Parameters**
> Signature: `fromFloatToMsbLsb(value)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`value`** | number ||A float between 0 and 1|
**Return Value**
> Returns: `Object`
**Attributes**: static
### `.fromMsbLsbToFloat(...)` {#fromMsbLsbToFloat}
Combines and converts MSB and LSB values (0-127) to a float between 0 and 1. The returned value
is within between 0 and 1 even if the result is greater than 1 or smaller than 0.
**Parameters**
> Signature: `fromMsbLsbToFloat(msb, [lsb])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`msb`** | number ||The most significant byte as a integer between 0 and 127.|
|[**`lsb`**] | number |0|The least significant byte as a integer between 0 and 127.|
**Return Value**
> Returns: `number`
A float between 0 and 1.
**Attributes**: static
### `.getCcNameByNumber(...)` {#getCcNameByNumber}
Returns the name of a control change message matching the specified number (0-127). Some valid
control change numbers do not have a specific name or purpose assigned in the MIDI
[spec](https://midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2).
In these cases, the method returns `controllerXXX` (where XXX is the number).
**Parameters**
> Signature: `getCcNameByNumber(number)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`number`** | number ||An integer (0-127) representing the control change message|
**Return Value**
> Returns: `string` or `undefined`
The matching control change name or `undefined` if no match was
found.
**Attributes**: static
### `.getCcNumberByName(...)` {#getCcNumberByName}
**Since**: 3.1
Returns the number of a control change message matching the specified name.
**Parameters**
> Signature: `getCcNumberByName(name)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`name`** | string ||A string representing the control change message|
**Return Value**
> Returns: `string` or `undefined`
The matching control change number or `undefined` if no match was
found.
**Attributes**: static
### `.getChannelModeByNumber(...)` {#getChannelModeByNumber}
**Since**: 2.0.0
Returns the channel mode name matching the specified number. If no match is found, the function
returns `false`.
**Parameters**
> Signature: `getChannelModeByNumber(number)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`number`** | number ||An integer representing the channel mode message (120-127)|
**Return Value**
> Returns: `string` or `false`
The name of the matching channel mode or `false` if no match could be
found.
**Attributes**: static
### `.getNoteDetails(...)` {#getNoteDetails}
**Since**: 3.0.0
Given a proper note identifier (`C#4`, `Gb-1`, etc.) or a valid MIDI note number (0-127), this
method returns an object containing broken down details about the specified note (uppercase
letter, accidental and octave).
When a number is specified, the translation to note is done using a value of 60 for middle C
(C4 = middle C).
**Parameters**
> Signature: `getNoteDetails(value)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`value`** | string number ||A note identifier A atring ("C#4", "Gb-1", etc.) or a MIDI note number (0-127).|
**Return Value**
> Returns: `Object`
**Attributes**: static
**Throws**:
* TypeError Invalid note identifier
### `.getPropertyByValue(...)` {#getPropertyByValue}
Returns the name of the first property of the supplied object whose value is equal to the one
supplied. If nothing is found, `undefined` is returned.
**Parameters**
> Signature: `getPropertyByValue(object, value)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`object`** | object ||The object to look for the property in.|
|**`value`** | * ||Any value that can be expected to be found in the object's properties.|
**Return Value**
> Returns: `string` or `undefined`
The name of the matching property or `undefined` if nothing is
found.
**Attributes**: static
### `.guessNoteNumber(...)` {#guessNoteNumber}
**Since**: 3.0.0
Returns a valid MIDI note number (0-127) given the specified input. The input usually is a
string containing a note identifier (`"C3"`, `"F#4"`, `"D-2"`, `"G8"`, etc.). If an integer
between 0 and 127 is passed, it will simply be returned as is (for convenience). Other strings
will be parsed for integer value, if possible.
If the input is an identifier, the resulting note number is offset by the `octaveOffset`
parameter. For example, if you pass in "C4" (note number 60) and the `octaveOffset` value is
-2, the resulting MIDI note number will be 36.
**Parameters**
> Signature: `guessNoteNumber(input, octaveOffset)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`input`** | string number ||A string or number to extract the MIDI note number from.|
|**`octaveOffset`** | number ||An integer to offset the octave by|
**Return Value**
> Returns: `number` or `false`
A valid MIDI note number (0-127) or `false` if the input could not
successfully be parsed to a note number.
**Attributes**: static
### `.offsetNumber(...)` {#offsetNumber}
Returns the supplied MIDI note number offset by the requested octave and semitone values. If
the calculated value is less than 0, 0 will be returned. If the calculated value is more than
127, 127 will be returned. If an invalid offset value is supplied, 0 will be used.
**Parameters**
> Signature: `offsetNumber(number, octaveOffset, octaveOffset)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`number`** | number ||The MIDI note to offset as an integer between 0 and 127.|
|**`octaveOffset`** | number |0|An integer to offset the note by (in octave)|
|**`octaveOffset`** | number ||An integer to offset the note by (in semitones)|
**Return Value**
> Returns: `number`
An integer between 0 and 127
**Attributes**: static
**Throws**:
* `Error` : Invalid note number
### `.sanitizeChannels(...)` {#sanitizeChannels}
**Since**: 3.0.0
Returns a sanitized array of valid MIDI channel numbers (1-16). The parameter should be a
single integer or an array of integers.
For backwards-compatibility, passing `undefined` as a parameter to this method results in all
channels being returned (1-16). Otherwise, parameters that cannot successfully be parsed to
integers between 1 and 16 are silently ignored.
**Parameters**
> Signature: `sanitizeChannels([channel])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`channel`**] | number Array.<number> ||An integer or an array of integers to parse as channel numbers.|
**Return Value**
> Returns: `Array.`
An array of 0 or more valid MIDI channel numbers.
**Attributes**: static
### `.toNoteIdentifier(...)` {#toNoteIdentifier}
**Since**: 3.0.0
Returns an identifier string representing a note name (with optional accidental) followed by an
octave number. The octave can be offset by using the `octaveOffset` parameter.
**Parameters**
> Signature: `toNoteIdentifier(number, octaveOffset)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`number`** | number ||The MIDI note number to convert to a note identifier|
|**`octaveOffset`** | number ||An offset to apply to the resulting octave|
**Return Value**
> Returns: `string`
**Attributes**: static
**Throws**:
* RangeError Invalid note number
* RangeError Invalid octaveOffset value
### `.toNoteNumber(...)` {#toNoteNumber}
**Since**: 3.0.0
Returns a MIDI note number matching the identifier passed in the form of a string. The
identifier must include the octave number. The identifier also optionally include a sharp (#),
a double sharp (##), a flat (b) or a double flat (bb) symbol. For example, these are all valid
identifiers: C5, G4, D#-1, F0, Gb7, Eb-1, Abb4, B##6, etc.
When converting note identifiers to numbers, C4 is considered to be middle C (MIDI note number
60) as per the scientific pitch notation standard.
The resulting note number can be offset by using the `octaveOffset` parameter.
**Parameters**
> Signature: `toNoteNumber(identifier, [octaveOffset])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`identifier`** | string ||The identifier in the form of a letter, followed by an optional "#", "##", "b" or "bb" followed by the octave number. For exemple: C5, G4, D#-1, F0, Gb7, Eb-1, Abb4, B##6, etc.|
|[**`octaveOffset`**] | number |0|A integer to offset the octave by.|
**Return Value**
> Returns: `number`
The MIDI note number (an integer between 0 and 127).
**Attributes**: static
**Throws**:
* RangeError Invalid 'octaveOffset' value
* TypeError Invalid note identifier
### `.toTimestamp(...)` {#toTimestamp}
**Since**: 3.0.0
Returns a valid timestamp, relative to the navigation start of the document, derived from the
`time` parameter. If the parameter is a string starting with the "+" sign and followed by a
number, the resulting timestamp will be the sum of the current timestamp plus that number. If
the parameter is a positive number, it will be returned as is. Otherwise, false will be
returned.
**Parameters**
> Signature: `toTimestamp([time])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`time`**] | number string ||The time string (e.g. `"+2000"`) or number to parse|
**Return Value**
> Returns: `number` or `false`
A positive number or `false` (if the time cannot be converted)
**Attributes**: static
================================================
FILE: website/api/classes/WebMidi.md
================================================
# WebMidi
The `WebMidi` object makes it easier to work with the low-level Web MIDI API. Basically, it
simplifies sending outgoing MIDI messages and reacting to incoming MIDI messages.
When using the WebMidi.js library, you should know that the `WebMidi` class has already been
instantiated. You cannot instantiate it yourself. If you use the **IIFE** version, you should
simply use the global object called `WebMidi`. If you use the **CJS** (CommonJS) or **ESM** (ES6
module) version, you get an already-instantiated object when you import the module.
**Extends**: [`EventEmitter`](EventEmitter)
**Fires**: [`connected`](#event:connected), [`disabled`](#event:disabled), [`disconnected`](#event:disconnected), [`enabled`](#event:enabled), [`error`](#event:error), [`midiaccessgranted`](#event:midiaccessgranted), [`portschanged`](#event:portschanged)
### `Constructor`
The WebMidi class is a singleton and you cannot instantiate it directly. It has already been
instantiated for you.
***
## Properties
### `.defaults` {#defaults}
**Type**: object
Object containing system-wide default values that can be changed to customize how the library
works.
**Properties**
| Property | Type | Description |
| ------------ | ------------ | ------------ |
|**`defaults.note`** |object|Default values relating to note|
|**`defaults.note.attack`** |number|A number between 0 and 127 representing the default attack velocity of notes. Initial value is 64.|
|**`defaults.note.release`** |number|A number between 0 and 127 representing the default release velocity of notes. Initial value is 64.|
|**`defaults.note.duration`** |number|A number representing the default duration of notes (in seconds). Initial value is Infinity.|
### `.enabled` {#enabled}
**Type**: boolean
**Attributes**: read-only
Indicates whether access to the host's MIDI subsystem is active or not.
### `.eventCount` {#eventCount}
**Type**: number
**Attributes**: read-only
The number of unique events that have registered listeners.
Note: this excludes global events registered with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) because they are not tied to a
specific event.
### `.eventMap` {#eventMap}
**Type**: Object
**Attributes**: read-only
An object containing a property for each event with at least one registered listener. Each
event property contains an array of all the [`Listener`](Listener) objects registered
for the event.
### `.eventNames` {#eventNames}
**Type**: Array.<string>
**Attributes**: read-only
An array of all the unique event names for which the emitter has at least one registered
listener.
Note: this excludes global events registered with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) because they are not tied to a
specific event.
### `.eventsSuspended` {#eventsSuspended}
**Type**: boolean
Whether or not the execution of callbacks is currently suspended for this emitter.
### `.flavour` {#flavour}
**Since**: 3.0.25
**Type**: string
**Attributes**: read-only
The flavour of the library. Can be one of:
* `esm`: ECMAScript Module
* `cjs`: CommonJS Module
* `iife`: Immediately-Invoked Function Expression
### `.inputs` {#inputs}
**Type**: Array.<Input>
**Attributes**: read-only
An array of all currently available MIDI inputs.
### `.interface` {#interface}
**Type**: MIDIAccess
**Attributes**: read-only
The [`MIDIAccess`](https://developer.mozilla.org/en-US/docs/Web/API/MIDIAccess)
instance used to talk to the lower-level Web MIDI API. This should not be used directly
unless you know what you are doing.
### `.octaveOffset` {#octaveOffset}
**Since**: 2.1
**Type**: number
An integer to offset the octave of notes received from external devices or sent to external
devices.
When a MIDI message comes in on an input channel the reported note name will be offset. For
example, if the `octaveOffset` is set to `-1` and a [`"noteon"`](InputChannel#event:noteon)
message with MIDI number 60 comes in, the note will be reported as C3 (instead of C4).
By the same token, when [`OutputChannel.playNote()`](OutputChannel#playNote) is called, the
MIDI note number being sent will be offset. If `octaveOffset` is set to `-1`, the MIDI note
number sent will be 72 (instead of 60).
### `.outputs` {#outputs}
**Type**: Array.<Output>
**Attributes**: read-only
An array of all currently available MIDI outputs as [`Output`](Output) objects.
### `.supported` {#supported}
**Type**: boolean
**Attributes**: read-only
Indicates whether the environment provides support for the Web MIDI API or not.
**Note**: in environments that do not offer built-in MIDI support, this will report `true` if
the
[`navigator.requestMIDIAccess`](https://developer.mozilla.org/en-US/docs/Web/API/MIDIAccess)
function is available. For example, if you have installed WebMIDIAPIShim.js but no plugin, this
property will be `true` even though actual support might not be there.
### `.sysexEnabled` {#sysexEnabled}
**Type**: boolean
**Attributes**: read-only
Indicates whether MIDI system exclusive messages have been activated when WebMidi.js was
enabled via the [`enable()`](#enable) method.
### `.time` {#time}
**Type**: DOMHighResTimeStamp
**Attributes**: read-only
The elapsed time, in milliseconds, since the time
[origin](https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp#The_time_origin).
Said simply, it is the number of milliseconds that passed since the page was loaded. Being a
floating-point number, it has sub-millisecond accuracy. According to the
[documentation](https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp), the
time should be accurate to 5 µs (microseconds). However, due to various constraints, the
browser might only be accurate to one millisecond.
Note: `WebMidi.time` is simply an alias to `performance.now()`.
### `.validation` {#validation}
**Type**: boolean
Indicates whether argument validation and backwards-compatibility checks are performed
throughout the WebMidi.js library for object methods and property setters.
This is an advanced setting that should be used carefully. Setting `validation` to `false`
improves performance but should only be done once the project has been thoroughly tested with
`validation` turned on.
### `.version` {#version}
**Type**: string
**Attributes**: read-only
The version of the library as a [semver](https://semver.org/) string.
***
## Methods
### `.addListener(...)` {#addListener}
Adds a listener for the specified event. It returns the [`Listener`](Listener) object
that was created and attached to the event.
To attach a global listener that will be triggered for any events, use
[`EventEmitter.ANY_EVENT`](#ANY_EVENT) as the first parameter. Note that a global
listener will also be triggered by non-registered events.
**Parameters**
> Signature: `addListener(event, callback, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event to listen to.|
|**`callback`** | EventEmitter~callback ||The callback function to execute when the event occurs.|
|[**`options`**] | Object |{}||
|[**`options.context`**] | Object |this|The value of `this` in the callback function.|
|[**`options.prepend`**] | boolean |false|Whether the listener should be added at the beginning of the listeners array and thus executed first.|
|[**`options.duration`**] | number |Infinity|The number of milliseconds before the listener automatically expires.|
|[**`options.remaining`**] | number |Infinity|The number of times after which the callback should automatically be removed.|
|[**`options.arguments`**] | array ||An array of arguments which will be passed separately to the callback function. This array is stored in the [`arguments`](Listener#arguments) property of the [`Listener`](Listener) object and can be retrieved or modified as desired.|
**Return Value**
> Returns: `Listener`
The newly created [`Listener`](Listener) object.
**Throws**:
* `TypeError` : The `event` parameter must be a string or
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT).
* `TypeError` : The `callback` parameter must be a function.
### `.addOneTimeListener(...)` {#addOneTimeListener}
Adds a one-time listener for the specified event. The listener will be executed once and then
destroyed. It returns the [`Listener`](Listener) object that was created and attached
to the event.
To attach a global listener that will be triggered for any events, use
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) as the first parameter. Note that a
global listener will also be triggered by non-registered events.
**Parameters**
> Signature: `addOneTimeListener(event, callback, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event to listen to|
|**`callback`** | EventEmitter~callback ||The callback function to execute when the event occurs|
|[**`options`**] | Object |{}||
|[**`options.context`**] | Object |this|The context to invoke the callback function in.|
|[**`options.prepend`**] | boolean |false|Whether the listener should be added at the beginning of the listeners array and thus executed first.|
|[**`options.duration`**] | number |Infinity|The number of milliseconds before the listener automatically expires.|
|[**`options.arguments`**] | array ||An array of arguments which will be passed separately to the callback function. This array is stored in the [`arguments`](Listener#arguments) property of the [`Listener`](Listener) object and can be retrieved or modified as desired.|
**Return Value**
> Returns: `Listener`
The newly created [`Listener`](Listener) object.
**Throws**:
* `TypeError` : The `event` parameter must be a string or
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT).
* `TypeError` : The `callback` parameter must be a function.
### `.disable()` {#disable}
**Since**: 2.0.0
**Attributes**: async
Completely disables **WebMidi.js** by unlinking the MIDI subsystem's interface and closing all
[`Input`](Input) and [`Output`](Output) objects that may have been opened. This also means that
listeners added to [`Input`](Input) objects, [`Output`](Output) objects or to `WebMidi` itself
are also destroyed.
**Return Value**
> Returns: `Promise.`
**Throws**:
* `Error` : The Web MIDI API is not supported by your environment.
### `.emit(...)` {#emit}
Executes the callback function of all the [`Listener`](Listener) objects registered for
a given event. The callback functions are passed the additional arguments passed to `emit()`
(if any) followed by the arguments present in the [`arguments`](Listener#arguments) property of
the [`Listener`](Listener) object (if any).
If the [`eventsSuspended`](#eventsSuspended) property is `true` or the
[`Listener.suspended`](Listener#suspended) property is `true`, the callback functions
will not be executed.
This function returns an array containing the return values of each of the callbacks.
It should be noted that the regular listeners are triggered first followed by the global
listeners (those added with [`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT)).
**Parameters**
> Signature: `emit(event, ...args)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string ||The event|
|**`args`** | * ||Arbitrary number of arguments to pass along to the callback functions|
**Return Value**
> Returns: `Array`
An array containing the return value of each of the executed listener
functions.
**Throws**:
* `TypeError` : The `event` parameter must be a string.
### `.enable(...)` {#enable}
**Attributes**: async
Checks if the Web MIDI API is available in the current environment and then tries to connect to
the host's MIDI subsystem. This is an asynchronous operation and it causes a security prompt to
be displayed to the user.
To enable the use of MIDI system exclusive messages, the `sysex` option should be set to
`true`. However, under some environments (e.g. Jazz-Plugin), the `sysex` option is ignored
and system exclusive messages are always enabled. You can check the
[`sysexEnabled`](#sysexEnabled) property to confirm.
To enable access to software synthesizers available on the host, you would set the `software`
option to `true`. However, this option is only there to future-proof the library as support for
software synths has not yet been implemented in any browser (as of September 2021).
By the way, if you call the [`enable()`](#enable) method while WebMidi.js is already enabled,
the callback function will be executed (if any), the promise will resolve but the events
([`"midiaccessgranted"`](#event:midiaccessgranted), [`"connected"`](#event:connected) and
[`"enabled"`](#event:enabled)) will not be fired.
There are 3 ways to execute code after `WebMidi` has been enabled:
- Pass a callback function in the `options`
- Listen to the [`"enabled"`](#event:enabled) event
- Wait for the promise to resolve
In order, this is what happens towards the end of the enabling process:
1. [`"midiaccessgranted"`](#event:midiaccessgranted) event is triggered once the user has
granted access to use MIDI.
2. [`"connected"`](#event:connected) events are triggered (for each available input and output)
3. [`"enabled"`](#event:enabled) event is triggered when WebMidi.js is fully ready
4. specified callback (if any) is executed
5. promise is resolved and fulfilled with the `WebMidi` object.
**Important note**: starting with Chrome v77, a page using Web MIDI API must be hosted on a
secure origin (`https://`, `localhost` or `file:///`) and the user will always be prompted to
authorize the operation (no matter if the `sysex` option is `true` or not).
##### Example
```js
// Enabling WebMidi and using the promise
WebMidi.enable().then(() => {
console.log("WebMidi.js has been enabled!");
})
```
**Parameters**
> Signature: `enable([options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`options`**] | object |||
|[**`options.callback`**] | function ||A function to execute once the operation completes. This function will receive an `Error` object if enabling the Web MIDI API failed.|
|[**`options.sysex`**] | boolean |false|Whether to enable MIDI system exclusive messages or not.|
|[**`options.validation`**] | boolean |true|Whether to enable library-wide validation of method arguments and setter values. This is an advanced setting that should be used carefully. Setting [`validation`](#validation) to `false` improves performance but should only be done once the project has been thoroughly tested with [`validation`](#validation) turned on.|
|[**`options.software`**] | boolean |false|Whether to request access to software synthesizers on the host system. This is part of the spec but has not yet been implemented by most browsers as of April 2020.|
|[**`options.requestMIDIAccessFunction`**] | function ||A custom function to use to return the MIDIAccess object. This is useful if you want to use a polyfill for the Web MIDI API or if you want to use a custom implementation of the Web MIDI API - probably for testing purposes.|
**Return Value**
> Returns: `Promise.`
The promise is fulfilled with the `WebMidi` object for
chainability
**Throws**:
* `Error` : The Web MIDI API is not supported in your environment.
* `Error` : Jazz-Plugin must be installed to use WebMIDIAPIShim.
### `.getInputById(...)` {#getInputById}
**Since**: 2.0.0
Returns the [`Input`](Input) object that matches the specified ID string or `false` if no
matching input is found. As per the Web MIDI API specification, IDs are strings (not integers).
Please note that IDs change from one host to another. For example, Chrome does not use the same
kind of IDs as Jazz-Plugin.
**Parameters**
> Signature: `getInputById(id, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`id`** | string ||The ID string of the input. IDs can be viewed by looking at the [`WebMidi.inputs`](WebMidi#inputs) array. Even though they sometimes look like integers, IDs are strings.|
|[**`options`**] | object |||
|[**`options.disconnected`**] | boolean ||Whether to retrieve a disconnected input|
**Return Value**
> Returns: `Input`
An [`Input`](Input) object matching the specified ID string or `undefined`
if no matching input can be found.
**Throws**:
* `Error` : WebMidi is not enabled.
### `.getInputByName(...)` {#getInputByName}
**Since**: 2.0.0
Returns the first [`Input`](Input) object whose name **contains** the specified string. Note
that the port names change from one environment to another. For example, Chrome does not report
input names in the same way as the Jazz-Plugin does.
**Parameters**
> Signature: `getInputByName(name, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`name`** | string ||The non-empty string to look for within the name of MIDI inputs (such as those visible in the [inputs](WebMidi#inputs) array).|
|[**`options`**] | object |||
|[**`options.disconnected`**] | boolean ||Whether to retrieve a disconnected input|
**Return Value**
> Returns: `Input`
The [`Input`](Input) that was found or `undefined` if no input contained the
specified name.
**Throws**:
* `Error` : WebMidi is not enabled.
### `.getListenerCount(...)` {#getListenerCount}
Returns the number of listeners registered for a specific event.
Please note that global events (those added with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT)) do not count towards the remaining
number for a "regular" event. To get the number of global listeners, specifically use
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) as the parameter.
**Parameters**
> Signature: `getListenerCount(event)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event which is usually a string but can also be the special [`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) symbol.|
**Return Value**
> Returns: `number`
An integer representing the number of listeners registered for the specified
event.
### `.getListeners(...)` {#getListeners}
Returns an array of all the [`Listener`](Listener) objects that have been registered for
a specific event.
Please note that global events (those added with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT)) are not returned for "regular"
events. To get the list of global listeners, specifically use
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) as the parameter.
**Parameters**
> Signature: `getListeners(event)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event to get listeners for.|
**Return Value**
> Returns: `Array.`
An array of [`Listener`](Listener) objects.
### `.getOutputById(...)` {#getOutputById}
**Since**: 2.0.0
Returns the [`Output`](Output) object that matches the specified ID string or `false` if no
matching output is found. As per the Web MIDI API specification, IDs are strings (not
integers).
Please note that IDs change from one host to another. For example, Chrome does not use the same
kind of IDs as Jazz-Plugin.
**Parameters**
> Signature: `getOutputById(id, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`id`** | string ||The ID string of the port. IDs can be viewed by looking at the [`WebMidi.outputs`](WebMidi#outputs) array.|
|[**`options`**] | object |||
|[**`options.disconnected`**] | boolean ||Whether to retrieve a disconnected output|
**Return Value**
> Returns: `Output`
An [`Output`](Output) object matching the specified ID string. If no
matching output can be found, the method returns `undefined`.
**Throws**:
* `Error` : WebMidi is not enabled.
### `.getOutputByName(...)` {#getOutputByName}
**Since**: 2.0.0
Returns the first [`Output`](Output) object whose name **contains** the specified string. Note
that the port names change from one environment to another. For example, Chrome does not report
input names in the same way as the Jazz-Plugin does.
**Parameters**
> Signature: `getOutputByName(name, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`name`** | string ||The non-empty string to look for within the name of MIDI inputs (such as those visible in the [`outputs`](#outputs) array).|
|[**`options`**] | object |||
|[**`options.disconnected`**] | boolean ||Whether to retrieve a disconnected output|
**Return Value**
> Returns: `Output`
The [`Output`](Output) that was found or `undefined` if no output matched
the specified name.
**Throws**:
* `Error` : WebMidi is not enabled.
### `.hasListener(...)` {#hasListener}
Returns `true` if the specified event has at least one registered listener. If no event is
specified, the method returns `true` if any event has at least one listener registered (this
includes global listeners registered to
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT)).
Note: to specifically check for global listeners added with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT), use
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) as the parameter.
**Parameters**
> Signature: `hasListener([event], [callback])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`event`**] | string Symbol |(any event)|The event to check|
|[**`callback`**] | function Listener |(any callback)|The actual function that was added to the event or the [Listener](Listener) object returned by `addListener()`.|
**Return Value**
> Returns: `boolean`
### `.removeListener(...)` {#removeListener}
Removes all the listeners that were added to the object upon which the method is called and
that match the specified criterias. If no parameters are passed, all listeners added to this
object will be removed. If only the `event` parameter is passed, all listeners for that event
will be removed from that object. You can remove global listeners by using
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) as the first parameter.
To use more granular options, you must at least define the `event`. Then, you can specify the
callback to match or one or more of the additional options.
**Parameters**
> Signature: `removeListener([event], [callback], [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|[**`event`**] | string ||The event name.|
|[**`callback`**] | EventEmitter~callback ||Only remove the listeners that match this exact callback function.|
|[**`options`**] | Object |||
|[**`options.context`**] | * ||Only remove the listeners that have this exact context.|
|[**`options.remaining`**] | number ||Only remove the listener if it has exactly that many remaining times to be executed.|
### `.suspendEvent(...)` {#suspendEvent}
Suspends execution of all callbacks functions registered for the specified event type.
You can suspend execution of callbacks registered with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) by passing
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) to `suspendEvent()`. Beware that this
will not suspend all callbacks but only those registered with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT). While this may seem counter-intuitive
at first glance, it allows the selective suspension of global listeners while leaving other
listeners alone. If you truly want to suspends all callbacks for a specific
[`EventEmitter`](EventEmitter), simply set its `eventsSuspended` property to `true`.
**Parameters**
> Signature: `suspendEvent(event)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event name (or `EventEmitter.ANY_EVENT`) for which to suspend execution of all callback functions.|
### `.unsuspendEvent(...)` {#unsuspendEvent}
Resumes execution of all suspended callback functions registered for the specified event type.
You can resume execution of callbacks registered with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) by passing
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) to `unsuspendEvent()`. Beware that
this will not resume all callbacks but only those registered with
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT). While this may seem
counter-intuitive, it allows the selective unsuspension of global listeners while leaving other
callbacks alone.
**Parameters**
> Signature: `unsuspendEvent(event)`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event name (or `EventEmitter.ANY_EVENT`) for which to resume execution of all callback functions.|
### `.waitFor(...)` {#waitFor}
**Attributes**: async
The `waitFor()` method is an async function which returns a promise. The promise is fulfilled
when the specified event occurs. The event can be a regular event or
[`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT) (if you want to resolve as soon as any
event is emitted).
If the `duration` option is set, the promise will only be fulfilled if the event is emitted
within the specified duration. If the event has not been fulfilled after the specified
duration, the promise is rejected. This makes it super easy to wait for an event and timeout
after a certain time if the event is not triggered.
**Parameters**
> Signature: `waitFor(event, [options])`
| Parameter | Type(s) | Default | Description |
| ------------ | ------------ | ------------ | ------------ |
|**`event`** | string Symbol ||The event to wait for|
|[**`options`**] | Object |{}||
|[**`options.duration`**] | number |Infinity|The number of milliseconds to wait before the promise is automatically rejected.|
***
## Events
### `connected` {#event-connected}
Event emitted when an [`Input`](Input) or [`Output`](Output) becomes available. This event is
typically fired whenever a MIDI device is plugged in. Please note that it may fire several
times if a device possesses multiple inputs and/or outputs (which is often the case).
**Event Properties**
| Property | Type | Description |
| ------------------------ | ------------------------ | ------------------------ |
|**`timestamp`** |number|The moment (DOMHighResTimeStamp) when the event occurred (in milliseconds since the navigation start of the document).|
|**`type`** |string|`connected`|
|**`target`** |WebMidi|The object to which the listener was originally added (`WebMidi`)|
|**`port`** |Input|The [`Input`](Input) or [`Output`](Output) object that triggered the event.|
### `disabled` {#event-disabled}
Event emitted once `WebMidi` has been successfully disabled.
**Event Properties**
| Property | Type | Description |
| ------------------------ | ------------------------ | ------------------------ |
|**`timestamp`** |DOMHighResTimeStamp|The moment when the event occurred (in milliseconds since the navigation start of the document).|
|**`target`** |WebMidi|The object that triggered the event|
|**`type`** |string|`"disabled"`|
### `disconnected` {#event-disconnected}
Event emitted when an [`Input`](Input) or [`Output`](Output) becomes unavailable. This event
is typically fired whenever a MIDI device is unplugged. Please note that it may fire several
times if a device possesses multiple inputs and/or outputs (which is often the case).
**Event Properties**
| Property | Type | Description |
| ------------------------ | ------------------------ | ------------------------ |
|**`timestamp`** |DOMHighResTimeStamp|The moment when the event occurred (in milliseconds since the navigation start of the document).|
|**`type`** |string|`disconnected`|
|**`target`** |WebMidi|The object to which the listener was originally added (`WebMidi`)|
|**`port`** |Input|The [`Input`](Input) or [`Output`](Output) object that triggered the event.|
### `enabled` {#event-enabled}
Event emitted once `WebMidi` has been fully enabled
**Event Properties**
| Property | Type | Description |
| ------------------------ | ------------------------ | ------------------------ |
|**`timestamp`** |DOMHighResTimeStamp|The moment when the event occurred (in milliseconds since the navigation start of the document).|
|**`target`** |WebMidi|The object that triggered the event|
|**`type`** |string|`"enabled"`|
### `error` {#event-error}
Event emitted when an error occurs trying to enable `WebMidi`
**Event Properties**
| Property | Type | Description |
| ------------------------ | ------------------------ | ------------------------ |
|**`timestamp`** |DOMHighResTimeStamp|The moment when the event occurred (in milliseconds since the navigation start of the document).|
|**`target`** |WebMidi|The object that triggered the event|
|**`type`** |string|`error`|
|**`error`** |*|Actual error that occurred|
### `midiaccessgranted` {#event-midiaccessgranted}
Event emitted once the MIDI interface has been successfully created (which implies user has
granted access to MIDI).
**Event Properties**
| Property | Type | Description |
| ------------------------ | ------------------------ | ------------------------ |
|**`timestamp`** |DOMHighResTimeStamp|The moment when the event occurred (in milliseconds since the navigation start of the document).|
|**`target`** |WebMidi|The object that triggered the event|
|**`type`** |string|`midiaccessgranted`|
### `portschanged` {#event-portschanged}
Event emitted when an [`Input`](Input) or [`Output`](Output) port is connected or
disconnected. This event is typically fired whenever a MIDI device is plugged in or
unplugged. Please note that it may fire several times if a device possesses multiple inputs
and/or outputs (which is often the case).
**Since**: 3.0.2
**Event Properties**
| Property | Type | Description |
| ------------------------ | ------------------------ | ------------------------ |
|**`timestamp`** |number|The moment (DOMHighResTimeStamp) when the event occurred (in milliseconds since the navigation start of the document).|
|**`type`** |string|`portschanged`|
|**`target`** |WebMidi|The object to which the listener was originally added (`WebMidi`)|
|**`port`** |Input|The [`Input`](Input) or [`Output`](Output) object that triggered the event.|
================================================
FILE: website/api/classes/_category_.json
================================================
{
"label": "Classes & Objects",
"position": 2,
"collapsed": false
}
================================================
FILE: website/api/index.md
================================================
---
sidebar_position: 1
title: API Documentation
slug: /
---
# API Documentation
## Core Classes
These classes are the ones developers are most likely to be dealing with while working on their MIDI
projects. Note that all these classes are pre-instantiated within WEBMIDI.js.
* [**WebMidi**](./classes/WebMidi.md)
* [**Input**](./classes/Input.md)
* [**InputChannel**](./classes/InputChannel.md)
* [**Output**](./classes/Output.md)
* [**OutputChannel**](./classes/OutputChannel.md)
* [**Message**](./classes/Message.md)
The exception are the [`Note`](./classes/Note.md) class which you can instantiate when you need
to store a musical note and the [`Forwarder`](./classes/Forwarder.md) class used to forward
messages from an input to an output:
* [**Note**](./classes/Note.md)
* [**Forwarder**](./classes/Forwarder.md)
## Support Classes
These classes are mostly for internal use, but you might find them useful in some contexts. The
[`Enumerations`](./classes/Enumerations.md) class contains static enums of MIDI messages,
registered parameters, etc. The [`Utilities`](./classes/Utilities.md) class contains various
static methods.
* [**Enumerations**](./classes/Enumerations.md)
* [**Utilities**](./classes/Utilities.md)
## DjipEvents Classes
The `EventEmitter` and `Listener` classes from the
[DjipEvents](https://github.com/djipco/djipevents) module are extended by various WEBMIDI.js
classes. So, in the interest of completeness, we include their full documentation here and
cross-reference it with the core classes
* [**EventEmitter**](./classes/EventEmitter.md)
* [**Listener**](./classes/Listener.md)
================================================
FILE: website/babel.config.js
================================================
module.exports = {
presets: [require.resolve("@docusaurus/core/lib/babel/preset")],
};
================================================
FILE: website/blog/2021-12-01/version-3-has-been-released.md
================================================
---
title: WEBMIDI.js v3 is available now!
description: Version 3 of WEBMIDI.js, the library that lets you interact with your MIDI instruments and devices, is now available. It features Node.js and TypeScript support, various new objects (Message, Note, etc.) and a completely rewritten engine.
authors:
- name: Jean-Philippe Côté
title: Creator of WEBMIDI.js
url: /about
image_url: /img/blog/jean-philippe_cote.jpg
hide_table_of_contents: false
keywords: [web midi api, music, instrument, midi, javascript]
image: /img/blog/2021-12-01/webmidijs-is-out.png
---
After a lot of work and testing, I am happy to announce today that version 3 of the go-to MIDI
library for JavaScript has been released! You can [try it out](https://webmidijs.org/docs) right
now!

### About WEBMIDI.js
[**WEBMIDI.js**](https://webmidijs.org) exists to make it easier for developers to use the
[Web MIDI API](https://webaudio.github.io/web-midi-api/). The Web MIDI API is a really exciting
addition to the web platform allowing a web page to interact directly with MIDI musical instruments
and devices.
While great, many developers will find the API to be too low-level for their needs. Having to
perform binary arithmetic or needing to constantly refer to the 300-page MIDI spec is no fun (trust
me on this!). So, the goal for [**WEBMIDI.js**](https://webmidijs.org) is to get developers and
musicians started with their web-based MIDI projects as efficiently as possible.
As of today, [**WEBMIDI.js**](https://webmidijs.org) generates over **744K hits a month on
[jsDelivr](https://www.jsdelivr.com/package/npm/webmidi)**. It is **downloaded over 4.4K times a
month on [NPM](https://www.npmjs.com/package/webmidi)** and has been **starred by over
[1000 developers](https://github.com/djipco/webmidi/stargazers)** on GitHub. Not too bad for a niche
library that grew out of a personal passion project. 😀
### About the New Version 3
Version 3 has been rewritten from scratch to make it both future-proof and backwards-compatible. It
uses a modern development paradigm and now has its own dedicated website at
[**webmidijs.org**](https://webmidijs.org). The library offers numerous new features such as:
* Long-awaited **support for Node.js** (thanks to the [jzz](https://www.npmjs.com/package/jzz)
module by Jazz-Soft). The exact same code can be used in supported browsers and in Node.js.
* Distribution in **3 flavours**: **ESM** (ECMAScript module for modern browsers), **CJS** (CommonJS
module for Node.js) and **IIFE** (Immediately Invoked Function Expression for legacy browsers and
_ad hoc_ usage).
* **TypeScript Support**. Every new release includes a TypeScript definition file for CJS and ESM in
the `dist` directory.
* **New `InputChannel` and `OutputChannel`** objects. You can now work with a single MIDI channel if
that's appropriate for your needs.
* **New `Note` object**. Makes it easier to work with notes and pass them around from one method to
the next.
* **New `Message` object** that allows easier routing of MIDI messages, including the ability to
automatically **forward inbound MIDI messages** to one, or more, outputs (much like the good ol'
physical THRU port).
* Improved support for **system exclusive** (sysex) messages.
* **Support for promises** while preserving legacy callback support.
* Improved **support for RPN/NRPN messages**.
* Addition of **hundreds of unit tests** to make sure the library remains stable at all times.
* and lots more...
### Try it out!
The [documentation section](https://webmidijs.org/docs) of the new website has all the information
to get you started. If you need help, you can exchange with fellow users and myself using the
[GitHub Discussions](https://github.com/djipco/webmidi/discussions) platform.
If you use the library and find it useful, please think about
[sponsoring](https://github.com/sponsors/djipco) 💜 the project.
Cheers!
Jean-Philippe
================================================
FILE: website/docs/archives/_category_.json
================================================
{
"label": "Previous Versions",
"position": 40
}
================================================
FILE: website/docs/archives/v1.md
================================================
---
sidebar_position: 2
slug: /archives/v1
sidebar_label: Version 1.0.0-beta.15
---
# Documentation for v1.0.0-beta.15
:::caution
There is no documentation per se for version 1.0.0-beta.15, However, you can still consult an
archived copy of the full
[API Reference](https://djipco.github.io/webmidi/archives/api/v1/classes/WebMidi.html).
:::
================================================
FILE: website/docs/archives/v2.md
================================================
---
sidebar_position: 1
slug: /archives/v2
sidebar_label: Version 2.5.3
---
# Documentation for v2.5.3
:::caution
Version 2.5.3 will be the last version of the 2.x branch. This documentation and the 2.5.3 version
will not be updated after 2021.
:::
## Browser Support
This library works in all browsers that natively support the
[Web MIDI API](https://webaudio.github.io/web-midi-api/). Currently (2021), the following browsers
have built-in support:
* Chrome (macOS, GNU/Linux, Android & Windows)
* Opera (macOS, GNU/Linux, Windows)
* Android WebView component (KitKat and above)
* Edge (Windows)
It is also possible to use this library in other browsers if you install version 1.4+ of
[Jazz-Plugin](http://jazz-soft.net/) together with the
[WebMIDIAPIShim](http://cwilso.github.io/WebMIDIAPIShim/) polyfill. This combination provides
support for the following additional browsers:
* Firefox v51 **or less** (Mac, GNU/Linux & Windows)
* Safari (macOS)
* Internet Explorer (Windows)
>For details on how to use **WebMidi.js** with the Jazz-Plugin (and WebMIDIAPIShim, please skip
>ahead to the [Using WebMidi.js with the Jazz-Plugin](#using-webmidijs-with-the-jazz-plugin)
>section.
For **Firefox v52+ support**, you need to install two extensions made by
[Jazz-Soft](https://www.jazz-soft.net/):
* [Jazz-MIDI extension](https://addons.mozilla.org/en-US/firefox/addon/jazz-midi/) v1.5.1+
* [Web MIDI API extension](https://addons.mozilla.org/en-US/firefox/addon/web-midi-api/)
Early tests show that WebMidi.js is working in Firefox when both these extensions installed. Further
testing will need to be done but it looks very promising.
I invite you to communicate with the Firefox and Safari teams to let them know how having native Web
MIDI support is important for you:
* Safari: https://bugs.webkit.org/show_bug.cgi?id=107250
* Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=836897
Note that, in 2020, [Apple has announced](https://webkit.org/tracking-prevention/) that they would not
implement the Web MIDI API (and a host of other APIs) in Safari over fingerprinting concerns.
## Node.js Support
There is no official Node.js support in WebMidi.js 2.5.x. Version 3 and above offer full Node.js
support.
## TypeScript Support
TypeScript type definitions have been tentatively added to WebMidi.js with version 2.3 (thanks to
[mmmveggies](https://www.github.com/mmmveggies)) but it should be noted that **TypeScript IS NOT
officially supported** in version 2.5.x. TypeScript is supported in version 3 and above.
Despite the absence of official suuport, this has been reported to work in v2.5.3:
```ts
import WebMidi from "webmidi";
WebMidi.enable(...);
```
Or (thanks to [michaelcaterisano](https://www.github.com/michaelcaterisano)):
```ts
const WebMidi: import("webmidi").WebMidi = require("webmidi");
```
You can also import the types, if you need them:
```ts
import WebMidi, { InputEventNoteon, InputEventNoteoff } from "webmidi";
input.addListener("noteon", "all", (event: InputEventNoteon) => {
...
})
```
## Installation
Depending on your needs and environment, you can install **WebMidi.js** in a variety of different
ways.
#### CDN
The easiest way to get started is to link the WebMidi.js library from the
[jsDelivr](https://www.jsdelivr.com/) CDN (content delivery network). To retrieve the latest
version, just add this `
#### Manual Install
Obviously, you can also install **WebMidi.js** the old fashioned way by downloading the
[2.5.3 release](https://github.com/djipco/webmidi/releases/tag/2.5.3) packaged as a zip file.
Uncompress the package, grab the `webmidi.min.js` file and copy it to your project. Link to it from
your HTML page as usual.
#### NPM Install
If it's more convenient, you can install **WebMidi.js** with NPM. Simply issue the following command
to perform the actual install:
npm install webmidi
Then, just add a `
#### Using with a Bundler
If you are using a bundler such as WebPack, you can import **WebMidi.js** in your project in this
way:
import WebMidi from 'path/to/webmidi';
## Insecure Origins
Starting with version 77,
[Chrome deprecates Web MIDI usage on insecure origins](https://www.chromestatus.com/feature/5138066234671104).
This means that, going forward, the page will need to be hosted on a secure origin (e.g.
`https://`, `localhost:` or `file:///`) and the user will need to explicitely authorize usage (no
matter if `sysex` is used or not).
## Quick Start
Getting started is easy. The first thing to do is to enable **WebMidi.js**. To do that, you call
`WebMidi.enable()` and pass it a function to execute when done. This function will receive an
`Error` object if enabling `WebMidi` failed:
```javascript
WebMidi.enable(function (err) {
if (err) {
console.log("WebMidi could not be enabled.", err);
} else {
console.log("WebMidi enabled!");
}
});
```
To send and receive MIDI messages, you will need to do so via the appropriate `Output` and `Input`
device. To view all the available `Input` and `Output` ports, you can use the matching arrays:
```javascript
WebMidi.enable(function (err) {
console.log(WebMidi.inputs);
console.log(WebMidi.outputs);
});
```
To send MIDI messages to a device, you simply need to grab that device and call one of its output
method (`playNote()`, `stopNote()`, `sendPitchBend()`, etc.). To retrieve a device, you can use
its position in the `WebMidi.outputs` array. For instance, to grab the first output device, you
could use:
```javascript
var output = WebMidi.outputs[0];
```
However, this is not very safe as the position of devices in the array could change. An alternative
is to use the device's ID:
```javascript
var output = WebMidi.getOutputById("1584982307");
```
Beware that device IDs are not the same across browsers and platforms. You could also use the device's name (as
displayed in the `WebMidi.outputs` array):
```javascript
var output = WebMidi.getOutputByName("Axiom Pro 25 Ext Out");
```
Then, you can call any of the output methods and all native MIDI communications will be handled for
you. For example, to play a "C" on the 3rd octave, you simply do:
```javascript
output.playNote("C3");
```
That's it.
Receiving messages works in a similar way: you retrieve the `Input` device you want to use, and then
add a callback function to be triggered when a specific MIDI message is received. For example, to
listen for pitch bend events on all channels of the device:
```javascript
var input = WebMidi.getInputByName("Axiom Pro 25 USB A In");
input.addListener('pitchbend', "all", function(e) {
console.log("Pitch value: " + e.value);
});
```
## API Documentation
The [API for WebMidi.js](https://webmidijs.org/archives/api/v2/) is fully documented. If you spot an
error (even something minor) or think a topic should be made clearer, do not hesitate to
[file an issue](https://github.com/djipco/webmidi/issues) or, better yet, send a PR.
Here is a link to the full **[API Reference](https://webmidijs.org/archives/api/v2/)**.
## More code examples
Here are various other examples to give you an idea of what is possible with **WebMidi.js**.
```javascript
// Enable WebMidi.js
WebMidi.enable(function (err) {
if (err) {
console.log("WebMidi could not be enabled.", err);
}
// Viewing available inputs and outputs
console.log(WebMidi.inputs);
console.log(WebMidi.outputs);
// Reacting when a new device becomes available
WebMidi.addListener("connected", function(e) {
console.log(e);
});
// Reacting when a device becomes unavailable
WebMidi.addListener("disconnected", function(e) {
console.log(e);
});
// Display the current time
console.log(WebMidi.time);
// Retrieving an output port/device using its id, name or index
var output = WebMidi.getOutputById("123456789");
output = WebMidi.getOutputByName("Axiom Pro 25 Ext Out");
output = WebMidi.outputs[0];
// Play a note on all channels of the selected output
output.playNote("C3");
// Play a note on channel 3
output.playNote("Gb4", 3);
// Play a chord on all available channels
output.playNote(["C3", "D#3", "G3"]);
// Play a chord on channel 7
output.playNote(["C3", "D#3", "G3"], 7);
// Play a note at full velocity on all channels)
output.playNote("F#-1", "all", {velocity: 1});
// Play a note on channel 16 in 2 seconds (relative time)
output.playNote("F5", 16, {time: "+2000"});
// Play a note on channel 1 at an absolute time in the future
output.playNote("F5", 16, {time: WebMidi.time + 3000});
// Play a note for a duration of 2 seconds (will send a note off message in 2 seconds). Also use
// a low attack velocity
output.playNote("Gb2", 10, {duration: 2000, velocity: 0.25});
// Stop a playing note on all channels
output.stopNote("C-1");
// Stopping a playing note on channel 11
output.stopNote("F3", 11);
// Stop a playing note on channel 11 and use a high release velocity
output.stopNote("G8", 11, {velocity: 0.9});
// Stopping a playing note in 2.5 seconds
output.stopNote("Bb2", 11, {time: "+2500"});
// Send polyphonic aftertouch message to channel 8
output.sendKeyAftertouch("C#3", 8, 0.25);
// Send pitch bend (between -1 and 1) to channel 12
output.sendPitchBend(-1, 12);
// You can chain most method calls
output.playNote("G5", 12)
.sendPitchBend(-0.5, 12, {time: 400}) // After 400 ms.
.sendPitchBend(0.5, 12, {time: 800}) // After 800 ms.
.stopNote("G5", 12, {time: 1200}); // After 1.2 s.
// Retrieve an input by name, id or index
var input = WebMidi.getInputByName("nanoKEY2 KEYBOARD");
input = WebMidi.getInputById("1809568182");
input = WebMidi.inputs[0];
// Listen for a 'note on' message on all channels
input.addListener('noteon', "all",
function (e) {
console.log("Received 'noteon' message (" + e.note.name + e.note.octave + ").");
}
);
// Listen to pitch bend message on channel 3
input.addListener('pitchbend', 3,
function (e) {
console.log("Received 'pitchbend' message.", e);
}
);
// Listen to control change message on all channels
input.addListener('controlchange', "all",
function (e) {
console.log("Received 'controlchange' message.", e);
}
);
// Listen to NRPN message on all channels
input.addListener('nrpn', "all",
function (e) {
if(e.controller.type === 'entry') {
console.log("Received 'nrpn' 'entry' message.", e);
}
if(e.controller.type === 'decrement') {
console.log("Received 'nrpn' 'decrement' message.", e);
}
if(e.controller.type === 'increment') {
console.log("Received 'nrpn' 'increment' message.", e);
}
console.log("message value: " + e.controller.value + ".", e);
}
);
// Check for the presence of an event listener (in such cases, you cannot use anonymous functions).
function test(e) { console.log(e); }
input.addListener('programchange', 12, test);
console.log("Has event listener: ", input.hasListener('programchange', 12, test));
// Remove a specific listener
input.removeListener('programchange', 12, test);
console.log("Has event listener: ", input.hasListener('programchange', 12, test));
// Remove all listeners of a specific type on a specific channel
input.removeListener('noteoff', 12);
// Remove all listeners for 'noteoff' on all channels
input.removeListener('noteoff');
// Remove all listeners on the input
input.removeListener();
});
```
## About Sysex Support
Per the
[Web MIDI API specification](https://webaudio.github.io/web-midi-api/#dom-navigator-requestmidiaccess),
system exclusive (sysex) support is disabled by default. If you need to use sysex messages, you will
need to pass `true` as the second parameter to `WebMidi.enable()`:
```javascript
WebMidi.enable(function (err) {
if (err) {
console.warn(err);
} else {
console.log("Sysex is enabled!");
}
}, true);
```
**Important**: depending on the browser, version and platform, it may also be necessary to serve the
page over https if you want to enable sysex support.
## Migration Notes
If you are upgrading from version 1.x to 2.x, you should know that v2.x is not backwards compatible.
Some important changes were made to the API to make it easier to use, more versatile and to better
future-proof it.
Here is a summary of the changes:
* All the "output" functions (`playNote()`, `sendPitchBend()`, etc.) have been moved to the
`Output` object. A list of all available `Output` objects is available in `WebMidi.outputs`
(like before).
* All the "input" functions (`addListener`, `removeListener()` and `hasListener()` have been
moved to the `Input` object. A list of all available `Input` objects is available in
`WebMidi.inputs` (also like before).
There might be a few other minor changes here and there but the refactoring mostly concerns the
introduction of `Input` and `Output` objects.
## Using WebMidi.js with the Jazz-Plugin
To use **WebMidi.js** on Safari, Firefox and Internet Explorer, you will first need to install
Jazz-Plugin. Simply [download the plugin](http://jazz-soft.net/download/Jazz-Plugin/) and run the
installer.
> Users of Firefox v52+ are currently out of luck because Mozilla deactivated support for NPAPI
> plugins. There is an add-on version of
> [Jazz-Midi](https://addons.mozilla.org/en-US/firefox/addon/jazz-midi/) but, unfortunately, the
> API is different and cannot be used as is. Firefox v52+ users will have to wait for native Web
> MIDI support to be finalized.
> [Reading from the comments on Bug 836897](https://bugzilla.mozilla.org/show_bug.cgi?id=836897),
> this might take a while...
Then, you will need to add the plugin to the page with the following HTML code:
Jazz-Plugin required!
To support recent versions of Internet Explorer, you also need to add a `meta` tag to the ``
of the page:
Since Jazz-Plugin does not use the same syntax as the native Web MIDI API, it is necessary to also
install the [WebMIDIAPIShim](http://cwilso.github.io/WebMIDIAPIShim/) polyfill. You can do that by
including the following in your page:
Obviously, you can also
[download a local copy](https://github.com/cwilso/WebMIDIAPIShim/zipball/master) and link to it.
================================================
FILE: website/docs/getting-started/_category_.json
================================================
{
"label": "Getting Started",
"position": 10
}
================================================
FILE: website/docs/getting-started/basics.md
================================================
---
sidebar_position: 3
---
# Basics
## Enabling the Library
The first step to get started is to enable the library. To do that, you simply call
[`WebMidi.enable()`](/api/classes/WebMidi#enable). Starting with v3, the `enable()` method returns a
promise which is resolved when the library has been enabled:
```javascript
WebMidi
.enable()
.then(() => console.log("WebMidi enabled!"))
.catch(err => alert(err));
```
:::caution
If you intend to use MIDI **system exclusive** messages, you must explicitly enable them by setting
the `sysex` option to `true`:
```javascript
WebMidi
.enable({sysex: true})
.then(() => console.log("WebMidi with sysex enabled!"))
.catch(err => alert(err));
```
:::
## Listing Available Devices
To interact with devices you need to know which `Input` and `Output` ports are available. Connect a
MIDI device and try the following:
```javascript
WebMidi
.enable()
.then(onEnabled)
.catch(err => alert(err));
function onEnabled() {
// Inputs
WebMidi.inputs.forEach(input => console.log(input.manufacturer, input.name));
// Outputs
WebMidi.outputs.forEach(output => console.log(output.manufacturer, output.name));
}
```
You should see your hardware and software devices appear in the console. Note that many devices make
available several input and/or output ports.
You can retrieve a reference to an [`Input`](/api/classes/Input) by using the
[`getInputByName()`](/api/classes/WebMidi#getInputByName) or
[`getInputById()`](/api/classes/WebMidi#getInputById) methods:
```javascript
const myInput = WebMidi.getInputByName("MPK mini 3");
```
Once you have a reference to the input, you can add listeners that will react when a message (such
as a note press) arrives.
## Listening For Incoming MIDI Messages
On a MIDI device, an input has 16 discrete channels. If you want to listen on all of them, you can
add a listener directly on the [`Input`](/api/classes/Input) object:
```javascript
const myInput = WebMidi.getInputByName("MPK mini 3");
myInput.addListener("noteon", e => {
console.log(e.note.identifier);
})
```
Try playing a note on your device. You should see the note's name and octave in the console.
Obviously, you can listen to many more messages coming from your device. For a full list, check out
the [`Input.addListener()`](/api/classes/Input#addListener) documentation.
It is also possible to listen to messages coming from a specific MIDI channel. For example, when
I press the drum pads on my Akai MPK Mini, the messages are sent to channel 10:
```javascript
const myInput = WebMidi.getInputByName("MPK mini 3");
const mySynth = myInput.channels[10]; // <-- the MIDI channel (10)
mySynth.addListener("noteon", e => {
console.log(e.note.identifier, e.message.channel);
})
```
In this case, the listener only listens to **noteon** messages coming in from channel 10 of the
input device.
## Sending Outgoing MIDI Messages
To send messages to an external device, you must first get a reference to it. For that, you can use
methods such as [`getOutputByName()`](/api/classes/WebMidi#getOutputByName) or
[`getOutputById()`](/api/classes/WebMidi#getOutputById):
```javascript
const myOutput = WebMidi.getOutputByName("SP-404MKII");
```
Then, you can use various methods to send your message. For example, if you want to tell you sampler
to turn all sounds off, you could do the following:
```javascript
const myOutput = WebMidi.getOutputByName("SP-404MKII");
myOutput.sendAllSoundOff();
```
You can learn about all the the methods available to send data by looking at the documentation for
the [`Output`](/api/classes/Output) object.
You can send messages to a specific MIDI channel by first grabbing a reference to the channel you
want. For example, on the Roland SP-404 MK II sampler, you can control a vocoder effet by sending a
**pitchbend** message on channel 11:
```javascript
const myOutput = WebMidi.getOutputByName("SP-404MKII");
const vocoder = myOutput.channels[11];
vocoder.sendPitchBend(-0.5);
```
In this case, the `vocoder` constant contains an [`OutputChannel`](/api/classes/OutputChannel)
object.
## Code Examples
Here are various other examples to give you an idea of what is possible with the library. All the
examples below only work if the library has first been properly enabled with
[`WebMidi.enable()`](/api/classes/WebMidi#enable).
### Retrieve an output port/device using its id, name or array index
Different ways to retrieve an output. Beware that IDs are different from one platform to another and
on Node.js the `id` is the same as the `name`.
```javascript
let output1 = WebMidi.getOutputById("123456789");
let output2 = WebMidi.getOutputByName("Axiom Pro 25 Ext Out");
let output3 = WebMidi.outputs[0];
```
### Play a note on a specific MIDI channel (1)
The `channels` property of an `Output` object contains references to 16 `OutputChannel` objects
(1-16).
```javascript
let output = WebMidi.outputs[0];
let channel = output.channels[1];
channel.playNote("C3");
```
### Play a note on multiple channels at once
You can call [`playNote()`](/api/classes/Output#playNote) (and various other methods) directly on
the [`Output`](/api/classes/Output) object. This allows you to play a note on several channels at
once. For example, to play a note on channels 1, 2 and 3:
```javascript
let output = WebMidi.outputs[0];
output.playNote("Gb4", [1, 2, 3]);
```
You can also create a [`Note`](/api/classes/Note) object and pass it to the
[`playNote()`](/api/classes/Output#playNote) method:
```javascript
const note = new Note("A4");
const output = WebMidi.outputs[0];
output.playNote(note);
```
### Play a note on a specific MIDI channel
To play a note on a specific MIDI channel, you can use the
[`playNote()`](/api/classes/OutputChannel#playNote) method of the
[`OutputChannel`](/api/classes/OutputChannel) object (instead of the one on the
[`Output`](/api/classes/Output) object).
For example, to play a chord on MIDI channel 1:
```javascript
let output = WebMidi.outputs[0];
let channel = output.channels[1];
channel.playNote(["C3", "D#3", "G3"]);
```
### Control note velocity
You can control attack and release velocities when playing a note by using the `options` parameter.
```javascript
let output = WebMidi.outputs[0];
let channel = output.channels[1];
channel.playNote("C3", {attack: 0.5});
```
If you prefer to use raw (7 bit) values between 0 and 127, you can use the `rawAttack` option
instead:
```javascript
let output = WebMidi.outputs[0];
let channel = output.channels[1];
channel.playNote("C3", {rawAttack: 123});
```
### Specify note duration
If you specify a duration (in decimal milliseconds) for the note, it will automatically be stopped
after the duration has expired. For example, to stop it after 1 second (1000 ms):
```javascript
let output = WebMidi.outputs[0];
let channel = output.channels[1];
channel.playNote("C3", {duration: 1000});
```
### Schedule notes
You can specify an absolute or relative time to schedule note playback in the future:
```javascript
WebMidi.outputs[0].channels[1].playNote("C3", {time: WebMidi.time + 3000});
WebMidi.outputs[0].channels[1].playNote("C3", {time: "+2000"});
```
You can retrieve the current time with [`WebMidi.time`](/api/classes/WebMidi#time). The time is in
milliseconds (decimal) relative to the navigation start of the document.
### Manually stopping playback
You can stop playback of a note right away or in the future.
```javascript
WebMidi.outputs[0].channels[1].stopNote("C3");
WebMidi.outputs[0].channels[1].stopNote("C3", {time: "+2500"});
```
### Sending a control change (a.k.a. CC) message
There are various ways to send a control change message. The most common way is to send the message
to a single channel. The first parameter is the controller, the second number is the value:
```javascript
// Use controller number
WebMidi.outputs[0].channels[1].sendControlChange(72, 64);
// Use controller name
WebMidi.outputs[0].channels[1].sendControlChange("volumecoarse", 123);
```
As you can see above, you can use either a name or number (0-127) to identify the controller to
target. A
[list of controller names](https://webmidijs.org/api/classes/OutputChannel#sendControlChange) can be
found in the API reference.
You can also send the control change message to several channels at once by using the
`sendControlChange()` method of the `Output` object:
```javascript
// Send to channels 1 through 3
WebMidi.outputs[0].sendControlChange("pancoarse", 123, {channels: [1, 2, 3]});
// Send to all channels
WebMidi.outputs[0].sendControlChange(72, 56);
```
### Set polyphonic aftertouch
Send polyphonic aftertouch message to channel 8:
```javascript
WebMidi.outputs[0].channels[8].sendKeyAftertouch("B#3", 0.25);
```
### Set pitch bend value
The value is between -1 and 1 (a value of 0 means no bend).
```javascript
WebMidi.outputs[0].channels[8].sendPitchBend(-0.25);
```
You can set the range of the bend with
[`OutputChannel.sendPitchBendRange()`](/api/classes/OutputChannel#sendPitchBendRange).
### Use Chained Methods
Most methods return `this` so you can chain them:
```javascript
WebMidi.outputs[0].channels[8]
.sendPitchBend(-0.25)
.playNote("F4");
```
### Listen to event on single channel
```javascript
WebMidi.inputs[0].channels[1].addListener("noteon", e => {
console.log(`Received 'noteon' message (${e.note.name}${e.note.octave}).`);
});
```
### Listen to event on multiple channels at once
If you add a listener to the `Input` instead of the `InputChannel`, you can listen on multiple
channels at once by using the `channels` option:
```javascript
WebMidi.inputs[0].addListener("controlchange", e => {
console.log(`Received 'controlchange' message.`, e);
}, {channels: [1, 2, 3]});
```
If you do not specify a `channels` option, the listener will listen on all channels.
### Check for the presence of an event listener
```javascript
let channel = WebMidi.inputs[0].channels[1];
let test = e => console.log(e);
channel.addListener('programchange', test);
console.log("Has event listener: ", channel.hasListener('programchange', test));
```
### Remove listeners
```javascript
let channel = WebMidi.inputs[0].channels[1];
let test = e => console.log(e);
channel.removeListener("noteoff", test); // specifically this one
channel.removeListener("noteoff"); // all noteoff on this channel
channel.removeListener(); // all listeners
```
## What's Next
I hope this short guide helped you getting started. Obviously, the library can do a whole lot more.
Some of that is covered in the **Going Further** section but all of it is detailed in the [API
documentation](../../api).
If you need help, you can ask questions in the
[Forum](https://github.com/djipco/webmidi/discussions). If you want to stay posted, I suggest you
subscribe to our low-volume [newsletter](https://mailchi.mp/eeffe50651bd/webmidijs-newsletter) and
follow our [@webmidijs](https://twitter.com/webmidijs) account on Twitter.
Finally, if this software proves useful I cannot encourage you enough to support it by becoming a
[sponsor](https://github.com/sponsors/djipco) on GitHub.
-- [Jean-Philippe](../../about#who-created-this)
================================================
FILE: website/docs/getting-started/installation.md
================================================
---
sidebar_position: 2
---
# Installation
## Distribution Flavours
To cater to various needs, WEBMIDI.js is distributed in 3 different flavours:
* **Immediately Invoked Function Expression** (IIFE): This version adds its objects directly to the
global namespace. This is the legacy approach which is often easier for beginners.
* **ES6 Module** (ESM): This is the modern approach which allows you to `import` the objects as
needed (works in newer versions of browsers and Node.js).
* **CommonJS Module** (CJS): this is the flavour traditionnally used by Node.js and often with
bundling tools such as WebPack.
All 3 flavours come in full and minified versions with sourcemap.
## Retrieving the Library
Depending on your needs and environment, you can retrieve and install **WEBMIDI.js** in a variety of
different ways. Let's look at all of them.
## Linking From CDN
The fastest way to get started is to link the library directly from the
[jsDelivr](https://www.jsdelivr.com/package/npm/webmidi) CDN (Content Delivery Network). Just add a
`
```
You can retrieve different versions and flavours of the library by modifying the URL. For example,
to grab a different flavour replace `/dist/iife/webmidi.iife.js` by one of these:
* `/dist/cjs/webmidi.cjs.js`
* `/dist/cjs/webmidi.cjs.min.js`
* `/dist/esm/webmidi.esm.js`
* `/dist/esm/webmidi.esm.min.js`
* `/dist/iife/webmidi.iife.js`
* `/dist/iife/webmidi.iife.min.js`
If you want more control over versions and flavours, check out the
[jsDelivr examples](https://www.jsdelivr.com/features).
## Installing Manually
Obviously, you can also install the library the old-fashioned way by manually
[downloading it](https://cdn.jsdelivr.net/npm/webmidi@latest/dist/iife/webmidi.iife.min.js) and
placing it somewhere in your project. Link to it from your HTML page using a `
```
* ### CommonJS
Using **CommonJS** is the traditional approach for Node.js.
```javascript
const {WebMidi} = require("webmidi");
```
* ### ES Module
This is the modern approach using the
[ECMAScript module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) format.
You can use it in browsers (see [compatibility](https://caniuse.com/es6-module-dynamic-import))
and in Node.js v12+. **Going forward, this is the favoured approach.**
**Browsers:**
```javascript
import {WebMidi} from "./node_modules/webmidi/dist/esm/webmidi.esm.min.js";
```
**Node.js:**
```javascript
import {WebMidi} from "webmidi";
```
:::caution
## Insecure Origins
Starting with version 77,
[Chrome deprecated Web MIDI usage on insecure origins](https://www.chromestatus.com/feature/5138066234671104).
This means that, going forward, any page using the library will need to be hosted on a secure
origin:
* `https://`
* `localhost:`
* `file:///`
Also, the user will need to explicitely authorize usage via a prompt (no matter if system exclusive
messages are used or not).
:::
================================================
FILE: website/docs/getting-started/supported-environments.md
================================================
---
sidebar_position: 1
slug: /getting-started
---
# Supported Environments
Starting with version 3, the library works in both the browser and Node.js. Let's quickly look at
the specificities of both these environments.
## Browser Support
The library works in all browsers that natively [support](https://caniuse.com/midi) the
[Web MIDI API](https://webaudio.github.io/web-midi-api/). Currently, the following major browsers
have native support:
* Edge v79+
* Chrome 43+
* Opera 30+
* Firefox 108+
It is also possible to use this library in other browsers if you install
[Jazz-Plugin](https://jazz-soft.net/download/Jazz-Plugin/) v1.4+. This combination provides
support for the following additional web browsers:
* Safari
* Internet Explorer
Note that, in 2020, [Apple has announced](https://webkit.org/tracking-prevention/) that they would
not natively support the Web MIDI API (and a host of other APIs) in Safari because of fingerprinting
concerns.
## Node.js Support
Version 3.0 of WEBMIDI.js introduced full Node.js support. Nothing special needs to be done, it
should just work in the following environments (with Node.js 8.5+):
* GNU/Linux
* macOS
* Windows
* Raspberry Pi
Support for the Node.js environment has been made possible in large part by the good folks of
[Jazz-Soft](https://jazz-soft.net/) via their [JZZ](https://www.npmjs.com/package/jzz) module.
## TypeScript Support
Starting with version 3, TypeScript is officially supported. You will find the TypeScript definition
file in these locations in side tje library's folder:
* `/dist/cjs/webmidi.cjs.d.ts` (Node.js module)
* `/dist/esm/webmidi.esm.d.ts` (ECMAScript module)
================================================
FILE: website/docs/going-further/_category_.json
================================================
{
"label": "Going Further",
"position": 20
}
================================================
FILE: website/docs/going-further/electron.md
================================================
---
sidebar_position: 5
title: Electron
---
# Electron
WEBMIDI.js works fine inside [Electron](https://www.electronjs.org/) but you must make sure to
properly handle the permission request and permission check handlers from within the main process:
```javascript
mainWindow.webContents.session.setPermissionRequestHandler((webContents, permission, callback, details) => {
if (permission === 'midi' || permission === 'midiSysex') {
callback(true);
} else {
callback(false);
}
})
mainWindow.webContents.session.setPermissionCheckHandler((webContents, permission, requestingOrigin) => {
if (permission === 'midi' || permission === 'midiSysex') {
return true;
}
return false;
});
```
================================================
FILE: website/docs/going-further/forwarding.md
================================================
---
sidebar_position: 3
title: Forwarding
---
# Forwarding Messages
Starting with version 3, it is now possible to forward messages from an
[`Input`](/api/classes/Input) to an [`Output`](/api/classes/Output). This is done by using the
`forward` method of the [`Input`](/api/classes/Input) object.
================================================
FILE: website/docs/going-further/middle-c.md
================================================
---
sidebar_position: 1
---
# Middle C & Octave Offset
## Default Value for Middle C
The general MIDI 1.0 specification does not explicitly define a pitch for middle C but it does
consider middle C to be note number 60. The **MIDI Tuning Standard** states that note number 69
should be tuned at 440Hz by default, which would make middle C (60) to be C4. However, different
manufacturers have assigned middle C to various octaves/pitches (usually C3, C4 or C5).
In accordance with the **MIDI Tuning Standard** and the
[**scientific pitch notation**](https://en.wikipedia.org/wiki/Scientific_pitch_notation), WEBMIDI.js
considers middle C (261.626 Hz) to be C4 by default.
## Offsetting Middle C
You can offset the reported note name and octave by using the `octaveOffset` property of various
objects. This will make it easier to interface with devices that do not place middle C at C4.
### Inbound Note Example
If your external MIDI keyboard sends C3 and WEBMIDI.js reports it as C4, it is because your keyboard
places middle C one octave lower than WEBMIDI.js does. To account for that, simply set
[`WebMidi.octaveOffset`](/api/classes/WebMidi#octaveOffset) to `-1`. This way, when your keyboard
sends C3, WEBMIDI.js will also report it as C3.
In both cases the actual note number (60) remains the same. It is just being reported differently.
### Outbound Note Example
If you are sending F#4 to an external device and that device thinks it's receiving F#5, it means
that the external device places middle C one octave higher. In this case, set
[`WebMidi.octaveOffset`](/api/classes/WebMidi#octaveOffset) to `1` to account for the difference.
## Offsetting Granularity
For most scenarios, setting the global [`WebMidi.octaveOffset`](/api/classes/WebMidi#octaveOffset)
is enough. However, the `octaveOffset` property is available for several objects to allow for better
granularity:
* [Input](/api/classes/Input)
* [InputChannel](/api/classes/InputChannel)
* [Output](/api/classes/Output)
* [OutputChannel](/api/classes/OutputChannel)
* [Note](/api/classes/Note)
* [WebMidi](/api/classes/WebMidi)
If you define `octaveOffset` on several objects, their value will be added. For example, if you
set [`WebMidi.octaveOffset`](/api/classes/WebMidi#octaveOffset) to `-1` and set `octaveOffset` on a
specific channel to `1`, the resulting offset on that channel will be `0` (-1 + 1) while the offset
on other channels will be `1`.
================================================
FILE: website/docs/going-further/performance.md
================================================
---
sidebar_position: 1
---
# Performance
## Targeting Channels
To complete.
## Disabling Validation
To complete.
================================================
FILE: website/docs/going-further/sysex.md
================================================
---
sidebar_position: 3
title: Sysex
---
# System Exclusive Messages (sysex)
================================================
FILE: website/docs/going-further/typescript.md
================================================
---
sidebar_position: 4
title: TypeScript
---
# TypeScript
TypeScript is supported in version 3+. However, it has not yet been tested extensively and some
minor issues may remain.
For instance, some types have been defined as `any` for lack of a better option. One such example
has been described in
[issue 229](https://github.com/djipco/webmidi/issues/229#issuecomment-1039036353). If you are a
TypeScript expert, perhaps you can help.
================================================
FILE: website/docs/index.md
================================================
---
sidebar_position: 1
slug: /
---
# Quick Start For v3.x
**You want to get started as quickly as possible?** This guide will let you establish a connection
with your MIDI instrument in less than 5 minutes.
:::info
Documentation for [version 2.5.x](https://webmidijs.org/archives/api/v2/) and
[version 1.0.0](http://webmidijs.org/archives/api/v1/classes/WebMidi.html) is also available.
:::
## Step 1 - Create the HTML page
:::tip
Hint: You can **go even faster** by copying the
[code](https://github.com/djipco/webmidi/blob/develop/examples/quick-start/index.html) from
our GitHub repo.
:::
Create an HTML document and link to the library:
```html
WebMidi.js Quick Start
WebMidi.js Quick Start
```
## Step 2 - Add a script
Add the following `
```
## Step 3 - Connect your device
🎹 Connect an input MIDI device (synth, drum machine, controller, etc.) and load the HTML page in a
[compatible browser](/docs/getting-started#browser-support). You will be
prompted to authorize the MIDI connection.
After authorization, the page should detect the connected MIDI devices and display their name.
:::info
If nothing shows up, first make sure your MIDI device is detected at the operating system level.
:::
## Step 4 - Listen for MIDI messages
In the `onEnabled()` function, we first retrieve the input device we want to work with and store it
in the `mySynth` variable. You can retrieve it by number or by name (as you wish).
Then we use the `addListener()` method on MIDI channel 1 of the input device to add a
callback function that will be called each time a **noteon** event is received on that MIDI channel.
```javascript
function onEnabled() {
if (WebMidi.inputs.length < 1) {
document.body.innerHTML+= "No device detected.";
} else {
WebMidi.inputs.forEach((device, index) => {
document.body.innerHTML+= `${index}: ${device.name} `;
});
}
const mySynth = WebMidi.inputs[0];
// const mySynth = WebMidi.getInputByName("TYPE NAME HERE!")
mySynth.channels[1].addListener("noteon", e => {
document.body.innerHTML+= `${e.note.name} `;
});
}
```
Alternatively, if you wish to listen for notes from several channels at once, you can add the
listener directly on the input device itself:
```javascript
// Listen to 'note on' events on channels 1, 2 and 3 of the first input MIDI device
WebMidi.inputs[0].addListener("noteon", e => {
document.body.innerHTML+= `${e.note.name} `;
}, {channels: [1, 2, 3]});
```
## Step 5 - Have fun!
**That's it!** To go further, please take some time to check out the
[Getting Started](getting-started) section. It covers important topics such as installation
options, compatibility, security, etc.
:::tip
If you ever need further help, you can also head over to the
[GitHub Discussions](https://github.com/djipco/webmidi/discussions) page and ask all the questions
you want!
:::
================================================
FILE: website/docs/migration/_category_.json
================================================
{
"label": "Migration",
"position": 30
}
================================================
FILE: website/docs/migration/migration.md
================================================
---
sidebar_position: 1
---
# Migrating from 2.5.x to 3.x
:::caution
This document is a work in progress. Your feedback is critical in identifying and, hopefully,
mitigating migration pitfalls. 🙏🏻 Please **share your observations, suggestions and issues** in the
[**Migration**](https://github.com/djipco/webmidi/discussions/categories/migration) section of the forum
so they can benefit others!
:::
## Highlights of the New Version
Version 3.x is a **major** update. It moves WEBMIDI.js from a smallish hobby project to a more
serious long-term endeavour. Here are some key highlights:
- Modern architecture with **ESM** (ECMAScript module), **IIFE** (Immediately Invoked Function
Expression) and **CJS** (CommonJS) flavours
- Full **Node.js** support
- **TypeScript** definitions files
- Various **new objects**:
- [`InputChannel`](/api/classes/InputChannel) and [`OutputChannel`](/api/classes/OutputChannel)
objects to communicate with a single MIDI channel
- [`Note`](/api/classes/Note) object to store and pass around note information
- [`Message`](/api/classes/Message) object to better encapsulate MIDI messages
- [`Forwarder`](/api/classes/Forwarder) object to allow message forwarding from an input to an output
- and others...
- More and better **unit tests** to prevent regression issues
- Support for **promises** where appropriate (such as
[`WebMidi.enable()`](/api/classes/WebMidi#enable))
- More granular **events**. For example:
- `midiaccessgranted` to know when a user clicked the MIDI authorization prompt
- `controlchange-controllerXXX` to listen to a single type of control change message
- and various others...
- Ability to query **current note state** (currently playing or not) with
[`InputChannel.getNoteState()`](/api/classes/InputChannel#getNoteState)
and [`InputChannel.notesState`](/api/classes/InputChannel#notesState) array
- Ability to unplug and replug a device while **retaining its state**
- Better **sysex** (system exclusive) message support
- **Octave transposition** can be performed at the global, input/output or channel level
- and so much more!
## Backwards Compatibility
Backwards compatibility was a major concern while developing the new version. While every effort
has been made to ease the transition, the new version might break a few things, mostly in edge
cases.
Whenever it was possible, we deprecated old practices instead of completely dropping support. This
means that your code should continue to work but you may see warnings in the console about the new
ways to do things.
:::info
If you expected certain things to keep working after upgrade and they don't, please reach out in the
[**Migration**](https://github.com/djipco/webmidi/discussions/categories/migration) section of the
forum so whe can assess if the behaviour is expected or not and troubleshoot from there.
:::
## Architecture Change
In v2, there was a top-level `WebMidi` object that provided access to a list of inputs and outputs.
Most of the activity happened at the `Input` and `Output` level. This is where you would listen for
inbound messages and send outbound messages.
For example, if you wanted to listen for a message on a single MIDI channel, you would have
specified it in the call to `addListener()` in this manner:
```javascript
// In WebMidi.js version 2.5.x
WebMidi.inputs[0].addListener("noteon", 7, someFunction);
```
This would listen for **noteon** events on MIDI channel 7 of input 0. **While this still works in
v3**, the preferred syntax would be:
```javascript
WebMidi.inputs[0].addListener("noteon", someFunction, {channels: [7]});
```
You may think (rightly so!) that this syntax is more cumbersome. The reasoning is that, if you want
to listen to events on a single channel, you should do so on the channel itself
(the new [`InputChannel`](/api/classes/InputChannel) object):
```javascript
WebMidi.inputs[0].channels[7].addListener("noteon", someFunction);
```
Here, `WebMidi.inputs[0].channels[7]` refers to an
[`InputChannel`](/api/classes/InputChannel) object that has, for the most part, the same methods
as the [`Input`](/api/classes/Input) object you were used to in v2.5.x.
So, the idea is to use the [`Input`](/api/classes/Input) object if you need to listen to events on
more than one channel and to use the [`InputChannel`](/api/classes/InputChannel) object to listen
for events dispatched by a single channel.
The exact same logic applies to the [`Output`](/api/classes/Output) and
[`OutputChannel`](/api/classes/OutputChannel) objects. For example, if you want to send a
**controlchange** message to all channels of an output, you can use:
```javascript
WebMidi.outputs[0].sendControlChange("resonance", 123);
```
To send to multiple, but specific, channels, you can use:
```javascript
WebMidi.outputs[0].sendControlChange("resonance", 123, {channels: [1, 2, 3]});
```
To send the message to a single channel (e.g. 7), you would use:
```javascript
WebMidi.outputs[0].channels[7].sendControlChange("resonance", 123);
```
In the end, the idea is to target specifically what is appropriate. Therefore, a more idiomatic
snippet would be something like this:
```javascript
const output = WebMidi.getOutputByName("My Awesome Midi Device");
const synth = output.channels[10];
synth.playNote("A4");
```
Having said all that, let me reiterate that **the previous way of doing things will still work in
v3**. This will give you a chance to smoothly transition to the new version.
Let's recap. In v3, there is a top-level [`WebMidi`](/api/classes/WebMidi) object which has both
an [`inputs`](/api/classes/WebMidi#inputs) and an [`outputs`](/api/classes/WebMidi#outputs)
array. These arrays contain, respectively, a list [`Input`](/api/classes/Input) and
[`Output`](/api/classes/Output) objects. The [`Input`](/api/classes/Input) and
[`Output`](/api/classes/Output) objects have a `channels` array that contains a list of
[`InputChannel`](/api/classes/InputChannel) or [`OutputChannel`](/api/classes/OutputChannel)
objects.
:::info
You can also check the [CHANGELOG.md](https://github.com/djipco/webmidi/blob/master/CHANGELOG.md)
file for more hints of what has changed in version 3.
:::
## Things to Watch Out For
### The `WebMidi.enable()` method now returns a promise
**You can still use a callback** with [`WebMidi.enable()`](/api/classes/WebMidi#enable) and it
will work just like before. However, you are now welcome to use the promise-based approach:
```javascript
WebMidi.enable().then(() => {
console.log("WebMidi.js has been enabled!");
})
```
================================================
FILE: website/docs/roadmap/_category_.json
================================================
{
"label": "Roadmap",
"position": 50
}
================================================
FILE: website/docs/roadmap/under-evaluation.md
================================================
---
sidebar_label: Under Evaluation
sidebar_position: 2
---
# Potential Enhancements To Evaluate
The analysis has not started yet. We will wait after the official launch of version 3. We do have a
lot of ideas and suggestions in store. Depending on whether these features break the API or not,
they may make it into version 3.x or be deployed in v4.
:::info
If you have suggestions, please post them for discussion to the
[Feature Request](https://github.com/djipco/webmidi/discussions/categories/feature-requests)
category of our GitHub Discussions.
:::
## Ideas & Suggestions to Evaluate
If you feel any of these ideas should be given priority, plese explain why in the
[Feature Request](https://github.com/djipco/webmidi/discussions/categories/feature-requests)
category of our GitHub Discussions so I can properly triage them.
* Add support for Web BLE MIDI ([browser implementation](https://github.com/skratchdot/ble-midi),
[Node implementation](https://github.com/natcl/ble-midi))
* Explore compatibility with WebMidiLink. Could we create an output that points to a WebMidiLinked
device?
* Could we allow `WebMidi.time` to be reset? (see
[discussion #213](https://github.com/djipco/webmidi/discussions/213))
* Add throttling or delay option to `sendSysex` (see discussion
[#235](https://github.com/djipco/webmidi/discussions/235)).
* Calculate BPM from clock messages
([Discussion #177](https://github.com/djipco/webmidi/discussions/177))
* Allow the first argument of `output.playNote( )` to be ‘0:0’ as ‘A0’, ‘7:3’ as ‘E:3’ and so on.
* Add a "mute" option for inputs/outputs
* Include the ability to add MIDI event listeners at the WebMidi.js level
([Issue #138](https://github.com/djipco/webmidi/issues/138))
* Emit events on `send()` so outbound MIDI messages can be listened for
([Discussion #171](https://github.com/djipco/webmidi/discussions/171))
* Add a `stopAllNotes()` method
* Calculate time values and make them directly available for `songposition` and `timecode` message
* Make Istanbul (nyc) break down the coverage stats by test file.
* Add the ability to send grouped messages for CC events (and potentially others)
* Add expliocit support for
[MIDI Polyphonic Expressions](https://www.midi.org/midi-articles/midi-polyphonic-expression-mpe).
* Add explicit support for
[Universal System Exclusive Messages](https://www.midi.org/specifications-old/item/table-4-universal-system-exclusive-messages)
* This would include a `sendIdentityRequest()` method to the output object (perhaps with a
`getIdentity()` companion method that waits for the response) ([Issue #117](https://github.com/djipco/webmidi/issues/117))
* This could also include the capability to query device for make/model (similar to
[jzz-midi-gear](https://www.npmjs.com/package/jzz-midi-gear))
* Implement show control protocol subset
* Add ability to inject Jazz-Plugin code for browsers with no native Web MIDI API support.
* Add the option to create sysex plugins for various devices
[forum thread](https://webmidijs.org/forum/discussion/comment/97#Comment_97)
* Add
[issue and PR templates](https://help.github.com/en/github/building-a-strong-community/about-issue-and-pull-request-templates)
* Add continuous integration tool
* Add ability to read/write MIDI files
* Solid timing, midi clock, sync, transport functionality
* Helper functions that help to deal with sysex checksum from specific manufacturer (Roland,
checksum, etc.)
* Add explicit support for Sample Dump Format (see discussion on
[forum](https://webmidijs.org/forum/discussion/30/has-there-been-any-work-on-sample-dump-standard))
* Allow third-party developers to develop modules that facilitate encoding and decoding of
device-specific sysex messages (see [forum discussion](https://webmidijs.org/forum/discussion/37/))
* Add timing capabilities such as syncing with Tone.js or being able to schedule events using
musical notes.
* Add the ability to export a MIDI file (perhaps with another lib such as
[MidiWriterJS](https://www.npmjs.com/package/midi-writer-js) or
[Jazz-Soft](https://jazz-soft.net/demo/WriteMidiFile.html)
* SMF Support
* Check if something specific needs to be done to support Electron
([this discussion](https://www.electronjs.org/docs/api/session#sessetpermissionrequesthandlerhandler)).
* Evaluate whether if would be worth it to switch from the `midi` module to the `web-midi-test`
module for unit tests (discussion [here](https://github.com/djipco/webmidi/discussions/223)).
## Enhancements Put On Hold For Now
* Consider usage of
[pipelining operator](https://github.com/tc39/proposal-pipeline-operator/blob/master/README.md#introduction)
for patching webmidi function calls to a sequence
* Consider using [middleware](https://github.com/unbug/js-middleware) approach for making the app
pluggable
* Investigate the possibility to add a `Device` object that would group inputs and outputs for a
single device (see [discussion #280](https://github.com/djipco/webmidi/discussions/280) for
details)
* Piano roll
================================================
FILE: website/docs/roadmap/v3.md
================================================
---
sidebar_label: Version 3
sidebar_position: 1
---
# Roadmap for version 3.x
## Enhancements Still Remaining
* Publish TypeScript definitions to
[DefinitelyTyped](https://definitelytyped.org/guides/contributing.html) when v3 is stable (they are
currently available in the `dist` directory)
* Add `inputconnected`, `inputdisconnected`, `outputconnected` and `outputdisconnected` events.
* There is something in `InputChannel.test.js` that prevents the tests from running correctly in
`Output.test.js`
* Unit tests should minimally check the 3 flavours of the library (ESM, CJS and IIFE) to make sure
they each properly run.
* A combined CC0 / CC32 / ProgChange method to call program changes with bank selection in a single
method
* The callback for `WebMidi.addListener()` and `Input.addListener()` should probably have both a
`target` and a `port` property when the target is not the same as the port. Perhaps both should be
kept everywhere for consistency.
## Enhancement Already Baked In
* Added triggering of `portschanged` event in `WebMidi` object (v3.0.2).
* Add `filter` option to allow listening to only a subset of events (e.g. specific controller change
or NRPN messages, discussed in [PR #88](https://github.com/djipco/webmidi/pull/88))
* Add a way to forward inbound messages on an `Input` object to an `Output` (to behave like a
physical MIDI THRU port). This could be expanded to a more elaborate filtering and routing system
([example](https://github.com/shemeshg/RtMidiWrap#routing-configuration))
* Add mechanism to Generate TypeScript type definitions (.d.ts files)
* Add `getNoteState()` method to `InputChannel` so it it is possible to check if a note is currently
playing or not. This allows to check for chords when a **noteon** message is received.
* Properly handle when a laptop's lid is closed then reopened
([Issue #140](https://github.com/djipco/webmidi/issues/140))
* As suggested by users, allow sending MSB and LSB at once when sending control change messages
([Issue #57](https://github.com/djipco/webmidi/issues/57)). This would have to be done for CC
messages 0-31 which all have a matching LSB.
* Rewrite the NRPN parsing mechanism in InputChannel. I do not think it works correctly. Here are
starting points:
- http://tetradev.blogspot.com/2010/03/nrpns-part-2-nrpns-in-ableton-with-max.html
- https://www.elektronauts.com/t/nrpn-tutorial-how-to/46669
- http://www.philrees.co.uk/nrpnq.htm
* Allow `sendSysex()` to accept `Uint8Array` ([Issue #124](https://github.com/djipco/webmidi/issues/124), [forum thread](https://webmidijs.org/forum/discussion/comment/97#Comment_97)) or perhaps to accept a `Message` object that can be built from a `Uint8Array` (this needs to be carefully examined)
* Add a `Message` object
* Add the ability to set default values for attack velocity, release velocity, etc. ( see [forum discussion](https://webmidijs.org/forum/discussion/44/things-in-webmidi-js-2-52-that-make-me-go-huh#latest))
* Various utility methods should probably be stashed in a `Utils` class (e.g. getCcNameByNumber(), etc.)
* Add convenience method to convert float and 7 bit: `to7bit()` and `toNormalized()`
* Add the ability to individually transpose `Input`, `Output`, `InputChannel` and `OutputChannel`.
* Add `InputChannel` and `OutputChannel` objects ([Issue #20](https://github.com/djipco/webmidi/issues/20))
* Use ES6+ modern syntax and add default export so library can be imported with `import`
(Issues [#49](https://github.com/djipco/webmidi/issues/49) and [#89](https://github.com/djipco/webmidi/issues/89))
* Move to Rollup for packaging the library ([Issue #61](https://github.com/djipco/webmidi/issues/61))
* Drop support for Bower ([Issue #60](https://github.com/djipco/webmidi/issues/60))
* Extend a proper event library to allow for modern event support (probably
[djipevents](https://github.com/djipco/djipevents)).
* Implement port `statechange` events (`connected` and `disconnected`)
* Make `WebMidi` a singleton (see example
[here](https://www.sitepoint.com/javascript-design-patterns-singleton/))
* WebMidi should dispatch 'enabled' and 'disabled' event
* Check that disable() really does disable everything
* Add methods for channel mode messages
* Implement `clear()` method ([Issue #52](https://github.com/djipco/webmidi/issues/52)) [this will
automatically work when browsers add support for it but will show a warning in the console until
then).
* Added the new ([software](https://webaudio.github.io/web-midi-api/#dom-midioptions)) parameter for
`requestMIDIAccess` [this will automatically work when browsers add support for it).
* Emit events for all channel mode messages
* Add `statusByte` and `dataBytes` properties to the event triggered when receiving messages
([#109](https://github.com/djipco/webmidi/issues/109))
* Deprecate the ability to send on all channels (default behaviour). This clogs up the MIDI stream
and I do not really see a good use case for it.
* Create a `Note` object with `duration` and `velocity` property
* Add official support for Node.js ([Issue #15](https://github.com/djipco/webmidi/issues/15))
* Allow specifying different note durations and velocities in `playNote()` when using arrays
([Issue #75](https://github.com/djipco/webmidi/issues/75) and
[#90](https://github.com/djipco/webmidi/issues/90)). [this is now possible with `Note` objects].
* Add [editorconfig file](https://atom.io/packages/editorconfig)
* Complete test suite for all objects
* Add `sendRaw()` method accepting either list of integers or `Uint8Array`.
* Allow `send()` to accept `Uint8Array`
================================================
FILE: website/docs/roadmap/v4.md
================================================
---
sidebar_label: Version 4
sidebar_position: 2
---
# Features Planned for Version 4
The full analysis has not started yet. We do have a lot of [ideas](/docs/roadmap/under-evaluation)
in store. Depending on whether these features break the API or not, they may make it into version
3.x or be deployed in v4.
:::info
If you have suggestions, please post them for discussion to the
[Feature Request](https://github.com/djipco/webmidi/discussions/categories/feature-requests)
category of our GitHub Discussions.
:::
================================================
FILE: website/docusaurus.config.js
================================================
const lightCodeTheme = require("prism-react-renderer/themes/github");
const darkCodeTheme = require("prism-react-renderer/themes/dracula");
const BASE_URL = "/";
/** @type {import("@docusaurus/types").DocusaurusConfig} */
module.exports = {
title: "WEBMIDI.js",
tagline: "Kickstart your JavaScript MIDI projects!",
url: "https://webmidijs.org",
baseUrl: BASE_URL,
onBrokenLinks: "warn",
onBrokenMarkdownLinks: "warn",
favicon: "img/favicon.ico",
organizationName: "djipco",
projectName: "webmidi",
// trailingSlash: false,
scripts: [
],
themeConfig: {
navbar: {
// title: "My Site",
logo: {
alt: "WEBMIDI.js",
src: "img/webmidijs-logo-dark.svg",
srcDark: "img/webmidijs-logo-light.svg",
},
items: [
{
label: "Docs",
type: "doc",
docId: "index",
position: "left",
},
{
type: "dropdown",
label: "API",
position: "left",
items: [
{
label: "3.x",
to: "api/"
},
{
label: "2.5.3",
href: "https://webmidijs.org/archives/api/v2/",
},
{
label: "1.0.0-beta.15",
href: "http://webmidijs.org/archives/api/v1/classes/WebMidi.html"
}
]
},
{
type: "dropdown",
label: "Community",
position: "left",
items: [
{
label: "Sponsors",
to: "sponsors"
},
{
label: "Showcase",
to: "showcase"
},
{
label: "GitHub Discussions",
href: "https://github.com/djipco/webmidi/discussions",
className: "external"
},
{
label: "Newsletter Subscription",
href: "https://mailchi.mp/eeffe50651bd/webmidijs-newsletter",
className: "external"
},
{
label: "Forum (Archived)",
href: "https://webmidijs.org/forum/",
className: "external"
}
]
},
{
type: "dropdown",
label: "Behind the Scenes",
position: "left",
items: [
{
to: "about",
label: "About",
},
{
to: "blog",
label: "Blog",
},
{
to: "research",
label: "Academic Research",
},
]
},
{
href: "https://github.com/djipco/webmidi",
position: "right",
className: "header-github-link",
"aria-label": "GitHub Repo"
},
{
href: "https://twitter.com/webmidijs",
position: "right",
className: "header-twitter-link",
"aria-label": "Twitter Feed"
},
]
},
footer: {
style: "dark",
logo: {
alt: "WebMidi.js",
src: "img/webmidijs-logo-dark.svg",
srcDark: "img/webmidijs-logo-light.svg",
},
links: [
{
title: "Docs",
items: [
{
label: "Quick Start",
to: "/docs",
},
{
label: "Getting Started",
to: "/docs/getting-started",
},
{
label: "Migration",
to: "/docs/migration",
},
],
},
{
title: "Community",
items: [
{
label: "Showcase",
href: "/showcase",
},
{
label: "Twitter",
href: "https://twitter.com/webmidijs",
},
],
},
{
title: "More",
items: [
{
label: "GitHub",
href: "https://github.com/djipco/webmidi",
},
],
},
],
copyright: `© 2015-${new Date().getFullYear()} Jean-Philippe Côté`,
},
prism: {
theme: lightCodeTheme,
darkTheme: darkCodeTheme,
},
algolia: {
apiKey: "417771b74406a78671b6592f451f2453",
indexName: "webmidi",
appId: "KHO24V8B5T",
// Optional: see doc section below
contextualSearch: true,
// Optional: Algolia search parameters
searchParameters: {},
//... other Algolia params
placeholder: "Search website..."
},
image: "img/og-card.png",
metadata: [{ name: "robots", content: "max-image-preview:large" }],
announcementBar: {
id: "sponsor-banner",
content: "" +
"Sponsor ❤️ WEBMIDI.js on GitHub!"
}
},
presets: [
[
"@docusaurus/preset-classic",
{
theme: {
customCss: [
require.resolve("./src/css/custom.scss"),
require.resolve("./src/css/index.scss"),
],
},
docs: {
path: "docs",
lastVersion: "current",
onlyIncludeVersions: ["current"],
sidebarPath: require.resolve("./sidebars.js"),
editUrl: "https://github.com/djipco/webmidi/edit/master/website/",
},
blog: {
path: "blog",
blogTitle: "Blog de Docusaurus !",
blogDescription: "Un blog alimenté par Docusaurus !",
postsPerPage: "ALL",
},
pages: {},
gtag: {
// trackingID: "UA-162785934-1",
trackingID: "G-Z65JF8XMJG",
},
googleAnalytics: {
// trackingID: "UA-162785934-1",
trackingID: "G-Z65JF8XMJG",
}
}
],
],
plugins: [
[
"@docusaurus/plugin-content-docs",
{
id: "api",
path: "api",
routeBasePath: "api",
sidebarPath: require.resolve("./sidebars.js"),
},
],
[
"docusaurus-plugin-sass",
{}
],
[
"@docusaurus/plugin-client-redirects",
{
redirects: [
{
from: ["/latest/classes/WebMidi.html"], // string | string[]
to: "/api/",
},
],
},
],
],
};
================================================
FILE: website/package.json
================================================
{
"name": "docusaurus",
"version": "0.0.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
"@docusaurus/core": "^3.0.0",
"@docusaurus/plugin-client-redirects": "^3.0.0",
"@docusaurus/plugin-content-blog": "^3.0.0",
"@docusaurus/plugin-content-docs": "^3.0.0",
"@docusaurus/preset-classic": "^3.0.0",
"@docusaurus/theme-search-algolia": "^3.0.0",
"@mdx-js/react": "^1.6.21",
"@svgr/webpack": "^6.3.1",
"clsx": "^1.1.1",
"docusaurus-plugin-sass": "^0.2.5",
"file-loader": "^6.2.0",
"prism-react-renderer": "^1.2.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-helmet": "^6.1.0",
"sass": "^1.43.4",
"typescript": "^4.4.2",
"url-loader": "^4.1.1"
},
"browserslist": {
"production": [
">0.5%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
================================================
FILE: website/sidebars.js
================================================
/**
* Creating a sidebar enables you to:
- create an ordered group of docs
- render a sidebar for each doc of that group
- provide next/previous navigation
The sidebars can be generated from the filesystem, or explicitly defined here.
Create as many sidebars as you want.
*/
module.exports = {
// By default, Docusaurus generates a sidebar from the docs folder structure
tutorialSidebar: [{type: "autogenerated", dirName: "."}],
// But you can create a sidebar manually
/*
tutorialSidebar: [
{
type: 'category',
label: 'Tutorial',
items: ['hello'],
},
],
*/
};
================================================
FILE: website/src/components/Button.js
================================================
import React from "react";
import styles from "./Button.module.scss";
/*export default function Button({children, type, href, target}) {
return (
);
}*/
export default function Button(props) {
const component = "Button";
const{
children,
href,
type,
target,
} = props;
return (
);
}
================================================
FILE: website/src/components/Button.module.css
================================================
.Button {
background-color: var(--color-accent);
width: fit-content;
height: fit-content;
border-radius: 10px;
position: relative;
overflow: hidden;
text-align: center;
}
.Button a {
display: block;
font: bold 1.56rem var(--font-secondary), sans-serif;
padding: var(--spacing-sm) 1.5em;
color: var(--color-text-primary);
text-decoration: none;
position: relative;
z-index: 2;
}
.Button .buttonBg {
width: 100%;
height: 100%;
position: absolute;
}
.Button.button-bg-full {
border: solid 4px var(--color-accent);
}
.Button.button-bg-full .buttonBg {
background-color: var(--color-accent-lighter);
z-index: 1;
top: 0;
left: -100%;
transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.Button.button-bg-full:hover {
border: solid 4px var(--color-accent-lighter);
transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.Button.button-bg-full:hover .buttonBg {
left: 0;
transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.Button.button-bg-empty {
background-color: rgba(0, 0, 0, 0);
border: solid 4px var(--color-accent);
}
.Button.button-bg-empty a {
color: var(--color-accent);
transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.Button.button-bg-empty .buttonBg {
background-color: var(--color-accent-lighter);
z-index: 1;
top: 0;
left: -100%;
transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.Button.button-bg-empty:hover {
border: solid 4px var(--color-accent-lighter);
transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.Button.button-bg-empty:hover a {
color: var(--color-white);
transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.Button.button-bg-empty:hover .buttonBg {
left: 0;
transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
/*# sourceMappingURL=Button.module.css.map */
================================================
FILE: website/src/components/Button.module.scss
================================================
$ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86);
.Button {
background-color: var(--color-accent);
width: fit-content;
height: fit-content;
border-radius: 10px;
position: relative;
overflow: hidden;
text-align: center;
a {
display: block;
font: bold 1.56rem var(--font-secondary), sans-serif;
padding: var(--spacing-sm) 1.5em;
color: var(--color-text-primary);
text-decoration: none;
position: relative;
z-index: 2;
}
.buttonBg {
width: 100%;
height: 100%;
position: absolute;
}
&.button-bg-full {
border: solid 4px var(--color-accent);
.buttonBg {
background-color: var(--color-accent-lighter);
z-index: 1;
top: 0;
left: -100%;
transition: all 0.3s $ease-in-out-circ;
}
&:hover {
border: solid 4px var(--color-accent-lighter);
transition: all 0.3s $ease-in-out-circ;
.buttonBg {
left: 0;
transition: all 0.3s $ease-in-out-circ;
}
}
}
&.button-bg-empty {
background-color: rgba(0, 0, 0, 0%);
border: solid 4px var(--color-accent);
a {
color: var(--color-accent);
transition: all 0.3s $ease-in-out-circ;
}
.buttonBg {
background-color: var(--color-accent-lighter);
z-index: 1;
top: 0;
left: -100%;
transition: all 0.3s $ease-in-out-circ;
}
&:hover {
border: solid 4px var(--color-accent-lighter);
transition: all 0.3s $ease-in-out-circ;
a {
color: var(--color-white);
transition: all 0.3s $ease-in-out-circ;
}
.buttonBg {
left: 0;
transition: all 0.3s $ease-in-out-circ;
}
}
}
}
================================================
FILE: website/src/components/Column.js
================================================
import React from "react";
import styles from "./Column.module.scss";
export default function Column({children, type,}) {
const component = "Column";
return (
{children}
);
}
================================================
FILE: website/src/components/Column.module.css
================================================
.Column {
display: grid;
}
.col-2 {
grid-template-columns: 48% 48%;
justify-content: space-between;
align-items: center;
}
@media screen and (max-width: 1000px) {
.col-2 {
display: grid;
grid-template-columns: 1fr;
text-align: center;
}
}
/*# sourceMappingURL=Column.module.css.map */
================================================
FILE: website/src/components/Column.module.scss
================================================
.Column{
display: grid;
}
.col-2 {
grid-template-columns: 48% 48%;
justify-content: space-between;
align-items: center;
@media screen and(max-width: 1000px){
display: grid;
grid-template-columns: 1fr;
text-align: center;
}
}
================================================
FILE: website/src/components/HomepageFeatures.js
================================================
import React from "react";
import clsx from "clsx";
import styles from "./HomepageFeatures.module.css";
const FeatureList = [
// {
// title: 'Easy to Use',
// Svg: require('../../static/img/undraw_docusaurus_mountain.svg').default,
// description: (
// <>
// Docusaurus was designed from the ground up to be easily installed and
// used to get your website up and running quickly.
// >
// ),
// },
// {
// title: 'Focus on What Matters',
// Svg: require('../../static/img/undraw_docusaurus_tree.svg').default,
// description: (
// <>
// Docusaurus lets you focus on your docs, and we'll do the chores. Go
// ahead and move your docs into the docs directory.
// >
// ),
// },
// {
// title: 'Powered by React',
// Svg: require('../../static/img/undraw_docusaurus_react.svg').default,
// description: (
// <>
// Extend or customize your website layout by reusing React. Docusaurus can
// be extended while reusing the same header and footer.
// >
// ),
// },
];
function Feature({Svg, title, description}) {
return (
);
}
export default function HomepageFeatures() {
return (
{FeatureList.map((props, idx) => (
))}
);
}
================================================
FILE: website/src/components/HomepageFeatures.module.css
================================================
/* stylelint-disable docusaurus/copyright-header */
.features {
display: flex;
align-items: center;
padding: 2rem 0;
width: 100%;
}
.featureSvg {
height: 200px;
width: 200px;
}
================================================
FILE: website/src/components/InformationBar.js
================================================
import React from "react";
import styles from "./InformationBar.module.scss";
export default function InformationBar({children, type,}) {
const component = "InformationBar";
return (
);
}
================================================
FILE: website/src/components/InformationBar.module.css
================================================
.InformationBar {
background-color: var(--color-bg-secondary);
padding: var(--spacing-xl) 0;
color: var(--color-white);
text-align: center;
}
.InformationBar p {
font-size: 1.87rem;
line-height: 120%;
margin: 0;
}
/*# sourceMappingURL=InformationBar.module.css.map */
================================================
FILE: website/src/components/InformationBar.module.scss
================================================
.InformationBar{
background-color: var(--color-bg-secondary);
padding: var(--spacing-xl) 0;
color: var(--color-white);
text-align: center;
p {
font-size: 1.87rem;
line-height: 120%;
margin:0;
}
}
================================================
FILE: website/src/css/custom.css
================================================
/* stylelint-disable docusaurus/copyright-header */
@import url("https://fonts.googleapis.com/css2?family=Exo:ital,wght@0,400;0,900;1,900&family=Lato:wght@400;700&display=swap");
:root {
--ifm-color-primary: #f9d137;
--ifm-color-primary-dark: #f8ca19;
--ifm-color-primary-darker: #f8c70b;
--ifm-color-primary-darkest: #cfa506;
--ifm-color-primary-light: #fad855;
--ifm-color-primary-lighter: #fadb63;
--ifm-color-primary-lightest: #fce590;
--ifm-code-font-size: 95%;
--ifm-link-color: #759DCE;
--ifm-code-padding-horizontal: 0.2rem;
--color-white: #ffffff;
--color-black: #1c1e21;
--color-text-primary: var(--color-black);
--color-text-secondary: var(--color-white);
--color-text-tertiary: #999;
--color-bg-primary: var(--color-white);
--color-bg-secondary: var(--color-black);
--color-bg-tertiary: #F8F8F8;
--color-accent: #ffd000;
--color-accent-lighter: #ffe571;
--color-accent-darker: #e0ba0f;
--spacing-xs: 15px;
--spacing-sm: 20px;
--spacing-md: 30px;
--spacing-lg: 50px;
--spacing-xl: 100px;
--spacing-xxl: 175px;
--spacing-xxxl: 250px;
--font-primary: "Lato", sans-serif;
--font-secondary: "Exo", sans-serif;
--font-size-content-desktop: 1.125rem;
--font-size-content-mobile: 1rem;
--font-size-button: 1.25rem;
--font-size-h1-desktop: 3rem;
--font-size-h1-mobile: 2.5rem;
--font-size-h2-desktop: 1.8rem;
--font-size-h2-mobile: 1.6rem;
--font-size-h3-desktop: 1.2rem;
--font-size-h3-mobile: 1.125rem;
}
@media screen and (max-width: 500px) {
:root {
--font-size-h1-desktop: 3.5rem;
}
}
html[data-theme=dark]:root {
--color-text-primary: var(--color-white);
--color-text-secondary: var(--color-black);
--color-bg-primary: var(--color-black);
--color-bg-secondary: #222428;
--color-bg-tertiary: #222428;
}
.docusaurus-highlight-code-line {
background-color: rgba(0, 0, 0, 0.1);
display: block;
margin: 0 calc(-1 * var(--ifm-pre-padding));
padding: 0 var(--ifm-pre-padding);
}
.navbar__brand {
height: 3rem;
}
.navbar__brand .navbar__logo {
height: 3.5rem;
}
.docs-wrapper main .container article .markdown header:nth-child(2) {
display: none;
}
main article a:link {
font-weight: var(--ifm-font-weight-bold);
}
html[data-theme=dark] .docusaurus-highlight-code-line {
background-color: rgba(0, 0, 0, 0.3);
}
.header-github-link {
padding: 4px;
}
.header-github-link:hover {
opacity: 0.6;
}
.header-github-link:before {
content: "";
width: 24px;
height: 24px;
display: flex;
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat;
}
html[data-theme=dark] .header-github-link:before {
filter: invert(1);
}
.header-twitter-link {
margin-right: 0.5em;
padding: 4px;
}
.header-twitter-link:hover {
opacity: 0.6;
}
.header-twitter-link:before {
content: "";
width: 25px;
height: 25px;
display: flex;
background: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3C!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3Csvg version='1.0' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 24 24' enable-background='new 0 0 24 24' xml:space='preserve'%3E%3Cpath fill='%23191717' d='M12,0C5.4,0,0,5.4,0,12s5.4,12,12,12s12-5.4,12-12S18.6,0,12,0z M17.4,9.3c0,0.1,0,0.2,0,0.4 c0,3.6-2.8,7.8-7.8,7.8c-1.5,0-3-0.4-4.2-1.2c0.2,0,0.4,0,0.7,0c1.2,0,2.4-0.4,3.4-1.2c-1.2,0-2.2-0.8-2.6-1.9 c0.4,0.1,0.8,0.1,1.2,0c-1.3-0.3-2.2-1.4-2.2-2.7v0c0.4,0.2,0.8,0.3,1.3,0.3C5.9,9.9,5.5,8.3,6.2,7.1c1.4,1.7,3.5,2.8,5.7,2.9 c0-0.2-0.1-0.4-0.1-0.6c0-0.8,0.3-1.5,0.9-2c1.1-1,2.9-1,3.9,0.1c0.6-0.1,1.2-0.3,1.8-0.7c-0.2,0.6-0.6,1.2-1.2,1.5 c0.5-0.1,1.1-0.2,1.6-0.4C18.4,8.4,17.9,8.9,17.4,9.3z'/%3E%3C/svg%3E%0A") no-repeat;
}
html[data-theme=dark] .header-twitter-link:before {
filter: invert(1);
}
.docs-wrapper article .markdown header:nth-of-type(2) {
display: none;
}
div[role=banner] {
background-color: var(--ifm-color-primary);
}
li a.dropdown__link svg {
display: none;
}
li a.recommended {
font-weight: bold;
}
li a.dropdown__link.external svg {
display: inline;
}
.parameter-table-container table {
display: table;
width: 100%;
}
.parameter-table-container table th:first-of-type {
width: 18%;
}
.parameter-table-container table th:nth-of-type(2) {
width: 18%;
}
.parameter-table-container table th:nth-of-type(3) {
width: 18%;
}
.parameter-table-container table th:nth-of-type(4) {
width: 46%;
}
/* ==========================================================================
HEADINGS / ELEMENT
========================================================================== */
h1 {
font-family: var(--font-secondary);
font-weight: 900;
font-size: var(--font-size-h1-mobile);
}
@media screen and (min-width: 1024px) {
h1 {
font-size: var(--font-size-h1-desktop);
}
}
h2 {
font-family: var(--font-secondary);
font-weight: 700;
font-size: var(--font-size-h2-mobile);
margin-top: 2.4rem;
}
@media screen and (min-width: 1024px) {
h2 {
font-size: var(--font-size-h2-desktop);
}
}
h3 {
font-family: var(--font-secondary);
font-weight: 700;
font-size: var(--font-size-h3-mobile);
}
@media screen and (min-width: 1024px) {
h3 {
font-size: var(--font-size-h3-desktop);
}
}
html[data-theme=light] a.cem-logo img {
filter: invert(1);
}
a.user-icon img {
border-radius: 50px;
border: 1px solid black;
margin: 0.5rem;
}
/*# sourceMappingURL=custom.css.map */
================================================
FILE: website/src/css/custom.scss
================================================
/* stylelint-disable docusaurus/copyright-header */
// Global custom stylesheet for Docusaurus. The 'classic' template bundles Infima. Infima is a CSS
// framework designed for content-centric websites.
// Import
@import url("https://fonts.googleapis.com/css2?family=Exo:ital,wght@0,400;0,900;1,900&family=Lato:wght@400;700&display=swap");
// Default Infima variables ////////////////////////////////////////////////////////////////////////
:root {
--ifm-color-primary: #f9d137;
--ifm-color-primary-dark: #f8ca19;
--ifm-color-primary-darker: #f8c70b;
--ifm-color-primary-darkest: #cfa506;
--ifm-color-primary-light: #fad855;
--ifm-color-primary-lighter: #fadb63;
--ifm-color-primary-lightest: #fce590;
--ifm-code-font-size: 95%;
--ifm-link-color: #759DCE;
//--ifm-background-color: #eee;
--ifm-code-padding-horizontal: 0.2rem;
// Base color
--color-white: #ffffff;
--color-black: #1c1e21;
// Project Color
--color-text-primary: var(--color-black);
--color-text-secondary: var(--color-white);
--color-text-tertiary: #999;
--color-bg-primary: var(--color-white);
--color-bg-secondary: var(--color-black);
--color-bg-tertiary: #F8F8F8;
--color-accent: #ffd000;
--color-accent-lighter: #ffe571;
--color-accent-darker: #e0ba0f;
// Dimensions
--spacing-xs: 15px;
--spacing-sm: 20px;
--spacing-md: 30px;
--spacing-lg: 50px;
--spacing-xl: 100px;
--spacing-xxl: 175px;
--spacing-xxxl: 250px;
--font-primary: "Lato", sans-serif;
--font-secondary: "Exo", sans-serif;
--font-size-content-desktop: 1.125rem;
--font-size-content-mobile: 1rem;
--font-size-button: 1.25rem;
/// Grosseur des textes
--font-size-h1-desktop: 3rem;
--font-size-h1-mobile: 2.5rem;
--font-size-h2-desktop: 1.8rem;
--font-size-h2-mobile: 1.6rem;
--font-size-h3-desktop: 1.2rem;
--font-size-h3-mobile: 1.125rem;
@media screen and(max-width: 500px){
--font-size-h1-desktop: 3.5rem;
}
}
html[data-theme='dark']:root{
--color-text-primary: var(--color-white);
--color-text-secondary: var(--color-black);
--color-bg-primary: var(--color-black);
--color-bg-secondary: #222428;
--color-bg-tertiary: #222428;
}
$breakpoint-xs: 375px;
$breakpoint-sm: 768px;
$breakpoint-md: 1024px;
$breakpoint-lg: 1440px;
$breakpoint-xl: 1920px;
.docusaurus-highlight-code-line {
background-color: rgba(0, 0, 0, 0.1);
display: block;
margin: 0 calc(-1 * var(--ifm-pre-padding));
padding: 0 var(--ifm-pre-padding);
}
.navbar__brand {
height: 3rem;
.navbar__logo {
height: 3.5rem;
}
}
.docs-wrapper main .container article .markdown header:nth-child(2) {
display: none;
}
main article a:link {
font-weight: var(--ifm-font-weight-bold);
}
html[data-theme='dark'] .docusaurus-highlight-code-line {
background-color: rgba(0, 0, 0, 0.3);
}
.header-github-link {
//margin-right: 0.5em;
padding: 4px;
}
.header-github-link:hover {
opacity: 0.6;
}
.header-github-link:before {
content: '';
width: 24px;
height: 24px;
display: flex;
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat;
}
html[data-theme=dark] .header-github-link:before {
filter: invert(1);
}
.header-twitter-link {
margin-right: 0.5em;
padding: 4px;
}
.header-twitter-link:hover {
opacity: 0.6;
}
.header-twitter-link:before {
content: '';
width: 25px;
height: 25px;
display: flex;
background: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3C!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3Csvg version='1.0' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 24 24' enable-background='new 0 0 24 24' xml:space='preserve'%3E%3Cpath fill='%23191717' d='M12,0C5.4,0,0,5.4,0,12s5.4,12,12,12s12-5.4,12-12S18.6,0,12,0z M17.4,9.3c0,0.1,0,0.2,0,0.4 c0,3.6-2.8,7.8-7.8,7.8c-1.5,0-3-0.4-4.2-1.2c0.2,0,0.4,0,0.7,0c1.2,0,2.4-0.4,3.4-1.2c-1.2,0-2.2-0.8-2.6-1.9 c0.4,0.1,0.8,0.1,1.2,0c-1.3-0.3-2.2-1.4-2.2-2.7v0c0.4,0.2,0.8,0.3,1.3,0.3C5.9,9.9,5.5,8.3,6.2,7.1c1.4,1.7,3.5,2.8,5.7,2.9 c0-0.2-0.1-0.4-0.1-0.6c0-0.8,0.3-1.5,0.9-2c1.1-1,2.9-1,3.9,0.1c0.6-0.1,1.2-0.3,1.8-0.7c-0.2,0.6-0.6,1.2-1.2,1.5 c0.5-0.1,1.1-0.2,1.6-0.4C18.4,8.4,17.9,8.9,17.4,9.3z'/%3E%3C/svg%3E%0A") no-repeat;
}
html[data-theme=dark] .header-twitter-link:before {
filter: invert(1);
}
// Styles applicable to "docs" and "api" ///////////////////////////////////////////////////////////
.docs-wrapper article .markdown header:nth-of-type(2) {
display: none;
}
div[role=banner] {
background-color: var(--ifm-color-primary);
}
// Styles applicable to menu ///////////////////////////////////////////////////////////////////////
li a.dropdown__link svg {
display: none;
}
li a.recommended {
font-weight: bold;
}
li a.dropdown__link.external svg {
display: inline;
}
// Styles applicable to parameter tables in API ////////////////////////////////////////////////////
.parameter-table-container {
table {
display: table;
width: 100%;
th:first-of-type {
width: 18%;
}
th:nth-of-type(2) {
width: 18%;
}
th:nth-of-type(3) {
width: 18%;
}
th:nth-of-type(4) {
width: 46%;
}
}
}
/* ==========================================================================
HEADINGS / ELEMENT
========================================================================== */
h1 {
font-family: var(--font-secondary);
font-weight: 900;
font-size: var(--font-size-h1-mobile);
@media screen and (min-width: $breakpoint-md) {
font-size: var(--font-size-h1-desktop);
}
}
h2 {
font-family: var(--font-secondary);
font-weight: 700;
font-size: var(--font-size-h2-mobile);
margin-top: 2.4rem;
@media screen and (min-width: $breakpoint-md) {
font-size: var(--font-size-h2-desktop);
}
}
h3 {
font-family: var(--font-secondary);
font-weight: 700;
font-size: var(--font-size-h3-mobile);
@media screen and (min-width: $breakpoint-md) {
font-size: var(--font-size-h3-desktop);
}
}
html[data-theme=light] a.cem-logo img {
filter: invert(1);
}
a.user-icon img {
border-radius: 50px;
border: 1px solid black;
margin: 0.5rem;
}
================================================
FILE: website/src/css/index.css
================================================
.hero {
padding: var(--spacing-lg) 0;
min-height: 60vh;
color: var(--color-text-primary);
display: flex;
align-items: center;
background: var(--color-bg-primary);
text-align: center;
}
@media screen and (max-width: 1000px) {
.hero {
min-height: 40vh;
padding-top: 0;
}
}
.hero .logo {
max-width: 600px;
height: 300px;
background: center url("/img/webmidijs-logo-dark.svg") no-repeat;
margin: auto;
}
@media screen and (max-width: 500px) {
.hero .logo {
height: 10em;
margin: var(--spacing-md) 0;
}
}
html[data-theme=dark] .hero .logo {
background: center url("/img/webmidijs-logo-light.svg") no-repeat;
}
.hero span {
font-family: var(--font-secondary);
font-size: 2rem;
display: block;
margin-bottom: var(--spacing-sm);
font-weight: bold;
color: var(--color-accent);
}
.hero .cta {
display: flex;
flex-wrap: wrap;
justify-content: center;
/*@media screen and(max-width: 1000px){
justify-content: center;
}*/
}
.hero .cta .Button {
margin: var(--spacing-sm) 0 0 var(--spacing-sm);
}
.hero .cta .Button:nth-child(1) {
margin-left: 0;
}
.hero .img {
margin: auto;
}
@media screen and (max-width: 1000px) {
.hero .texts {
order: 2;
}
}
/*======== Presentation ========*/
.presentation {
margin: var(--spacing-lg) 0;
}
.presentation h2 {
text-align: center;
}
.presentation p {
font-size: 1.5rem;
margin: var(--spacing-sm) 0;
}
.presentation .media {
background-color: var(--color-bg-tertiary);
border-radius: 20px;
width: 100%;
height: 375px;
display: flex;
justify-content: center;
align-items: center;
margin: var(--spacing-xl) auto;
box-shadow: 10px 10px 0 rgba(255, 208, 0, 0.5);
}
@media screen and (max-width: 1000px) {
.presentation .media {
width: 90%;
margin: var(--spacing-md) auto;
}
}
.presentation .media .imgMedia {
width: 80%;
height: 80%;
}
html[data-theme=light] .presentation .media .imgMedia {
filter: invert(1);
}
.presentation .media :nth-child(1) {
background: center url("/img/front-page/webmidi-demonstration.svg") no-repeat;
}
@media screen and (max-width: 1000px) {
.presentation .media :nth-child(1) {
background: center url("/img/front-page/webmidi-demonstration-vertical.svg") no-repeat;
}
}
/*# sourceMappingURL=index.css.map */
================================================
FILE: website/src/css/index.scss
================================================
.hero{
padding: var(--spacing-lg) 0;
min-height: 60vh;
color: var(--color-text-primary);
display: flex;
align-items: center;
background: var(--color-bg-primary);
text-align: center;
@media screen and(max-width: 1000px){
min-height: 40vh;
padding-top: 0;
}
.logo {
max-width: 600px;
height: 300px;
background: center url("/img/webmidijs-logo-dark.svg") no-repeat;
margin: auto;
@media screen and(max-width: 500px){
height: 10em;
margin: var(--spacing-md) 0;
}
html[data-theme=dark] &{
background: center url("/img/webmidijs-logo-light.svg") no-repeat;
}
}
span {
font-family: var(--font-secondary);
font-size: 2rem;
display: block;
margin-bottom: var(--spacing-sm);
font-weight: bold;
color: var(--color-accent);
}
.cta {
display: flex;
flex-wrap: wrap;
justify-content: center;
.Button{
margin: var(--spacing-sm) 0 0 var(--spacing-sm);
}
.Button:nth-child(1){
margin-left: 0;
}
/*@media screen and(max-width: 1000px){
justify-content: center;
}*/
}
.img{
margin: auto;
}
.texts{
@media screen and(max-width: 1000px){
order: 2;
}
}
}
/*======== Presentation ========*/
.presentation {
margin: var(--spacing-lg) 0;
h2 {
text-align: center;
}
p{
font-size: 1.5rem;
margin: var(--spacing-sm) 0;
}
.media {
background-color: var(--color-bg-tertiary);
border-radius: 20px;
width: 100%;
height: 375px;
display: flex;
justify-content: center;
align-items: center;
margin: var(--spacing-xl) auto;
box-shadow: 10px 10px 0 rgba(255, 208, 0, 0.5);
@media screen and(max-width: 1000px){
width: 90%;
margin: var(--spacing-md) auto;
}
.imgMedia{
width: 80%;
height: 80%;
html[data-theme=light] &{
filter: invert(1);
}
}
}
.media :nth-child(1){
background: center url("/img/front-page/webmidi-demonstration.svg") no-repeat;
@media screen and(max-width: 1000px){
background: center url("/img/front-page/webmidi-demonstration-vertical.svg") no-repeat;
}
}
}
================================================
FILE: website/src/pages/about/index.md
================================================
# About
## Who created this?
**WEBMIDI.js** is a passion project of mine. I am Jean-Philippe Côté (a.k.a.
[djip.co](https://djip.co)), an
[academic](https://www.cegepmontpetit.ca/cegep/recherche/professeurs-chercheurs/jean-philippe-cote)
and artist with particular interests in creative coding, interactive arts and music technology. You
can reach out to me in different ways:
* Twitter: **[@djipco](https://twitter.com/djipco)** or **[@webmidijs](https://twitter.com/webmidijs)**
* Website: **[https://djip.co/](https://djip.co)**
* GitHub: **[https://github.com/djipco/](https://github.com/djipco)**
One of my students, **Jean-Marie Gariépy** has also been helping out in various capacities with the
creation of this website. Let's all thank him for his contribution! 👏
## Sponsoring the project
You can [sponsor the project](https://github.com/sponsors/djipco/) by becoming a GitHub sponsor. All
you need is a GitHub account. If you see value in this library, please consider a contribution. 🙏🏻
## Licensing
Starting with version 3.0.0, this library is licensed under the **Apache License, Version 2.0**. You
may not use this library except in compliance with this license. You may obtain a copy at:
* [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)
Unless required by applicable law or agreed to in writing, software distributed under this license
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the above license for the specific language governing permissions and limitations.
© 2015-2023, Jean-Philippe Côté.
================================================
FILE: website/src/pages/index.js
================================================
import React from "react";
import useBaseUrl from '@docusaurus/useBaseUrl';
import Layout from "@theme/Layout";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import Button from "../components/Button";
import Column from "../components/Column";
import InformationBar from "../components/InformationBar";
function HomepageHero() {
const {siteConfig} = useDocusaurusContext();
return (
{siteConfig.tagline}
Get started in 5 minutes!
);
}
function Presentation() {
const {siteConfig} = useDocusaurusContext();
return (
What is {siteConfig.title}?
The existing Web MIDI API is a really exciting addition to the web platform
allowing a web page to interact with MIDI musical instruments .
However, while great, most developers will find the original API to be
too low-level for their needs. Having to perform binary arithmetic
or needing to read the 300-page MIDI spec is no fun (trust us on this!).
The goal behind WEBMIDI.js is to get you started with your web-based
MIDI project as efficiently as possible.
);
}
export default function Home() {
const {siteConfig} = useDocusaurusContext();
return (
Version 3.0 has been released!
Subscribe to the newsletter
to learn about all the new features.
);
}
================================================
FILE: website/src/pages/index.module.css
================================================
/* stylelint-disable docusaurus/copyright-header */
/**
* CSS files with the .module.css suffix will be treated as CSS modules
* and scoped locally.
*/
/*
.heroBanner {
padding: 4rem 0;
text-align: center;
position: relative;
overflow: hidden;
}
@media screen and (max-width: 966px) {
.heroBanner {
padding: 2rem;
}
}
.buttons {
display: flex;
align-items: center;
justify-content: center;
}
*/
.hero {
padding: var(--spacing-lg);
min-height: 60vh;
display: flex;
align-items: center;
}
.hero h1 {
margin: 0;
text-transform: uppercase;
}
.hero span {
font-family: var(--font-secondary);
font-size: 1.56rem;
display: block;
margin-bottom: var(--spacing-sm);
}
.hero .cta {
display: flex;
}
.hero .cta .button:nth-child(n) {
margin-left: var(--spacing-md);
}
.hero .cta .button:first-child {
margin-left: 0;
}
/*======== Boutons ========*/
.button {
background-color: var(--color-accent);
width: fit-content;
border-radius: 10px;
position: relative;
overflow: hidden;
}
.button a {
display: block;
padding: var(--spacing-sm) var(--spacing-lg);
font-weight: bold;
font-family: var(--font-secondary);
font-size: 1.56rem;
position: relative;
z-index: 2;
}
.button .button--bg {
width: 100%;
height: 100%;
position: absolute;
}
.button.button-bg-full .button--bg {
background-color: var(--color-accent-lighter);
z-index: 1;
top: 0;
left: -100%;
transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.button.button-bg-full:hover .button--bg {
left: 0;
transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.button.button-bg-empty {
background-color: rgba(0, 0, 0, 0);
border: solid 4px var(--color-accent);
}
.button.button-bg-empty a {
color: var(--color-accent);
transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.button.button-bg-empty .button--bg {
background-color: var(--color-accent-lighter);
z-index: 1;
top: 0;
left: -100%;
transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.button.button-bg-empty:hover a {
color: var(--color-white);
transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
.button.button-bg-empty:hover .button--bg {
left: 0;
transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
/*======== Columns ========*/
.col-2 {
display: grid;
grid-template-columns: 48% 48%;
justify-content: space-between;
align-items: center;
}
/*# sourceMappingURL=index.module.css.map */
================================================
FILE: website/src/pages/index.module.scss
================================================
/* stylelint-disable docusaurus/copyright-header */
/**
* CSS files with the .module.css suffix will be treated as CSS modules
* and scoped locally.
*/
/*
.heroBanner {
padding: 4rem 0;
text-align: center;
position: relative;
overflow: hidden;
}
@media screen and (max-width: 966px) {
.heroBanner {
padding: 2rem;
}
}
.buttons {
display: flex;
align-items: center;
justify-content: center;
}
*/
.hero{
padding: var(--spacing-lg);
min-height: 60vh;
display: flex;
align-items: center;
h1 {
margin: 0;
text-transform: uppercase;
}
span {
font-family: var(--font-secondary);
font-size: 1.56rem;
display: block;
margin-bottom: var(--spacing-sm);
}
.cta {
display: flex;
.button:nth-child(n) {
margin-left: var(--spacing-md);
}
.button:first-child {
margin-left: 0;
}
}
}
/*======== Boutons ========*/
$ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86);
.button {
background-color: var(--color-accent);
width: fit-content;
border-radius: 10px;
position: relative;
overflow: hidden;
a {
display: block;
padding: var(--spacing-sm) var(--spacing-lg);
font-weight: bold;
font-family: var(--font-secondary);
font-size: 1.56rem;
position: relative;
z-index: 2;
}
.button--bg {
width: 100%;
height: 100%;
position: absolute;
}
&.button-bg-full {
.button--bg {
background-color: var(--color-accent-lighter);
z-index: 1;
top: 0;
left: -100%;
transition: all 0.3s $ease-in-out-circ;
}
&:hover {
.button--bg {
left: 0;
transition: all 0.3s $ease-in-out-circ;
}
}
}
&.button-bg-empty {
background-color: rgba(0, 0, 0, 0%);
border: solid 4px var(--color-accent);
a {
color: var(--color-accent);
transition: all 0.3s $ease-in-out-circ;
}
.button--bg {
background-color: var(--color-accent-lighter);
z-index: 1;
top: 0;
left: -100%;
transition: all 0.3s $ease-in-out-circ;
}
&:hover {
a {
color: var(--color-white);
transition: all 0.3s $ease-in-out-circ;
}
.button--bg {
left: 0;
transition: all 0.3s $ease-in-out-circ;
}
}
}
}
/*======== Columns ========*/
.col-2 {
display: grid;
grid-template-columns: 48% 48%;
justify-content: space-between;
align-items: center;
}
================================================
FILE: website/src/pages/research/index.md
================================================
# Academic Research
I invite all academics and researchers to show their support for this project by properly citing it
wherever appropriate in your publications and references.
## Citing this Software
I wrote a
[paper about WEBMIDI.js](https://nime.pubpub.org/pub/user-friendly-midi-in-the-web-browser) and,
more specifically, about how it tries to address the usability shortcomings of the Web MIDI API. I
invite academics to cite it in their publication whenever appropriate:
> Côté, J.-P. (2022). User-Friendly MIDI in the Web Browser. NIME 2022.
> https://doi.org/10.21428/92fbeb44.388e4764
You can also cite the library itself like so (APA style):
> Côté, J. P. (2025). WEBMIDI.js v3.1.14 [Computer Software]. Retrieved from
> https://github.com/djipco/webmidi
## Papers Citing Usage of WEBMIDI.js
* Arora, A., Tandori, E., Marshall, J. et Favilla, S. (2025). **Designing Sensory NIME for Autism**
(p. 436‑442). Proceedings of the International Conference on New Interfaces for Musical
Expression. https://www.nime.org/proc/nime2025_63/index.html
* Leischner, V., & Husa, P. (2023). Sonification of a Juggling Performance Using Spatial Audio.
Proceedings of the ACM on Computer Graphics and Interactive Techniques, 6(2), 1–6.
https://doi.org/10.1145/3597619
* Baratè, A., & Ludovico, L. A. (2022). **Web MIDI API: State of the Art and Future Perspectives**.
Journal of the Audio Engineering Society, 70(11), 918–925.
https://www.aes.org/e-lib/browse.cfm?elib=22016
* Graber, Z., & Henrion, W. (2021). **Music For Me**. Computer Science and Engineering Senior Theses.
https://scholarcommons.scu.edu/cseng_senior/203
* Kostek, B. (Éd.). (2021). **Postępy badań w inżynierii dźwięku i obrazu**. Politechnika Wrocławska,
Oficyna Wydawnicza. https://doi.org/10.37190/ido2021
* Walczak, M., & Łukasik, E. (2021). **Rozproszony system generowania, edycji i transmisji dźwięku
wykorzystujący interfejsy Web Audio API, WebRTC i Web MIDI API**. In Postępy badań w inżynierii
dźwięku i obrazu: Nowe trendy i zastosowania technologii dźwięku wielokanałowego oraz badania
jakości dźwięku (pp. 83–104). Oficyna Wydawnicza Politechniki Wrocławskiej.
https://doi.org/10.37190/ido2021
* Krawczuk, J. (2020). **Real-Time and Post-Hoc-Visualizations of Guitar Performances as a Support
for Music Education**. https://doi.org/10/gkb622
* Lundh Haaland, M. (2020). **The Player as a Conductor: Utilizing an Expressive Performance
System to Create an Interactive Video Game Soundtrack** (Dissertation). Retrieved from
http://urn.kb.se/resolve?urn=urn:nbn:se:kth:diva-281324
* Bazin, T. & Hadjeres, G. (2019). **NONOTO: A Model-agnostic Web Interface for Interactive
Music Composition by Inpainting**, presented at 10th International Conference on Computational
Creativity, Charlotte, 2019. Retrieved from https://arxiv.org/abs/1907.10380
* Smith, A. (2019). **Sonification: Turning the Yield Curve into Music**. FT.com.
http://search.proquest.com/docview/2191715473/fulltext/3D4C05EAFC6A4AEEPQ/1?accountid=14719
* Cárdenas, A. & Mauricio B. (2018). **Diseño y desarrollo de un prototipo para integración de
Tecnología de Tracking 3d con Tecnología MIDI** [Doctoral dissertation, Pontificia Universidad
Católica del Ecuador]. Retrieved from http://repositorio.puce.edu.ec/handle/22000/15838
If you are using WEBMIDI.js in your research, I would love to know about it. To notify me, you can
simply [drop me a note](https://djip.co/contact).
================================================
FILE: website/src/pages/showcase/index.md
================================================
---
toc_max_heading_level: 2
---
# Showcase
WebMidi.js is being used by amazing people to create awesome projects. Here are some examples of
what the library can do.
:::tip Contribute
I would love for your project to be listed here. To add it, simply
[modify](https://github.com/djipco/webmidi/edit/develop/website/src/pages/showcase/index.md) this
page on GitHub and submit a pull request.
:::
---
## DAWs & Editors
* ### [DAWG](https://dawg.dev/)
Created by: **Jacob Smith, Amir Eldesoky, Alex ODonnell & Matt DeSilva**
Digital Audio Workstation (DAW) application built using web technologies and distributed as a
native application using Electron.
## Education
* ### [Learn Push 2](https://github.com/greyivy/learn-push2-with-svelte)
Created by: **Ivy**
A website to learn chords, scales and music theory with the Push 2 controller.
* ### [Raaga](https://raaga.riteshkr.com/)
Created by: **Ritesh Kumar, Jack Hsu, Prateek Bhatnagar, Sruthi, Majid Hajian & Rohit Kokate**
An online app to play and learn music on a keyboard.
* ### [Shared Piano](https://musiclab.chromeexperiments.com/Shared-Piano/)
Created by: **Yotam Mann**
Shared Piano is a simple tool for remote music teaching and collaboration that lets you play music
together live on the web.
* ### [Chromatone](https://chromatone.center/)
Created by: **Denis Starov**
An interactive educational web-site for the visual music language based on associating chromatic notes with spectral colors.
* ### [Chromatone](https://chromatone.center/)
Created by: **Denis Starov**
An interactive educational web-site for the visual music language based on associating chromatic
notes with spectral colors.
* ### [WebAudio Generator](https://webaudio.simmsreeve.com/)
Created by: **Joe Reeve & Magnus**
A user interface to facilitate the generation of linear WebAudio code.
* ### [Midi Sandbox](https://midisandbox.com/)
Created by: **Jon Lee**
A collection of midi responsive widgets made to aid musicians, teachers, and students in their education,
creative process, and practice routine.
* ### [Virtual Piano Keyboard](https://muted.io/piano/)
Created by: **Sébastien Noël**
A simple way to practice playing a piano keyboard online with helpers like chord identification and locking to a scale.
## Experiments
* ### [A.I. Duet](https://experiments.withgoogle.com/ai-duet)
Created by: **Yotam Mann**
This experiment lets you play a piano duet with the computer. Just play some notes, and the
computer will respond to your melody.
* ### [Eternal](https://github.com/kousun12/eternal)
Created by: **Rob Cheung**
Eternal is node-based sound and music programming and exploration environment.
* ### [Mideo](https://github.com/jwktje/mideo)
Created by: **Jan Willem Kilkman**
A Mac application that turns videos into music. It uses color tracking in combination with a grid
to generate MIDI data.
* ### [Sonification: turning the yield curve into music](https://www.ft.com/content/80269930-40c3-11e9-b896-fe36ec32aece)
Created by: **Alan Smith**
This experiment turns the US yield curve into music and uses WEBMIDI.js to generate the MIDI note
messages.
* ### [Seeing Music](https://experiments.withgoogle.com/seeing-music)
Created by: **Jay Alan Zimmerman, Yotam Mann, Claire Kearney-Volpe, Luisa Pereira, Kyle Phillips,
and Google Creative Lab**
An experiment to experience music visually. This is a tool for visualizing music. You can turn on
your mic to sing or play sounds. You can also drop in your own audio or video file.
* ### [webmidirtc](https://github.com/philmillman/webmidirtc)
Created by: **philmillman**
This project is meant to demonstrate controlling hardware synths using Web MIDI (via WebMidi.js)
over a WebRTC video call (using Daily.)
## Music Hardware Control
* ### [Mercury7 editor](https://github.com/francoisgeorgy/mercury7-web-editor)
Created by: **François Georgy**
Control your Meris Mercury7 pedal with your web browser. View all the pedal's settings at once.
* ### [MicroFreak Reader](https://studiocode.dev/doc/microfreak-reader/)
Created by: **François Georgy**
An application to read and display the presets stored in the Arturia MicroFreak memory.
* ### [NTS1 Web Controller](https://directions4.github.io/nts1-web-controller/)
Created by: **Satoru Shikita**
Web browser interface to control the KORG Nu:Tekt NTS-1.
* ### [Pacer Editor](https://studiocode.dev/pacer-editor/#/)
Created by: **François Georgy**
Web interface for the Nektar Pacer MIDI controller.
* ### [Soundshed](https://soundshed.com/)
Created by: **Christopher Cook & Lavabyrd**
A Desktop app to browse and manage guitar amp tones. Control your bluetooth amp, jam to video
backing tracks.
* ### [Sonicware Liven XFM Web Editor](https://aesqe.github.io/sonicware-liven-xfm-web-editor/)
Created by: **Bruno Babić**
Web editor for the Sonicware Liven XFM groovebox.
## Components & Languages
* ### [FAUST](https://faust.grame.fr/)
Created by: **Grame Research Lab**
The online Faust IDE can be used to edit, compile and run Faust code from the Web Browser.
* ### [Midi Bricks](https://midi-bricks.timsusa.vercel.app/)
Created by: **Tim Susa**
A tool to build custom interfaces for MIDI control.
* ### [React Audio Tools](http://react-audio-tools.surge.sh/)
Created by: **ambewas**
A set of React components to build things with the Web Audio and Web MIDI APIs.
## Live Coding
* ### [Sema](https://sema.codes/)
Created by: **Francisco Bernardo, Chris Kiefer & Thor Magnusson**
Sema is a playground where you can rapidly prototype live coding mini-languages for signal
synthesis, machine learning and machine listening.
## Online Synthesizers
* ### [synth.kitchen](https://synth.kitchen/)
Created by: **Rain Rudnick**
In-browser modular synthesis with Web Audio and Web MIDI.
## Notation
* ### [Inscore](https://inscore.grame.fr/)
Created by: **Dominique Fober, guillaumeg03 & Gabriel Leptit-Aimon**
An environment for the design of interactive, augmented, dynamic musical scores.
* ### [Nonoto](https://github.com/SonyCSLParis/NONOTO)
Created by: **Théis Bazin, Sebastian Haas & Gaetan Hadjeres**
An A.I.-powered interactive score distributed as an Electron application. It allows users to
interact intuitively with any existing music composition algorithm.
## Robotics
* ### [Dexter Development Environment](https://www.hdrobotic.com/software)
Created by: **Haddington Dynamics**
The Dexter Development Environment, which is used to control the Dexter 7-axis robot arm, bundles
WEBMIDI.js for MIDI control of the device.
## SysEx Librarian
* ### [Patchup](https://www.patchup.app)
Created by: **Middledot Tech**
Patchup is a free web app for sending, receiving, saving, and managing System Exclusive messages. All data is saved to your browser's local storage.
================================================
FILE: website/src/pages/sponsors/index.md
================================================
# Sponsors
:::tip Consider a sponsorship
Please help me grow and nurture WEBMIDI.js by ❤️ [sponsoring](https://github.com/sponsors/djipco)
the project on GitHub.
:::
## Sponsoring Organizations
---
## Sponsors
---
## About Software Development...
As you surely know, proper software development takes time. This time is spent on coding new
features but also on various other important tasks such as writing useful documentation, answering
questions, reviewing issues, fixing bugs, writing tests, etc.
While WEBMIDI.js started as a hobby project, it is quietly becoming the "go to" library for MIDI on
the web. Hopefully, your contributions will make allow me to continue developing, maintaining and
advocating for this project that I cherish.
Thank you so much. 😀
================================================
FILE: website/src/pages/tester/index.js
================================================
import React from "react";
import Layout from "@theme/Layout";
import {Helmet} from "react-helmet";
// import useBaseUrl from "@docusaurus/useBaseUrl";
// import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
// const piano = new Nexus.Piano("#target",{
// size: [500,125],
// mode: "button", // "button", "toggle", or "impulse"
// lowNote: 24,
// highNote: 60
// })
function Tester() {
return (
{/*
*/}
Edit pages/helloReact.js and save to reload.
);
}
export default Tester;
================================================
FILE: website/src/theme/CodeBlock/index.js
================================================
import React from 'react';
import CodeBlock from '@theme-original/CodeBlock';
export default function CodeBlockWrapper(props) {
return (
<>
>
);
}
================================================
FILE: website/src/theme/Footer/index.js
================================================
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from "react";
import {useThemeConfig} from "@docusaurus/theme-common";
import useBaseUrl from "@docusaurus/useBaseUrl";
import styles from "./styles.module.scss";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import {Helmet} from "react-helmet";
function Footer() {
const {footer} = useThemeConfig();
// eslint-disable-next-line no-unused-vars
const {sponsors = []} = useDocusaurusContext();
const {copyright,} = footer || {};
if (!footer) {
return null;
}
const sponsorLogoPath = useBaseUrl("img/sponsors/edouard-montpetit-logo.svg");
return (
This project is supported in part by:
{copyright ? (
) : null}
);
}
export default Footer;
================================================
FILE: website/src/theme/Footer/styles.module.css
================================================
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
.footerLogoLink {
opacity: 0.5;
transition: opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default);
}
.footerLogoLink:hover {
opacity: 1;
}
*/
.footer .container {
display: flex;
justify-content: space-between;
align-items: center;
}
@media screen and (max-width: 1000px) {
.footer .container {
flex-direction: column;
}
.footer .container .copyright {
margin-top: var(--spacing-md);
}
}
.footer .container p {
margin: 0 var(--spacing-md) 0 0;
}
.footer .container .sponsor {
display: flex;
align-items: center;
}
.footer .container .sponsor .sponsors {
display: flex;
flex-wrap: wrap;
}
.footer .container img {
width: 150px;
height: 50px;
object-fit: fill;
}
html[data-theme=light] .footer .container img {
filter: invert(1);
}
/*# sourceMappingURL=styles.module.css.map */
================================================
FILE: website/src/theme/Footer/styles.module.scss
================================================
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
.footerLogoLink {
opacity: 0.5;
transition: opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default);
}
.footerLogoLink:hover {
opacity: 1;
}
*/
.footer {
.container {
display: flex;
justify-content: space-between;
align-items: center;
@media screen and (max-width: 1000px){
flex-direction: column;
.copyright{
margin-top: var(--spacing-md);
}
}
p{
margin: 0 var(--spacing-md) 0 0;
}
.sponsor{
display: flex;
align-items: center;
.sponsors{
display: flex;
flex-wrap: wrap;
}
}
img {
width: 150px;
height: 50px;
object-fit: fill;
html[data-theme=light] &{
filter: invert(1);
}
}
}
}
================================================
FILE: website/src/theme/Navbar/index.js
================================================
import React from 'react';
import Navbar from '@theme-original/Navbar';
export default function NavbarWrapper(props) {
return (
<>
>
);
}
================================================
FILE: website/src/theme/Navbar/styles.module.css
================================================
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
Hide toggle in small viewports
*/
@media (max-width: 996px) {
.toggle {
display: none;
}
}
.navbarHideable {
transition: transform var(--ifm-transition-fast) ease;
}
.navbarHidden {
transform: translate3d(0, calc(-100% - 2px), 0);
}
.navbarSidebarToggle {
margin-right: 1rem;
}
.navbarSidebarMobile ul li a{
padding: 10px var(--spacing-sm);
}
================================================
FILE: website/static/.nojekyll
================================================
================================================
FILE: website/static/js/newsletter-popup.js
================================================
// eslint-disable-next-line max-len
!function(c,h,i,m,p){m=c.createElement(h),p=c.getElementsByTagName(h)[0],m.async=1,m.src=i,p.parentNode.insertBefore(m,p)}(document,"script","https://chimpstatic.com/mcjs-connected/js/users/4ad018201643381a89d30000c/0e8ce2085fb209fed1b656a74.js");
================================================
FILE: website/static/styles/default.css
================================================
a {
text-decoration: none;
}
a:link {
color: #ffd000;
}
a:visited {
color: #ffd000;
}
a:hover {
color: #ffd000;
text-decoration: underline;
}
a:active {
color: #ffd000;
}
body {
font-family: "Yanone Kaffeesatz", sans-serif;
font-size: 24px;
line-height: 1.25;
text-align: justify;
}
header {
margin: 5vh auto;
width: 75%;
max-width: 500px;
}
header img {
max-width: 100%;
}
main {
margin: 0 auto;
width: 75%;
max-width: 500px;
padding: 0 50px;
}
/*# sourceMappingURL=default.css.map */
================================================
FILE: website/static/styles/default.scss
================================================
// Colors
$base-color: #ffd000;
a {
text-decoration: none;
&:link {
color: $base-color;
}
&:visited {
color: $base-color;
}
&:hover {
color: $base-color;
text-decoration: underline;
}
&:active {
color: $base-color;
}
}
body {
font-family: 'Yanone Kaffeesatz', sans-serif;
font-size: 24px;
line-height: 1.25;
text-align: justify;
}
header {
margin: 5vh auto;
width: 75%;
max-width: 500px;
img {
max-width: 100%;
}
}
main {
margin: 0 auto;
width: 75%;
max-width: 500px;
padding: 0 50px;
}
================================================
FILE: website/static/styles/jsdoc.css
================================================
.page-header, pre.code-toolbar > .toolbar:hover {
background-color: red;
}