Web MIDI API

W3C 作業草案

この文書についての詳細
このバージョン:
https://www.w3.org/TR/2025/WD-webmidi-20250121/
最新公開バージョン:
https://www.w3.org/TR/webmidi/
最新の編集者草案:
https://webaudio.github.io/web-midi-api/
履歴:
https://www.w3.org/standards/history/webmidi/
コミット履歴
テストスイート:
https://github.com/web-platform-tests/wpt/tree/master/webmidi
編集者:
(Google)
(Google)
以前の編集者:
Jussi Kalliokoski
フィードバック:
GitHub WebAudio/web-midi-api (プルリクエスト新しい課題未解決の課題)
実装報告:
予備的な相互運用性報告または実装報告は存在しません。

概要

一部のユーザーエージェントには、シンセサイザー、キーボードや その他のコントローラー、ドラムマシンなどの音楽デバイスが、ホストコンピューター またはデバイスに接続されている。広く採用されている Musical Instrument Digital Interface (MIDI) プロトコルは、電子楽器、コントローラー、 コンピューターが相互に通信し同期することを可能にする。MIDI は 音声信号を送信しない。代わりに、楽音の ノート、音量、ビブラート、 パンニングなどのパラメーターに対するコントローラー信号、テンポを設定するためのキューや クロック信号、およびシステム固有の MIDI 通信(例:シンセサイザー固有のパッチ データを遠隔で保存するための通信)に関するイベントメッセージを送信する。この同じプロトコルは、 ショー制御、照明、特殊効果制御など、 音楽以外の用途においても標準となっている。

