Update webmidi typings (#41300)

* Update webmidi typings

* Add eventlistener overloads
* Update 'type' field to string literal to allow for use as a
discriminated union

* Fix lint

* Update comment

* Fix lint & tests
This commit is contained in:
Caleb Eggensperger 2020-01-22 11:23:07 -05:00 committed by Nathan Shively-Sanders
parent d21e32460f
commit 1326b348ff
2 changed files with 226 additions and 178 deletions

View File

@ -1,196 +1,237 @@
// Type definitions for Web MIDI API 2.0
// Project: http://www.w3.org/TR/webmidi/, https://github.com/djipco/webmidi
// Definitions by: six a <https://github.com/lostfictions>
// Project: http://www.w3.org/TR/webmidi/
// Definitions by: DefinitelyTyped <https://github.com/DefinitelyTyped>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
interface Navigator {
/**
* When invoked, returns a Promise object representing a request for access to MIDI
* devices on the user's system.
*/
requestMIDIAccess(options?: WebMidi.MIDIOptions): Promise<WebMidi.MIDIAccess>;
/**
* When invoked, returns a Promise object representing a request for access to MIDI
* devices on the user's system.
*/
requestMIDIAccess(options?: WebMidi.MIDIOptions): Promise<WebMidi.MIDIAccess>;
}
declare namespace WebMidi {
interface MIDIOptions {
/**
* This member informs the system whether the ability to send and receive system
* exclusive messages is requested or allowed on a given MIDIAccess object.
*/
sysex: boolean;
}
/**
* This is a maplike interface whose value is a MIDIInput instance and key is its
* ID.
*/
type MIDIInputMap = Map<string, MIDIInput>;
/**
* This is a maplike interface whose value is a MIDIOutput instance and key is its
* ID.
*/
type MIDIOutputMap = Map<string, MIDIOutput>;
interface MIDIAccess extends EventTarget {
/**
* The MIDI input ports available to the system.
*/
inputs: MIDIInputMap;
interface MIDIOptions {
/**
* This member informs the system whether the ability to send and receive system
* exclusive messages is requested or allowed on a given MIDIAccess object.
*/
sysex: boolean;
}
/**
* The MIDI output ports available to the system.
* This is a maplike interface whose value is a MIDIInput instance and key is its
* ID.
*/
outputs: MIDIOutputMap;
type MIDIInputMap = Map<string, MIDIInput>;
/**
* The handler called when a new port is connected or an existing port changes the
* state attribute.
* This is a maplike interface whose value is a MIDIOutput instance and key is its
* ID.
*/
onstatechange(e: MIDIConnectionEvent): void;
type MIDIOutputMap = Map<string, MIDIOutput>;
/**
* This attribute informs the user whether system exclusive support is enabled on
* this MIDIAccess.
*/
sysexEnabled: boolean;
}
interface MIDIAccess extends EventTarget {
/**
* The MIDI input ports available to the system.
*/
inputs: MIDIInputMap;
type MIDIPortType = "input" | "output";
/**
* The MIDI output ports available to the system.
*/
outputs: MIDIOutputMap;
type MIDIPortDeviceState = "disconnected" | "connected";
/**
* The handler called when a new port is connected or an existing port changes the
* state attribute.
*/
onstatechange(e: MIDIConnectionEvent): void;
type MIDIPortConnectionState = "open" | "closed" | "pending";
addEventListener(
type: 'statechange',
listener: (this: this, e: MIDIConnectionEvent) => any,
options?: boolean | AddEventListenerOptions,
): void;
addEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions,
): void;
interface MIDIPort extends EventTarget {
/**
* A unique ID of the port. This can be used by developers to remember ports the
* user has chosen for their application.
*/
id: string;
/**
* This attribute informs the user whether system exclusive support is enabled on
* this MIDIAccess.
*/
sysexEnabled: boolean;
}
/**
* The manufacturer of the port.
*/
manufacturer?: string;
type MIDIPortType = 'input' | 'output';
/**
* The system name of the port.
*/
name?: string;
type MIDIPortDeviceState = 'disconnected' | 'connected';
/**
* A descriptor property to distinguish whether the port is an input or an output
* port.
*/
type: MIDIPortType;
type MIDIPortConnectionState = 'open' | 'closed' | 'pending';
/**
* The version of the port.
*/
version?: string;
interface MIDIPort extends EventTarget {
/**
* A unique ID of the port. This can be used by developers to remember ports the
* user has chosen for their application.
*/
id: string;
/**
* The state of the device.
*/
state: MIDIPortDeviceState;
/**
* The manufacturer of the port.
*/
manufacturer?: string;
/**
* The state of the connection to the device.
*/
connection: MIDIPortConnectionState;
/**
* The system name of the port.
*/
name?: string;
/**
* The handler called when an existing port changes its state or connection
* attributes.
*/
onstatechange(e: MIDIConnectionEvent): void;
/**
* A descriptor property to distinguish whether the port is an input or an output
* port.
*/
type: MIDIPortType;
/**
* Makes the MIDI device corresponding to the MIDIPort explicitly available. Note
* that this call is NOT required in order to use the MIDIPort - calling send() on
* a MIDIOutput or attaching a MIDIMessageEvent handler on a MIDIInputPort will
* cause an implicit open().
*
* When invoked, this method returns a Promise object representing a request for
* access to the given MIDI port on the user's system.
*/
open(): Promise<MIDIPort>;
/**
* The version of the port.
*/
version?: string;
/**
* Makes the MIDI device corresponding to the MIDIPort
* explicitly unavailable (subsequently changing the state from "open" to
* "connected"). Note that successful invocation of this method will result in MIDI
* messages no longer being delivered to MIDIMessageEvent handlers on a
* MIDIInputPort (although setting a new handler will cause an implicit open()).
*
* When invoked, this method returns a Promise object representing a request for
* access to the given MIDI port on the user's system. When the port has been
* closed (and therefore, in exclusive access systems, the port is available to
* other applications), the vended Promise is resolved. If the port is
* disconnected, the Promise is rejected.
*/
close(): Promise<MIDIPort>;
}
/**
* The state of the device.
*/
state: MIDIPortDeviceState;
interface MIDIInput extends MIDIPort {
onmidimessage(e: MIDIMessageEvent): void;
}
/**
* The state of the connection to the device.
*/
connection: MIDIPortConnectionState;
interface MIDIOutput extends MIDIPort {
/**
* Enqueues the message to be sent to the corresponding MIDI port.
* @param data The data to be enqueued, with each sequence entry representing a single byte of data.
* @param timestamp The time at which to begin sending the data to the port. If timestamp is set
* to zero (or another time in the past), the data is to be sent as soon as
* possible.
*/
send(data: number[] | Uint8Array, timestamp?: number): void;
/**
* The handler called when an existing port changes its state or connection
* attributes.
*/
onstatechange(e: MIDIConnectionEvent): void;
/**
* Clears any pending send data that has not yet been sent from the MIDIOutput 's
* queue. The implementation will need to ensure the MIDI stream is left in a good
* state, so if the output port is in the middle of a sysex message, a sysex
* termination byte (0xf7) should be sent.
*/
clear(): void;
}
addEventListener(
type: 'statechange',
listener: (this: this, e: MIDIConnectionEvent) => any,
options?: boolean | AddEventListenerOptions,
): void;
addEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions,
): void;
interface MIDIMessageEvent extends Event {
/**
* A timestamp specifying when the event occurred.
*/
receivedTime: number;
/**
* Makes the MIDI device corresponding to the MIDIPort explicitly available. Note
* that this call is NOT required in order to use the MIDIPort - calling send() on
* a MIDIOutput or attaching a MIDIMessageEvent handler on a MIDIInputPort will
* cause an implicit open().
*
* When invoked, this method returns a Promise object representing a request for
* access to the given MIDI port on the user's system.
*/
open(): Promise<MIDIPort>;
/**
* A Uint8Array containing the MIDI data bytes of a single MIDI message.
*/
data: Uint8Array;
}
/**
* Makes the MIDI device corresponding to the MIDIPort
* explicitly unavailable (subsequently changing the state from "open" to
* "connected"). Note that successful invocation of this method will result in MIDI
* messages no longer being delivered to MIDIMessageEvent handlers on a
* MIDIInputPort (although setting a new handler will cause an implicit open()).
*
* When invoked, this method returns a Promise object representing a request for
* access to the given MIDI port on the user's system. When the port has been
* closed (and therefore, in exclusive access systems, the port is available to
* other applications), the vended Promise is resolved. If the port is
* disconnected, the Promise is rejected.
*/
close(): Promise<MIDIPort>;
}
interface MIDIMessageEventInit extends EventInit {
/**
* A timestamp specifying when the event occurred.
*/
receivedTime: number;
interface MIDIInput extends MIDIPort {
type: 'input';
onmidimessage(e: MIDIMessageEvent): void;
/**
* A Uint8Array containing the MIDI data bytes of a single MIDI message.
*/
data: Uint8Array;
}
addEventListener(
type: 'midimessage',
listener: (this: this, e: MIDIMessageEvent) => any,
options?: boolean | AddEventListenerOptions,
): void;
addEventListener(
type: 'statechange',
listener: (this: this, e: MIDIConnectionEvent) => any,
options?: boolean | AddEventListenerOptions,
): void;
addEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions,
): void;
}
interface MIDIConnectionEvent extends Event {
/**
* The port that has been connected or disconnected.
*/
port: MIDIPort;
}
interface MIDIOutput extends MIDIPort {
type: 'output';
interface MIDIConnectionEventInit extends EventInit {
/**
* The port that has been connected or disconnected.
*/
port: MIDIPort;
}
/**
* Enqueues the message to be sent to the corresponding MIDI port.
* @param data The data to be enqueued, with each sequence entry representing a single byte of data.
* @param timestamp The time at which to begin sending the data to the port. If timestamp is set
* to zero (or another time in the past), the data is to be sent as soon as
* possible.
*/
send(data: number[] | Uint8Array, timestamp?: number): void;
/**
* Clears any pending send data that has not yet been sent from the MIDIOutput 's
* queue. The implementation will need to ensure the MIDI stream is left in a good
* state, so if the output port is in the middle of a sysex message, a sysex
* termination byte (0xf7) should be sent.
*/
clear(): void;
}
interface MIDIMessageEvent extends Event {
/**
* A timestamp specifying when the event occurred.
*/
receivedTime: number;
/**
* A Uint8Array containing the MIDI data bytes of a single MIDI message.
*/
data: Uint8Array;
}
interface MIDIMessageEventInit extends EventInit {
/**
* A timestamp specifying when the event occurred.
*/
receivedTime: number;
/**
* A Uint8Array containing the MIDI data bytes of a single MIDI message.
*/
data: Uint8Array;
}
interface MIDIConnectionEvent extends Event {
/**
* The port that has been connected or disconnected.
*/
port: MIDIPort;
}
interface MIDIConnectionEventInit extends EventInit {
/**
* The port that has been connected or disconnected.
*/
port: MIDIPort;
}
}