この仕様は、MIDI プロトコルをサポートする API を定義し、 Web アプリケーションがクライアントシステム上の MIDI 入出力デバイスを列挙および選択し、 MIDI メッセージを送受信できるようにする。これは、 ユーザーのシステムで利用可能なMIDI デバイスへの低レベルアクセスを提供することで、 音楽向けの MIDI アプリケーションだけでなく、音楽以外の MIDI アプリケーションも可能にすることを 意図している。Web MIDI API は、音楽または コントローラー入力を意味論的に記述することを意図していない。これは、MIDI 入出力インターフェイスの 仕組み、およびMIDI メッセージの送受信に関する実際的な側面を公開するよう設計されており、 それらの動作が意味論的に何を意味し得るかを特定しない(例:「ビブラートを 20Hz 変調する」または「G#7 コードを演奏する」という観点ではなく、 コントローラー値を変更する、またはたまたま G#7 コードを表す ノートオンメッセージの集合を送信するという観点に限る)。

一部のユーザーにとって、「MIDI」は Standard MIDI Files および General MIDI と同義になっている。この API の意図はそこにはない。 .SMF ファイルを単に再生するというユースケースは、この 仕様の範囲外である(たとえば、HTML の audio 要素によってサポートされる別の形式とみなすことができる)。Web MIDI API は、 MIDI に応答するデバイス、たとえばコントローラー、 外部シンセサイザー、照明システムなどへの直接アクセスを可能にすることを意図している。 Web MIDI API はまた、MIDI コントローラー入力に応答できる Web 上の新しい種類のアプリケーションを可能にするよう明示的に設計されている。 物理的なボタン、ノブ、スライダーを備えた外部ハードウェアコントローラー (およびキーボード、ギター、管楽器コントローラーなどの音楽コントローラー)を使用して Web アプリケーションを制御するものである。

Web MIDI API はまた、Web プラットフォームの他の API および要素、とりわけ Web Audio API と組み合わせて使用されることも期待されている。 この API はまた、Apple の CoreMIDI や Microsoft の Windows MIDI API など、他のシステム上の MIDI API のユーザーにとって馴染みのあるものとなることも意図している。

この文書のステータス

このセクションは、この 文書の公開時点におけるステータスを説明する。現在の W3C 公開文書の一覧およびこの技術報告の最新版は、 W3C 技術 報告インデックス( https://www.w3.org/TR/)で確認できる。

この文書は、Audio Working Group によって、 勧告トラックを使用した 作業草案として公開された。

作業草案としての公開は、 W3C およびその会員による支持を意味しない。

これは草案文書であり、いつでも他の 文書によって更新、置換、または廃止される可能性がある。この文書を 進行中の作業以外のものとして引用することは不適切である。

この文書は、 W3C Patent Policy の下で活動するグループによって作成された。 W3C は、そのグループの 成果物に関連して行われた特許開示の公開一覧を 維持している。そのページには、 特許を開示するための手順も含まれている。ある個人が、 その個人が 必須請求項を含むと考える特許について 実際の知識を有している場合、その個人は W3C Patent Policy のセクション 6に従って 情報を開示しなければならない。

この文書は、 2023年11月3日の W3C Process Document によって管理される。

1. 序論

このセクションは非規範的である。

Web MIDI API 仕様は、Web 開発者が MIDI デバイスを列挙、操作、およびアクセスするための手段を定義する。たとえば、 他のデバイスが接続されたハードウェア MIDI ポートを提供し得る インターフェイスや、USB-MIDI 仕様をサポートする USB デバイスである。MIDI のための Web API を持つことで、 既存のソフトウェアおよびハードウェアシンセサイザー、ハードウェア音楽 コントローラー、照明システム、および MIDI によって制御されるその他の機械装置を 使用する Web アプリケーションが可能になる。この API は、この幅広いユースケースを 念頭に置いて定義されている。

この API が採るアプローチは、Apple の CoreMIDI API および Microsoft の Windows MIDI API で採られているものと類似している。すなわち、この API は、 開発者がその上に強力な MIDI ソフトウェアを構築できるようにするため、 MIDI の低レベルなソフトウェアプロトコルを表現するよう設計されている。この API は、 開発者が入力および出力インターフェイスを列挙し、 MIDI メッセージを送受信できるようにするが、(前述の API と同様に) 現行のデバイスを堅牢にサポートするために必要な範囲を超えて、 MIDI メッセージを意味論的に定義または解釈しようとはしない。

Web MIDI API は、シーケンシングなどの高レベルな 概念を直接実装することを意図していない。たとえば Standard MIDI Files を直接サポートしないが、Standard MIDI File プレーヤーは Web MIDI API の上に構築できる。また、General MIDI が行うように、 パッチやコントローラー割り当てを意味論的に 捉えることも意図していない。そのような解釈は Web MIDI API の範囲外である (ただし、この場合も General MIDI は Web MIDI API を通じて容易に利用できる)。

2. 適合性

非規範的であると示されたセクションに加えて、この仕様におけるすべての作成ガイドライン、図、例、および注記は 非規範的である。この仕様におけるそれ以外のすべては規範的である。

この文書におけるキーワード MUST および SHOULD は、 ここに示されているようにすべて大文字で現れる場合、かつその場合に限り、 BCP 14 [RFC2119] [RFC8174] に記述されているとおりに解釈される。

この仕様は、単一の 製品、すなわち、この仕様に含まれるインターフェイスを 実装するユーザーエージェントに適用される適合性基準を定義する。

ECMAScript を使用してこの仕様で定義される API を実装する 実装は、この仕様がその仕様および用語を使用しているため、 Web IDL 仕様 [WEBIDL] で定義される ECMAScript Bindings と整合する方法で それらを実装しなければならない(MUST)。

3. 用語

Web Audio API およびそれに 関連する インターフェイスと概念は [webaudio] で定義される。

用語 MIDIMIDI デバイスMIDI 入力 ポートMIDI 出力ポートMIDI インターフェイスMIDI メッセージSystem Real Time および System Exclusive は [MIDI] で定義される。

注記
妥当な MIDI メッセージは [MIDI] で定義される。 次は 非規範的なガイドとして使用できる:
  • 最初のバイト(ステータスバイト)は上位ビットが 設定されているべきであり、後続のバイトは System Exclusive メッセージの一部でない限り、設定されているべきではない
  • ステータスバイトの上位ニブルを 16 進数で表したものが 89AB、または E の場合、メッセージ全体の長さは 3 バイトであるべきである
  • ステータスバイトの上位ニブルが C または D の場合、 メッセージ全体の長さは 2 バイトであるべきである
  • ステータスバイトが F1 または F3 の場合、メッセージ全体の 長さは 2 バイトであるべきである
  • ステータスバイトが F2 の場合、メッセージ全体の長さは 3 バイトであるべきである
  • ステータスバイトが F6F8FAFBFCFE、 または FF の場合、メッセージ全体の長さは 1 バイト (ステータスバイトのみ)であるべきである
  • ステータスバイトが F0 の場合、これは長さ制限のない System Exclusive メッセージであり、最後の バイトは F7 であるべきである
  • ステータスバイトが F4F5F7F9、または FD の場合、 メッセージは妥当ではない

4. MIDI デバイスへのアクセスの取得

4.1 Permissions との統合

Web Midi API は、強力な機能であり、 名前 "midi" によって識別される。これは、 次の権限関連フラグを定義することで、 Permissions と統合される:

権限記述子 型
WebIDLdictionary MidiPermissionDescriptor : PermissionDescriptor {
  boolean sysex = false;
};

{name: "midi", sysex: true}{name: "midi", sysex: false} よりも強い

4.2 Permissions Policy との統合

Web Midi API は、 "midi" という名前のポリシー制御 機能を定義し、それは 'self'デフォルト 許可リストを持つ。

4.3 Navigator インターフェイスへの拡張

WebIDLpartial interface Navigator {
  [SecureContext]
  Promise <MIDIAccess> requestMIDIAccess(optional MIDIOptions options = {});
};
requestMIDIAccess() メソッド

呼び出されると、ユーザーのシステム上の MIDI デバイスへのアクセス要求を表す Promise オブジェクトを返す。

MIDI アクセスを要求することは、特に System Exclusive アクセスが 要求されている場合、MIDI デバイスへのアクセスについてユーザーに確認を求めるべきである (SHOULD)。一部のシナリオでは、この権限がすでに 暗黙的または明示的に付与されている場合があり、その場合、このプロンプトは 表示されないことがある。ユーザーが明示的な許可を与えるか、呼び出しが 他の方法で承認された場合、提供された Promise は解決される。 基盤システムは、この API に公開する特定の MIDI インターフェイスをユーザーが選択できるようにすることを選択してもよい (すなわち、個別にインターフェイスを選択する)が、これは 必須ではない。システムはまた、 System Exclusive サポートが要求されているかどうかに基づいて プロンプトを表示する(またはしない)ことを選択してもよい。これは、 System Exclusive アクセスが、より大きなプライバシーおよび セキュリティ上の影響を持つためである。

ユーザーが拒否した場合、またはその他の理由で呼び出しが拒否された場合、 Promise は DOMException パラメーターで拒否される。

すべての提供された Promise がまだ settle していなくても、 requestMIDIAccess() を複数回呼び出すことは 許可される。

requestMIDIAccess() メソッドが呼び出されたとき、ユーザーエージェントは 次の手順を実行しなければならない(MUST):

  1. promise を新しい Promise オブジェクトとし、 resolver をそれに関連付けられた resolver とする。

  2. promise を返し、次の手順を 非同期に実行する。

  3. document を呼び出し元コンテキストの Document とする。

  4. document使用を許可されていない場合、 ポリシー制御 機能である midi を、下の failure とラベル付けされた手順へジャンプする。

  5. 任意で、たとえば以前に確立されたユーザー 設定、セキュリティ上の理由、またはプラットフォームの 制限に基づいて、下の failure とラベル付けされた手順へジャンプする。

  6. 任意で、たとえば以前に確立されたユーザー 設定に基づいて、下の success とラベル付けされた手順へジャンプする。

  7. エントリースクリプトの origin に、ユーザーの MIDI デバイスに対する制御を表す MIDIAccess オブジェクトを提供する許可について、ユーザーエージェント固有の方法で ユーザーに確認を求める。この プロンプトは、 System Exclusive サポートが要求されたかどうかに依存してもよく、そのアクセスを有効または 無効にすることをユーザーに許可してもよい。

    許可が拒否された場合、下の failure とラベル付けされた手順へジャンプする。ユーザーが決して応答しない場合、この アルゴリズムはこの手順を越えて進行しない。許可が 付与された場合、次の手順を続行する。

  8. successaccess を新しい MIDIAccess オブジェクトとする。( requestMIDIAccess() を複数回呼び出すことは可能である。これはユーザーに 複数回プロンプトを表示する可能性があるため、ベストプラクティスではない場合があり、 MIDIAccess の同じインスタンスが毎回返されるわけではない。)

  9. resolveraccept(value) メソッドを、 access を値引数として呼び出す。

  10. これらの手順を終了する。

  11. failureerror を新しい DOMException とする。 この例外の .name は、ユーザーまたはそのセキュリティ設定が、 要求された options で MIDIAccess インスタンスを作成することをアプリケーションに対して 拒否した場合、またはエラーが document がその機能の使用を許可されていないことの結果である場合、 "NotAllowedError" であるべきであり、ページがユーザー ナビゲーションのために閉じられようとしている場合は "AbortError"、 基盤システムが何らかのエラーを 発生させた場合は "InvalidStateError" であり、それ以外の場合は "NotSupportedError" であるべきである。

  12. resolverreject(value) メソッドを、 error を値引数として呼び出す。

4.3.1 MIDIOptions 辞書

この辞書には、 requestMIDIAccess() 要求に提供され得る任意の設定が含まれる。

WebIDLdictionary MIDIOptions {
  boolean sysex;
  boolean software;
};
sysex

このメンバーは、 System Exclusive メッセージを送受信する能力が、 所与の MIDIAccess オブジェクト上で要求されているか、または 許可されているかをシステムに通知する。 requestMIDIAccess() に渡される option において、 このメンバーが true に設定されているが、 System Exclusive サポートが(ポリシーまたは ユーザー操作によって)拒否された場合、アクセス要求は "NotAllowedError" エラーで失敗する。このサポートが要求(かつ 許可)されていない場合、ユーザーが System Exclusive メッセージを送信しようとすると、システムは例外を投げ、 そのポートで受信した System Exclusive メッセージをすべて黙って マスクアウトする。

software

このメンバーは、ホストシステムにインストールされている 任意のソフトウェアシンセサイザーを利用する能力が、 所与の MIDIAccess オブジェクト上で要求されているか、または 許可されているかをシステムに通知する。 requestMIDIAccess() において、 このメンバーが true に設定されているが、 ソフトウェアシンセサイザーのサポートが(ポリシーまたは ユーザー操作によって)拒否された場合、アクセス要求は "NotAllowedError" エラーで失敗する。このサポートが要求されていない場合、 システムは利用可能なポートの MIDIAccess 公開に、ソフトウェアシンセサイザーを含めるべきではない。

ソフトウェアシンセサイザーのサポートが望まれるが必須ではない場合、 これは 2 段階の要求手続きになる可能性があることに注意されたい。すなわち、 MIDI ハードウェアデバイスアクセスが 許可されている場合でも、ソフトウェアシンセサイザーが無効にされることがある。

5. MIDI API

5.1 MIDIInputMap インターフェイス

WebIDL[SecureContext, Exposed=(Window,Worker)] interface MIDIInputMap {
  readonly maplike <DOMString, MIDIInput>;
};

MIDIInputMap は、値が MIDIInput インスタンスであり、キーがその ID である maplike インターフェイスである。

この型は、現在利用可能なすべてのMIDI 入力 ポートを表すために使用される。

5.2 MIDIOutputMap インターフェイス

WebIDL[SecureContext, Exposed=(Window,Worker)] interface MIDIOutputMap {
  readonly maplike <DOMString, MIDIOutput>;
};

MIDIOutputMap は、値が MIDIOutput インスタンスであり、キーがその ID である maplike インターフェイスである。

この型は、現在利用可能なすべてのMIDI 出力 ポートを表すために使用される。

5.3 MIDIAccess インターフェイス

このインターフェイスは、MIDI 入力および出力 デバイスを一覧し、個々のデバイスへのアクセスを取得するためのメソッドを提供する。

WebIDL[SecureContext, Exposed=(Window,Worker), Transferable] interface MIDIAccess: EventTarget {
  readonly attribute MIDIInputMap inputs;
  readonly attribute MIDIOutputMap outputs;
  attribute EventHandler onstatechange;
  readonly attribute boolean sysexEnabled;
};
inputs
システムで利用可能なMIDI 入力ポート
outputs
システムで利用可能なMIDI 出力ポート
onstatechange

新しいポートが接続されたとき、または既存の ポートが state 属性を変更したときに呼び出されるハンドラー。

このイベント ハンドラーは、型 MIDIConnectionEvent であり、 MIDIAccess インターフェイスを実装するすべてのオブジェクトでサポートされなければならない (MUST)。

注記

このオブジェクトにEventHandler を接続したままにすると、そのオブジェクトが ガベージコレクトされなくなることを理解することが重要である。 MIDIAccess の使用を終えたら、 すべての onstatechange リスナーを削除するべきである。

以前は利用できなかった MIDI ポートが利用可能になったとき、 または既存のポートが state 属性を変更したときは常に、ユーザー エージェントは次の手順を実行するべきである(SHOULD):

  1. port を、新たに利用可能になった、 または既存のポートに対応するMIDIPortとする。
  2. イベントを発火するMIDIAccess で "statechange" という名前のイベントを発火し、 MIDIConnectionEvent を用い、portport に設定する。
sysexEnabled
この属性は、この MIDIAccess で System Exclusive サポートが有効であるかどうかをユーザーに知らせる。

5.4 MIDIPort インターフェイス

このインターフェイスは、MIDI 入力ポートまたは出力ポートを表す。

WebIDL[SecureContext, Exposed=(Window,Worker)] interface MIDIPort: EventTarget {
  readonly attribute DOMString id;
  readonly attribute DOMString? manufacturer;
  readonly attribute DOMString? name;
  readonly attribute MIDIPortType type;
  readonly attribute DOMString? version;
  readonly attribute MIDIPortDeviceState state;
  readonly attribute MIDIPortConnectionState connection;
  attribute EventHandler onstatechange;
  Promise <MIDIPort> open();
  Promise <MIDIPort> close();
};
id

ポートの一意な ID。これは、ユーザーがアプリケーションのために選択した ポートを開発者が記憶するために使用できる。 ユーザーエージェントは、id がそのポートだけに対して一意であることを保証しなければならない (MUST)。ユーザーエージェントは、アプリケーションのインスタンスをまたいで id が維持されること、たとえばシステムが再起動された場合や、 デバイスがシステムから取り外された場合にも維持されることを保証するべきである (SHOULD)。 アプリケーションは、MIDI セットアップを再作成するために、 これらの id をローカルにキャッシュしたい場合がある。一部のシステムは、完全に一意な 永続識別子をサポートしないことがある。そのような場合、別のインターフェイスが システムに追加または削除されたときに識別子を維持することは、より 困難になる。(これにより、要求されたポートのインデックスが ずれる可能性がある。)システムは、MIDI API のインスタンスをまたいでポートを照合するために、 可能な限り最善を尽くすことが期待される。たとえば、実装は、 ポートインターフェイスの製造者、名前、およびインデックスの何らかの形式のハッシュを id として 不透明に使用し、そのポート id への参照が、接続されたときのポートに一致する可能性を高めてもよい。 アプリケーションは、MIDIPort の id の比較を使用して等価性を検査してもよい。

manufacturer

ポートの製造者。

name

ポートのシステム名。

type

ポートが入力ポートであるか出力ポートであるかを区別するための 記述子プロパティ。MIDIOutput については、これは "output" でなければならない(MUST)。MIDIInput については、これは "input" でなければならない(MUST)。

version

ポートのバージョン。

state
デバイスの状態。
connection
デバイスへの接続の状態。
onstatechange

既存のポートがその state または connection 属性を変更したときに呼び出されるハンドラー。

このイベント ハンドラーは、型 "statechange" であり、MIDIPort インターフェイスを実装するすべてのオブジェクトで サポートされなければならない(MUST)。

注記

このオブジェクトにEventHandler を接続したままにすると、そのオブジェクトが ガベージコレクトされなくなることを理解することが重要である。 MIDIPort の使用を終えたら、 すべての onstatechange リスナーを削除するべきである。

open

MIDIPort に対応するMIDI デバイスを 明示的に利用可能にする。この呼び出しは、 MIDIPort を使用するために必要ではないことに注意。 MIDIOutputsend() を呼び出すこと、 MIDIInput に MIDIMessageEvent EventHandler を接続すること、または MIDIInput に MIDIMessageEvent EventListener を追加することにより、暗黙的な open() が発生する。基盤となる 実装は、この呼び出しに応答して何も行う必要がない場合がある。 しかし、一部の基盤となる実装は、MIDI デバイスへの共有アクセスを サポートできない場合があるため、明示的な open() および close() 呼び出しを使用することで、MIDI アプリケーションは デバイスへのこの排他的アクセスを予測可能に制御できる。

呼び出されると、このメソッドは、ユーザーのシステム上の所与の MIDI ポートへの アクセス要求を表す Promise オブジェクトを返す。

ポートデバイスの state が "connected" である場合、 ポートへのアクセスが取得されると(そしてポートが入力または 出力の準備ができると)、提供された Promise は解決される。

接続済みポートへのアクセスが利用できない場合(たとえば、 排他的アクセスのみのプラットフォームでそのポートがすでに使用中である場合)、 Promise は拒否される(もしあれば)呼び出される。

open()"disconnected" であるポートで呼び出された場合、 そのポートの .connection"pending" に遷移し、 そのポートが "connected" になるか、それへのすべての参照が 破棄されるまで続く。

提供されたすべての Promise がまだ settle していなくても、 open() を複数回呼び出すことは許可される。

このメソッドが呼び出されたとき、ユーザーエージェントは MIDIPort を開くアルゴリズムを 実行しなければならない(MUST):

  1. promise を新しい Promise オブジェクトとし、 resolver をそれに関連付けられた resolver とする。

  2. promise を返し、次の手順を 非同期に実行する。

  3. port を所与の MIDIPort オブジェクトとする。

  4. デバイスの connection がすでに "open" である場合 (例:open() がすでにこの MIDIPort で呼び出されている、またはポートが 暗黙的に開かれている場合)、下の success とラベル付けされた手順へジャンプする。

  5. デバイスの connection が "pending" である場合 (すなわち、connection は開かれていたが、その後デバイスが 切断された場合)、下の success とラベル付けされた手順へジャンプする。

  6. デバイスの state が "disconnected" である場合、 MIDIPortconnection 属性を "pending" に変更し、新しい MIDIConnectionEventMIDIAccessstatechange ハンドラーと、 MIDIPortstatechange ハンドラーにキューイングし、下の success とラベル付けされた手順へジャンプする。

  7. システム内の所与のMIDI デバイスへのアクセス取得を試みる。 デバイスが利用できない場合(例:別のプロセスによってすでに使用中であり 開けない、または切断されている場合)、 下の failure とラベル付けされた手順へジャンプする。デバイスが 利用可能でアクセスが取得された場合、次の手順を続行する。

  8. MIDIPort の connection 属性を "open" に変更し、新しい MIDIConnectionEventMIDIAccessstatechange ハンドラーと、 MIDIPortstatechange ハンドラーにキューイングする。

  9. このポートが出力ポートであり、将来の timestamp を持つキュー済み送信データが ある場合、そのデータの送信を非同期に開始する。

  10. successresolveraccept(value) メソッドを、port を 値引数として呼び出す。

  11. これらの手順を終了する。

  12. failureerror を新しい DOMException とする。 この例外の .name は、ポートが利用できない場合、 "InvalidAccessError" であるべきである。

  13. resolverreject(value) メソッドを、 error を値引数として呼び出す。

close

MIDIPort に対応するMIDI デバイスを 明示的に利用不可にする(その後、状態を "open" から "closed" に変更する)。この メソッドの呼び出しが成功すると、MIDIInput 上の MIDIMessageEvent ハンドラーに MIDI メッセージがもはや 配信されなくなることに注意(ただし、 新しいハンドラーを設定すると暗黙的な open() が発生する)。

基盤となる実装は、この呼び出しに応答して 何も行う必要がない場合がある。しかし、一部の基盤となる実装は MIDI デバイスへの共有アクセスをサポートできない場合があり、 明示的な close() 呼び出しにより、MIDI アプリケーションは 他のアプリケーションがデバイスへのアクセスを取得できるようにすることを確実にできる。

呼び出されると、このメソッドは、ユーザーのシステム上の所与の MIDI ポートへの アクセス要求を表す Promise オブジェクトを返す。 ポートが閉じられたとき(したがって、排他的アクセス システムでは、そのポートが他のアプリケーションで利用可能になったとき)、 提供された Promise は解決される。ポートが切断されている場合、Promise は 拒否される。

提供されたすべての Promise がまだ settle していなくても、 close() を複数回呼び出すことは許可される。

close() メソッドが呼び出されたとき、ユーザーエージェントは 次の手順を実行しなければならない(MUST):

  1. promise を新しい Promise オブジェクトとし、 resolver をそれに関連付けられた resolver とする。

  2. promise を返し、次の手順を 非同期に実行する。

  3. port を所与の MIDIPort オブジェクトとする。

  4. ポートがすでに閉じている場合(その .connection"closed" である場合。例:ポートが まだ暗黙的にも明示的にも開かれていない、または close() がすでにこの MIDIPort で 呼び出されている場合)、下の closed とラベル付けされた手順へジャンプする。

  5. ポートが入力ポートである場合、次の手順へスキップする。出力ポートの .state"connected" でない場合、またはその .connection が "pending" である場合は、 すべてのキュー済み送信データを消去し、次の手順へスキップする。 将来の timestamp を持つ、システム内のすべてのキュー済み送信データを消去し、 その後、timestamp がない、または過去もしくは現在の timestamp を持つ送信メッセージの送信を完了してから、 次の手順へ進む。

  6. 基盤システムで開かれている場合はポートへのアクセスを閉じ、 基盤システムのブロッキングリソースをすべて解放する。

  7. MIDIPort の connection 属性を "closed" に変更し、新しい MIDIConnectionEventMIDIAccessstatechange ハンドラーと、 MIDIPortstatechange ハンドラーにキューイングする。

  8. closedresolveraccept(value) メソッドを、port を 値引数として呼び出す。

MIDIPort に対応する MIDI ポートが state 属性を変更したときは常に、ユーザーエージェントは次の手順を実行するべきである (SHOULD):

  1. portMIDIPort とする。

  2. イベントを発火するMIDIPortstatechange という名前のイベントを発火し、 MIDIAccessstatechange を発火し、 MIDIConnectionEvent を用い、port 属性を port に設定する。

5.4.1 MIDIInput インターフェイス

WebIDL[SecureContext, Exposed=(Window,Worker)] interface MIDIInput: MIDIPort {
  attribute EventHandler onmidimessage;
};
onmidimessage

このイベント ハンドラーは、型 "midimessage" であり、MIDIInput インターフェイスを実装するすべてのオブジェクトで サポートされなければならない(MUST)。

ハンドラーが設定されており、state 属性が "opened" でない場合、基盤となる実装は ポートを利用可能にしようとし、state 属性を "opened" に変更する。成功した場合、MIDIConnectionEvent が 対応する MIDIPort および MIDIAccess に配信される。

MIDIInput に対応する MIDI ポートが 1 つ以上のMIDI メッセージの受信を終えたときは常に、 ユーザーエージェントは 次の手順を実行しなければならない(MUST):

  1. portMIDIInput とする。

  2. MIDIAccessSystem Exclusive アクセスを有効にしておらず、かつそのメッセージが System Exclusive メッセージである場合、この 処理を中止する。

  3. port で "midimessage" という名前の イベントを発火するMIDIMessageEvent を用い、timeStamp 属性を、そのメッセージがシステムによって受信された時刻に設定し、 data 属性を、単一の MIDI メッセージを表す MIDI データバイトの Uint8Array に設定する。

MIDI System Real Time メッセージは、 入力ストリーム内の他のメッセージの途中で実際に発生することがあると、 特に注記される。この場合、System Real Time メッセージは 発生した時点でディスパッチされ、一方、通常のメッセージは 完了するまでバッファリングされる(その後ディスパッチされる)。

5.4.2 MIDIOutput インターフェイス

WebIDL[SecureContext, Exposed=(Window,Worker)] interface MIDIOutput : MIDIPort {
  undefined send(sequence<octet> data, optional DOMHighResTimeStamp timestamp = 0);
  undefined clear();
};
send

対応する MIDI ポートへ送信されるメッセージをキューに入れる。 基盤となる実装は(必要であれば)シーケンスの各 メンバーを符号なし 8 ビット整数に変換する。Uint8Array ではなく sequence を使用することで、開発者は Uint8Array を作成する必要なく、たとえば output.send( new Uint8Array( [ 0x90, 0x45, 0x7f ] ) ); ではなく、 output.send( [ 0x90, 0x45, 0x7f ] ); という簡便さを利用できる。

data には、1 つ以上の完全な妥当な MIDI メッセージが含まれる。 Running status は、基盤システムがサポートしない可能性があるため、 data では許可されない。

data が妥当な sequence でない、または 妥当な MIDI メッセージを含まない場合、 TypeError 例外を投げる。

dataSystem Exclusive メッセージであり、 かつ MIDIAccessSystem Exclusive アクセスを有効にしていない場合、 InvalidAccessError 例外を投げる。

ポートが "disconnected" である場合、 InvalidStateError 例外を投げる。

ポートが "connected" であるが、 connection が "closed" である場合、 非同期にポートを開くことを試みる。

sequence<octet> data
キューに入れられるデータ。各 sequence エントリーは 1 バイトのデータを表す。
optional DOMHighResTimeStamp timestamp
データをポートへ送信し始める時刻( DOMHighResTimeStamp として、文書の navigation start から相対的に測定された ミリ秒数)。timestamp が 0(または過去の別の時刻)に 設定されている場合、データはできるだけ早く送信される。 同じ timestamp で send() を複数回呼び出した場合、 そのデータは呼び出しが行われた順序で送信されなければならない。
clear

MIDIOutput のキューから、 まだ送信されていないすべてのキュー済み送信データを消去する。実装は、 MIDI ストリームが良好な状態に残されることを保証する必要があるため、 出力ポートが sysex メッセージの途中にある場合、sysex 終了バイト(0xf7)が送信されるべきである。

5.4.3 MIDIPortType 列挙

WebIDLenum MIDIPortType {
  "input",
  "output",
};
input
MIDIPort が入力ポートである場合、type メンバーは この値でなければならない(MUST)。
output
MIDIPort が出力ポートである場合、type メンバーは この値でなければならない(MUST)。

5.4.4 MIDIPortDeviceState 列挙

WebIDLenum MIDIPortDeviceState {
  "disconnected",
  "connected",
};
disconnected
MIDIPort が表すデバイスは システムから切断されている。デバイスがシステムから切断されている場合、それは 入力および出力ポートの関連する map に現れるべきではない。
connected
MIDIPort が表すデバイスは 接続されており、入力および出力ポートの map に現れるべきである。

5.4.5 MIDIPortConnectionState 列挙

WebIDLenum MIDIPortConnectionState {
  "open",
  "closed",
  "pending",
};
open
MIDIPort が表すデバイスは (暗黙的または明示的に) 開かれており、使用可能である。
closed
MIDIPort が表すデバイスは 開かれていない、または 明示的に閉じられている。MIDIPort が 明示的に(MIDIPort.open() を通じて)または暗黙的に (入力ポートに midimessage イベントハンドラーを追加すること、または出力ポートで MIDIOutput.send() を呼び出すことによって)開かれるまでは、これはデバイスの既定状態であるべきである。
pending
MIDIPort が表すデバイスは (暗黙的または 明示的に)開かれていたが、その後デバイスが切断され、 使用できない状態にある。デバイスが再接続された場合、 statechange イベントを送信する前に、システムは( MIDIPort を開くアルゴリズムに従って) デバイスを再び開くことを試みるべきである。これにより、connection state は "open" または "closed" のいずれかに遷移する。

5.5 MIDIMessageEvent インターフェイス

このインターフェイスを実装するイベントオブジェクトは、 MIDI メッセージが 受信されたとき、MIDIInput の onmidimessage ハンドラーに渡される。 DOM EventtimeStamp 属性は DOMHighResTimeStamp として定義され、 イベントが受信された、または送信される高解像度時刻を表すことに注意。

WebIDL[SecureContext, Exposed=(Window,Worker)]
interface MIDIMessageEvent : Event {
  constructor(DOMString type, optional MIDIMessageEventInit eventInitDict = {});
  readonly attribute Uint8Array? data;
};
data

単一のMIDI メッセージの MIDI データバイトを含む Uint8Array。

5.5.1 MIDIMessageEventInit 辞書

WebIDLdictionary MIDIMessageEventInit: EventInit {
  Uint8Array data;
};
data

単一のMIDI メッセージの MIDI データバイトを含む Uint8Array。

5.6 MIDIConnectionEvent インターフェイス

このインターフェイスを実装するイベントオブジェクトは、 新しいポートが利用可能になったとき(たとえば、MIDI デバイスがコンピューターに初めて 接続されたとき)、以前利用可能であったポートが利用不可になったとき、 または再び利用可能になったとき(たとえば、MIDI インターフェイスが 切断され、その後再接続されたとき)、 MIDIAccessonstatechange ハンドラーに渡される。 また(存在する場合)、そのポートを参照する任意の MIDIPortonstatechange ハンドラーにも渡される。

MIDIPort"pending" 状態にあり、デバイスが ホストシステムに再接続された場合、 statechange イベントを発火する前に、 そのポートを再び開くことを試みるため、 MIDIPort を開くアルゴリズムがその上で実行される。この 遷移が失敗した場合(例:ポートが基盤システム内の別の何かによって予約されており、 したがって使用できない場合)、connection state は "closed" に移動し、そうでない場合は "open" に戻る。 これは、デバイス状態変更に対する statechange イベントの前に行われるため、そのイベントは最終的な connection state と device state の両方を反映する。

一部の基盤システムは、デバイス接続状態の通知イベントを提供しない場合がある。 そのようなシステムでは、新しいデバイスをまれにポーリングするため、 長い時間遅延が生じることがある。そのため、 接続イベントに大きく依存しないことが推奨される。

WebIDL[SecureContext, Exposed=(Window,Worker)]
interface MIDIConnectionEvent : Event {
  constructor(DOMString type, optional MIDIConnectionEventInit eventInitDict = {});
  readonly attribute MIDIPort? port;
};
port

接続または切断されたポート。

5.6.1 MIDIConnectionEventInit 辞書

WebIDLdictionary MIDIConnectionEventInit: EventInit {
  MIDIPort port;
};
port

接続または切断されたポート。

6. プライバシーに関する考慮事項

ユーザーのMIDI インターフェイスの列挙を許可することは、 フィンガープリンティングの潜在的な対象である。すなわち、接続されている特定の MIDI インターフェイスによって、 ユーザーを一意に識別することである。

この文脈で列挙できるものは MIDI インターフェイスであることに注意。 これには、USB でホストコンピューターに接続されるほとんどのデバイスが含まれる。 なぜなら、USB-MIDI デバイスは 通常、それ自身のMIDI インターフェイスを持ち、列挙されるためである。 5 ピン DIN ケーブルでMIDI インターフェイスに接続された個々のサンプラーまたはシンセサイザーの MIDI デバイスは列挙されない。 フィンガープリンティングされ得るインターフェイスは MIDI の「ポート」に相当し、 各 MIDI インターフェイスについて、この API はデバイス名、 製造者、およびMIDI インターフェイスの 不透明な識別子を公開する。

ほとんどのシステムにはMIDI インターフェイスが接続されていない。 多数のMIDI インターフェイスが接続されている システムは少ない。したがって、MIDI デバイスを列挙することによる追加の フィンガープリンティング露出は、Gamepad API がゲームパッドの列挙を通じて持つ 追加のフィンガープリンティング露出と類似している。典型的なユーザーでは、 接続されているデバイスは多くても数個であり、その構成は変化し得るし、 公開される情報はインターフェイス自体に関するもの(すなわち、ユーザーが設定したデータではない)である。

7. セキュリティに関する考慮事項

最初のMIDI デバイスは、Web プラットフォームとそのセキュリティリスクが 存在する前の 1983 年にリリースされた。多くのMIDI デバイスは、 製造者がサポートを停止してから長い時間が経過した後も使用されている。 MIDI は、元のシリアル接続を超えた FireWire、USB、Bluetooth などの転送方式に適応してきた。これは、 公式サポートを持たないが現在も活発に使用され、設計者が想定していなかった方法で コンピューターや Web に接続されている、異なる時代のデバイスの長い尾を伴う セキュリティ上の課題をもたらす。

7.1 悪意のあるファームウェア更新

懸念される理論上の攻撃の 1 つに、USB-MIDI デバイスに対する悪意のある ファームウェア更新がある。一般に USB デバイスは、 USB デバイス自身から送信されるデバイス記述子に基づいて動作できる。 USB-MIDI デバイスの ファームウェアが送信される記述子を変更できる場合、 そのデバイスは自身をヒューマンインターフェイスデバイスとして動作させることができる。 これにより、悪意のある Web サイトがホストコンピューター上の キーストロークやその他のイベントを読み取ったり注入したりできるようになり、 システム全体の侵害につながる可能性がある。

攻撃は次のように進行する:

  1. 悪意のあるサイトが、ユーザーをだまして Web MIDI の 権限を付与させる。
  2. 悪意のあるサイトが、ユーザーのマシンに接続されている MIDI デバイスを列挙し、 脆弱なデバイスを特定する。
  3. 悪意のあるサイトが、事前に作成された一連のMIDI メッセージを 脆弱なデバイスに送信し、そのファームウェアを上書きして USB 記述子にヒューマンインターフェイスデバイスを追加することで デバイスを侵害する。
  4. 侵害されたデバイスが、事前に作成されたセキュリティエクスプロイトを ダウンロードまたはその他の方法で再現し、それを実行するためのキーストロークを注入し、 システムを侵害する。

上記の攻撃を可能にするためには、MIDI デバイスについて、 次のすべてが真である必要がある:

悪意のあるファームウェア更新に対して脆弱であるが、他の条件を満たさない MIDI デバイスは、この攻撃でホストシステムを侵害するために 使用することはできない。それでも悪意のあるファームウェア更新は、これらの MIDI デバイスを 動作不能にしたり、望ましくない方法で動作させたりする可能性がある。

このリスクを軽減するため、実装者は実装において 次の点を重視するべきである:

既知の MIDI デバイスのリストを明示的に許可またはブロックすることも、 この特定の攻撃を軽減するのに役立つ可能性がある。しかし、 多くの小企業や個人が MIDI デバイスを作っており、 多くの MIDI デバイスはもはやサポートされていないため、 これを行うと Web MIDI API の使いやすさが大幅に低下する。

7.2 追加のセキュリティに関する考慮事項

利用可能なポートを識別することによるフィンガープリンティングの懸念とは別に、 MIDI メッセージの送受信に関する懸念がある。 それらの問題は以下でより詳しく検討される。

MIDI メッセージは、System Exclusive メッセージと、短い(非System Exclusive)メッセージに 分けることができる。System Exclusive メッセージはさらに、 広く認識されている MIDI Time Code や MIDI Sample Dump Standard のような Universal System Exclusive メッセージと、 他のデバイスには適用されない「Roland Jupiter-80 シンセサイザーのパッチ制御データ」のような デバイス固有メッセージに細分できる。

セキュリティ上の懸念を議論する前に、これらの機能を使用する MIDI によって どのようなシナリオが可能になるかを調べると有用である:

これらそれぞれの潜在的なセキュリティ上の影響は次のとおりである:

8. 変更履歴

8.1 2015年3月17日の作業草案以降の変更

A. 参考文献

A.1 規範的参考文献

[dom]
DOM Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://dom.spec.whatwg.org/
[hr-time]
High Resolution Time. Yoav Weiss. W3C. 2024年 11月7日. W3C 作業草案. URL: https://www.w3.org/TR/hr-time-3/
[html]
HTML Standard. Anne van Kesteren; Domenic Denicola; Dominic Farolino; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[infra]
Infra Standard. Anne van Kesteren; Domenic Denicola. WHATWG. Living Standard. URL: https://infra.spec.whatwg.org/
[MIDI]
MIDI 1.0 Core Specifications. The MIDI Manufacturers Association. 2014. URL: https://midi.org/midi-1-0-core-specifications
[Permissions]
Permissions. Marcos Caceres; Mike Taylor. W3C. 2024年12月20日. W3C 作業草案. URL: https://www.w3.org/TR/permissions/
[permissions-policy]
Permissions Policy. Ian Clelland. W3C. 2025年1月13日. W3C 作業草案. URL: https://www.w3.org/TR/permissions-policy-1/
[RFC2119]
RFC において要求レベルを示すために用いる キーワード. S. Bradner. IETF. 1997年3月. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
RFC 2119 キーワードにおける大文字と小文字の曖昧さ. B. Leiba. IETF. 2017年5月. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc8174
[webaudio]
Web Audio API. Paul Adenot; Hongchan Choi. W3C. 2021年6月17日. W3C 勧告. URL: https://www.w3.org/TR/webaudio-1.0/
[WEBIDL]
Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. Living Standard. URL: https://webidl.spec.whatwg.org/