View File

@ -1,36 +1,43 @@
const onFulfilled = function(item: WebMidi.MIDIAccess) {
this._midiPort = item;
const onFulfilled = (item: WebMidi.MIDIAccess) => {
const midiPort = item;
item.onstatechange = (event: WebMidi.MIDIConnectionEvent) => {
console.log("onstatechange");
midiPort.onstatechange = event => {
console.log('onstatechange');
console.log(event);
};
midiPort.addEventListener('statechange', event => {
console.log(event.port);
});
console.log("sysexenabled");
console.log('sysexenabled');
console.log(item.sysexEnabled);
const inputs = this._midiPort.inputs.values();
for (const o of inputs) {
this._inputs.push(o);
console.log(o);
}
const outputs = item.outputs.values();
for (const op of outputs) {
this._outputs.push(op);
op.send([ 0x90, 0x45, 0x7f ]);
op.send(new Uint8Array([ 0x90, 0x45, 0x7f ]));
op.send([0x90, 0x45, 0x7f]);
op.send(new Uint8Array([0x90, 0x45, 0x7f]));
}
for (const input of this._inputs) {
input.onmidimessage = (event: WebMidi.MIDIMessageEvent) => {
this.onMidiMessage(event.data);
const inputs = midiPort.inputs.values();
for (const input of inputs) {
input.onmidimessage = event => {
console.log(event.data);
};
input.addEventListener('midimessage', event => {
console.log(event.data);
});
}
const inputOrOutput = [...inputs, ...outputs][0];
if (inputOrOutput.type === 'output') {
// 'send' only available on outputs
inputOrOutput.send([12345]);
}
};
const onRejected = (e: Error) => { console.error(e); };
const onRejected = (e: Error) => {
console.error(e);
};
if (navigator.requestMIDIAccess !== undefined) {
navigator.requestMIDIAccess().then(onFulfilled, onRejected);