Web MIDI API (日本語訳)

W3C Working Draft 16 November 2024

この文書は、W3Cの文書 "「Web MIDI API」 W3C Working Draft 16 November 2024" の日本語訳です。

旧版はこちら :
Web MIDI API Working Draft (日本語訳) 2015年3月17日版

Web MIDI APIの正式な文書は英語版のみであり、日本語訳には翻訳に起因する誤りが含まれている場合があります。
正式な文書はhttp://www.w3.org/TR/webmidi/にあります。

翻訳元の文書はまだワーキングドラフト(Working Draft)です。現状ではまだ説明が不足している部分があったり、今後も頻繁に更新される可能性がある事に注意してください。

日本語訳GitHub : https://github.com/g200kg/web-midi-api-ja
日本語訳公開URL : http://g200kg.github.io/web-midi-api-ja/

Tatsuya Shinyagaito @ g200kg
誤りその他があればGitHubページ、下のサイト経由などで連絡をお願いいたします。
http://www.g200kg.com/

2024年11月27日


Web MIDI API

W3C Working Draft

More details about this document
このバージョン:
https://www.w3.org/TR/2024/WD-webmidi-20241116/
最新の公開バージョン:
https://www.w3.org/TR/webmidi/
最新のエディターズドラフト:
https://webaudio.github.io/web-midi-api/
変更履歴:
https://www.w3.org/standards/history/webmidi/
Commit history
テストスイート:
https://github.com/web-platform-tests/wpt/tree/master/webmidi
編集者:
(Google)
(Google)
以前の編集者:
Jussi Kalliokoski
フィードバック:
GitHub WebAudio/web-midi-api (pull requests, new issue, open issues)
実装レポート:
相互運用性または実装に関する暫定レポートは存在しません。

概要

シンセサイザー、キーボード等のコントローラー、ドラムマシンなど、 ユーザーエージェントはそのホストコンピューターやデバイスに接続された音楽機器を持っている場合があります。 広く採用されている、Musical Instrument Digital Interface (MIDI) プロトコルは電子楽器、 コントローラー、コンピューター間で相互に通信と同期を可能にするものです。 MIDI はオーディオ信号は送信しません: その代わりに音符に関するイベントメッセージ、音量やビブラートやパンニングなどのパラメーターのコントローラー信号、 キューやテンポを合わせるためのクロック、あるいはそのシステム固有の (例えばリモートに保存されているシンセサイザー固有のパッチデータなど) 通信を行います。 このプロトコルは照明装置や特殊効果の制御を行うショーコントロールのような、音楽以外での分野でも標準となっています。

この仕様は MIDI プロトコルをサポートする API を定義し、Web アプリケーションがクライアントシステム上の MIDI 入出力デバイスを列挙、選択し、MIDI メッセージ を送信および受信する事を可能にします。 ユーザーのシステム上にある MIDI デバイス への低レベルアクセスが可能になる事によって、音楽に関するものだけでなく、 非音楽関係の MIDI アプリケーションも構築できるようになります。 一方で Web MIDI API は、音楽やコントローラーの入力の意味を表現するものではありません; これは MIDI 入力と出力インターフェースのメカニズムに触れる事ができ、 MIDI メッセージ をその動作の意味を認識する事なく、送信および受信するという実用的な側面のために設計されています (例えば、"20Hz でビブラートを掛ける" や "G#7 の和音を演奏する" と言うような、 コントローラーの値が変わった事や G#7 の和音を演奏した時の一組のノートオンメッセージという以上の意味を持ちません)。

ユーザーの一部では、"MIDI" は、スタンダード MIDI ファイルおよびジェネラル 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 の刊行物およびこの技術レポートの最新版のリストは、 https://www.w3.org/TR/にある、W3C technical reports index から見つけることができます。

この文書は Audio Working Group によって Recommendation track を使用してワーキングドラフトとして公開されました。

ワーキングドラフトとしての公開は W3C およびそのメンバーによる承認を意味するものではありません。 これは草案文書であり、いつでも他の文書によって改訂、置き換えあるいは廃止される可能性があります。 この文書を作業中のもの以外として引用する事は不適切です。

この文書は W3C Patent Policy に基づいて運用されるグループによって作成されました。 W3C はグループの成果物から作成された特許開示リストを維持しており、そのページでは特許開示の手順についても提示しています。特許について実際の知識を持ち、 それが基本的な請求事項を含むと考える者は、W3C Patent Policy 6 節に従って情報を開示しなくてはなりません。

この文書は2023年11月03日 W3C文書管理方法によって管理されます。

1. 序文

このセクションは非基準情報です

Web MIDI API 仕様は Web 開発者が MIDI デバイス、 例えば機器が接続されるハードウェア MIDI ポートや USB-MIDI 仕様をサポートした USB 機器などに対して列挙、操作、アクセスを行う手段を定義しています。 MIDI に対する Web API を持つ事で、Web アプリケーションは既存のソフトウェアやハードウェアのシンセサイザー、ハードウェアの音楽コントローラー、 照明システムやその他の MIDI で制御される機器/機械を使う事ができるようになります。 この API は広い範囲の使用例を念頭に定義されています。

このAPIが取るアプローチは Apple 社の CoreMIDI や Microsoft 社の Windows MIDI API に似ています; つまり、API は開発者がその上に有用な MIDI ソフトウェアを構築できるように、 MIDI の低レベルのソフトウェアプロトコルを表すように設計されていると言う事です。 API は開発者が入出力インターフェースを列挙し、MIDI メッセージを送受信する事を可能にしますが、 (前述の他の API と同様に) 現存するデバイスを確実にサポートするために必要な範囲を超えて、MIDI メッセージ の意味を定義したり解釈する事は行いません。

Web MIDI API はシーケンサーなどの高レベルの概念を直接実装する事は意図していません; 例えば、スタンダード MIDI ファイルは直接サポートされませんが、Web MIDI API の上にスタンダード MIDI ファイルプレーヤーを構築する事は可能です。 また、ジェネラル MIDI が行っているようにパッチやコントローラーのアサインを意味的に定める事も意図しません; それらの解釈は Web MIDI API の範囲外です (繰り返しになりますが、Web MIDI API を通してジェネラル MIDI を簡単に利用する事はできます)。

2. 準拠

この仕様中、非基準情報と書かれているセクションと同様に、全てのオーサリングガイドライン、図、例、注、は非基準情報です。 仕様内のその他の全ては基準情報です。

この文書内の MUSTSHOULD がこのように大文字で書かれている場合は BCP 14 [RFC2119] [RFC8174] で規定されているように解釈されます。

この仕様で定義される準拠の基準は、このインターフェースを実装するユーザーエージェント単体に適用されます。

この仕様で定められている API の実装に ECMAScript を使用する場合、この仕様で使用されている Web IDL 仕様 [WEBIDL] で定義される ECMAScript バインディングの作法で実装しなくてはなりません (MUST)。

3. 用語

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

MIDIMIDI デバイスMIDI 入力ポートMIDI 出力ポートMIDI インターフェースMIDI メッセージシステムリアルタイムシステムエクスクルーシブという用語は [MIDI] で定義されています。

有効な MIDI メッセージは [MIDI] で定義されています。以下は非基準的な指標です。
  • 最初のバイト (ステータスバイト) は最上位ビットをセットし、それに続くバイトはシステムエクスクルーシブメッセージの一部を除いてセットしません。
  • ステータスバイトの上位ニブルは 16 進で 89ABE のどれかならば、メッセージ全体の長さは 3 バイトです。
  • もしステータスバイトの上位ニブルが C または D ならばメッセージ全体の長さは 2 バイトです。
  • もしステータスバイトが F1 または F3 ならばメッセージ全体の長さは 2 バイトです。
  • もしステータスバイトが F2 ならばメッセージ全体の長さは 3 バイトです。
  • もしステータスバイトが F6F8FAFBFCFEFFのどれかならばメッセージ全体の長さは 1 バイト (ステータスバイトのみ) です。
  • もしステータスバイトが F0 ならばこれは長さの制限がないシステムエクスクルーシブメッセージであり、最終バイトは F7 になります。
  • もしステータスバイトが F4F5F7F9FDのどれかならばこのメッセージは有効ではありません。

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

4.1 権限の統合

Web Midi API は、"midi" という名前で識別される強力な機能です。次の権限関連のフラグを定義することで、パーミッションに統合されます。

permission descriptor type
WebIDLdictionary MidiPermissionDescriptor : PermissionDescriptor {
    boolean sysex = false;
  };

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

です。

4.2 権限ポリシーの統合

Web Midi API は、デフォルトの許可リスト"self" である "midi" という名前のポリシー制御機能を定義します。

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

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

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

MIDI アクセスの要求、特にシステムエクスクルーシブへのアクセスは、ユーザーに対してプロンプトを出し、 MIDI デバイスにアクセスする旨の許可を得なくてはなりません(SHOULD)。 シナリオによっては、この許可は暗黙的あるいは明示的に既に得られている事もあり、 この場合にはこのプロンプトは表示されないかも知れません。 もしユーザーが承諾するか、他の手段で許可されている場合、Promise は解決されます。 必須ではありませんが、下層のシステムはユーザーが特定の MIDI インターフェースをこの API を通じて選択できるように、(個別の基準で選択して) 選ぶ事もあります。 また、システムエクスクルーシブはプライバシー情報をより多く含んでいるため、そのサポートを要求されたかどうかに応じて、 システムはプロンプトメッセージを出す(あるいは出さない)を選択するかも知れません。

ユーザーが拒否、あるいは別の理由で呼び出しが許可されていない場合、Promise は DOMException パラメーターと共にリジェクトされます。

requestMIDIAccess() メソッドが呼び出された時、ユーザーエージェントは次のステップを実行しなくてはなりません (MUST) :

  1. 新しい Promise オブジェクト、promise を作成し、resolver をそれに結びついたリゾルバーとします。

  2. promise を返し、以下のステップを非同期に実行します。

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

  4. document が "midi" という名前のポリシー制御機能を使用することを許可されていない場合は、以下の 失敗 というラベルの付いた手順に進みます。

  5. 前もって行われているユーザー設定、セキュリティ上の理由、プラットフォームの制限などによって必要ならば、以下の 失敗 ステップに進みます。

  6. 前もって行われているユーザー設定によって必要ならば、以下の 成功 ステップに進みます。

  7. ユーザーエージェント固有の方法でユーザーにプロンプトを出し、ユーザーのMIDI デバイスの制御を表す MIDIAccess オブジェクトをスクリプトのオリジンに渡す事に対する許可を得ます。 このプロンプトの表示はシステムエクスクルーシブのサポートを要求したかどうかによる場合があり、ユーザーはアクセスを有効にするか無効にするかを選択できます。

    もし拒否された場合、下の 失敗 ステップにジャンプします。 ユーザーが応答しない場合、このアルゴリズムはこのステップに留まったまま進みません。もし許可された場合は次のステップに進みます。

  8. 成功 : 新しい MIDIAccess オブジェクトを作成し、access とします。 (ユーザーへのプロンプトが複数回表示されるかも知れませんのであまり実際的ではありませんが、 requestMIDIAccess() を複数回呼び出す事は可能です。 そしてこの場合、毎回 MIDIAccess のインスタンスは同じではありません)

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

  10. これらのステップを終了します。

  11. 失敗 : 新しい DOMException 例外を作成し、error とします。 この例外の .name は、ユーザーまたはそのセキュリティ設定によって、要求されたオプションでアプリケーションが MIDIAccess インスタンスを作成することが拒否された場合、または document が機能を 使用可能 でないためにエラーが発生した場合は "NotAllowedError"、ユーザーのナビゲーションによりページが閉じられている場合は "AbortError"、下層のシステムでエラーが発生した場合は "InvalidStateError"、それ以外の場合は "NotSupportedError" になります。

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

4.3.1 MIDIOptions ディクショナリ

このディクショナリは requestMIDIAccess() 要求に渡される、オプション設定を保持します。

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

このメンバーはその MIDIAccess オブジェクトによって システムエクスクルーシブ メッセージの送受信を行う事を要求、または許可する事を表します。このメンバーが true に設定されたオプションが requestMIDIAccess() に渡され、しかし、(ポリシーまたはユーザーアクションによって)、システムエクスクルーシブのサポートが否定された場合、アクセス要求は失敗し "NotAllowedError" エラーが発生します。 もし、このサポートを要求しない(そして許可された)場合に、ユーザーがシステムエクスクルーシブメッセージを送信しようとした時には例外が発生し、そのポートでシステムエクスクルーシブメッセージを受信した時には全て無反応のままマスクされます。

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 になっているマップ風のインターフェースです。

この型は現在使用可能なすべての MIDI 入力ポートを表すのに使用されます。これにより、次の書き方を可能にします :

// to tell how many entries there are:
  let numberOfMIDIInputs = inputs.size;
  
  // add each of the ports to a <select> box
  inputs.forEach( function( port, key ) {
    let opt = document.createElement("option");
    opt.text = port.name;
    document.getElementById("inputportselector").add(opt);
  });
  
  // or you could express in ECMAScript 6 as:
  for (let input of inputs.values()) {
    let opt = document.createElement("option");
    opt.text = input.name;
    document.getElementById("inputportselector").add(opt);
  }

5.2 MIDIOutputMap インターフェース

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

MIDIOutputMap は値が MIDIOutput のインスタンスで、キーがその ID になっているマップ風のインターフェースです。

この型は現在使用可能なすべての MIDI 出力ポートを表すのに使用されます。これにより、次の書き方を可能にします :

// to tell how many entries there are:
  let numberOfMIDIOutputs = outputs.size;
  
  // add each of the ports to a <select> box
  outputs.forEach( function( port, key ) {
    let opt = document.createElement("option");
    opt.text = port.name;
    document.getElementById("outputportselector").add(opt);
  });
  
  // or you could express in ECMAScript 6 as:
  for (let output of outputs.values()) {
    let opt = document.createElement("option");
    opt.text = output.name;
    document.getElementById("outputportselector").add(opt);
  }

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 input ports 入力ポートです。
outputs
システムで使用可能な MIDI output ports 出力ポートです。
onstatechange

新しいポートが接続された、あるいは既存のポートの state 属性が変化した時に呼ばれるハンドラーです。

このイベントハンドラーは、MIDIConnectionEvent 型に対応し、 MIDIAccess インターフェースを実装した全てのオブジェクトでサポートされなくてはなりません (MUST)。

このオブジェクトに EventHandler をアタッチしたままにしておくと、ガベージコレクションが行われない事に注意が必要です。MIDIAccess の使用が終了したら、onstatechange リスナーをすべて削除する必要があります。

ユーザーエージェントは、これまで有効でなかった MIDI ポートが有効になった時、あるいは既存のポートの state 属性が変化した時にはいつでも、 次のステップを実行しなくてはなりません(SHOULD) :

  1. 新たに有効になったポート、 あるいはその既存のポートに対応する MIDIPortport とします。
  2. MIDIConnectionEventportport に設定し、MIDIAccess で "statechange" という名前のイベントを発行します。
sysexEnabled
この属性は、この MIDIAccess 上でシステムエクスクルーシブのサポートが有効かどうかを示します。

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

存在するポートの状態または接続の状態が変化した時に呼び出されるハンドラーです。

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

このオブジェクトに EventHandler をアタッチしたままにしておくと、ガベージコレクションが行われない事に注意が必要です。MIDIPort の使用が終了したら、onstatechange リスナーをすべて削除する必要があります。

open

MIDIPort に対応する MIDI デバイスを明示的に使用可能にします。 この呼び出しは MIDIPort を使用するために必須のものではない事に注意してください - MIDIOutput での send() の呼び出し、または MIDIInput に MIDIMessageEvent ハンドラーを設定する事で暗黙的に open() を実行する 事ができます。 下層の実装はこの呼び出しに対して何もする必要がないかも知れません。 しかしながら下層の実装によっては MIDI デバイスへの共有アクセスをサポートしていないかも知れないため、 明示的な open() および close() 呼び出しを使う事で MIDI アプリケーションがデバイスへの排他アクセスを予想して制御する事が可能になります。

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

もしそのポートが "connected" 状態であってポートへのアクセスが取得できれば (そしてそのポートで入出力が可能になれば)、Promise はリゾルブされます。

connected 状態のポートへのアクセスができなかった場合 (例えば、排他的アクセスシステムのプラットフォームでそのポートが既に使用中になっていた場合)、Promise は(設定されていれば) リジェクトが起動されます。

もし "disconnected" 状態のポートに対して open() が呼び出された場合、 そのポートの .connectionは ポートが "connected" 状態に移行するかすべての参照が無くなるまで、 "pending" に遷移します。

このメソッドが呼び出された時、ユーザーエージェントは MIDIPort オープンのアルゴリズムを実行します (MUST):

  1. 新しい Promise オブジェクト、promise を作成し、結び付けられるリゾルバーを resolver とします。

  2. promise を返却し、以下のステップを非同期的に実行します。

  3. port を与えられた MIDIPort オブジェクトとします。

  4. もしデバイスの状態が既に "open" の場合、(例えば この MIDIPort の open() が既に呼ばれている、または暗黙的にオープンされている場合)、 下の 成功 ラベルのステップにジャンプします。

  5. もしデバイスの状態が "pending" の場合 (例えば既にオープンされ、その後デバイスが切断されている場合)、下の 成功 ラベルのステップにジャンプします。

  6. もしデバイスの状態が "disconnected" の場合、MIDIPortconnection 属性は "pending" に変化し、 新しい MIDIConnectionEventMIDIAccessstatechange ハンドラーと MIDIPortstatechange ハンドラーに発行され、 下の 成功 ラベルのステップにジャンプします。

  7. システム内の MIDI デバイスへのアクセスの取得を試みます。 もしそのデバイスが使用不能の場合 (例えば、既に他のプロセスで使用されていてオープンできない、あるいは切断された場合) 下の 失敗 ラベルのステップにジャンプします。 もしそのデバイスが使用可能でアクセスを取得できた場合、次のステップを続けます。

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

  9. このポートが出力ポートであり、送信を待機している保留中のデータがある場合は、そのデータの送信を非同期で開始します。

  10. 成功 : resolveraccept(value) メソッドを port を引数として呼び出します。

  11. これらのステップの実行を終了します。

  12. 失敗 : error を新たな DOMException とします。 この例外の .name はもしポートが使用不能の場合、"InvalidAccessError" となります。

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

close

MIDIPort に対応する MIDI デバイスを明示的に使用不能にします (この後状態は "open" から "closed" になります)。 このメソッドの呼び出しが成功すると (新しいハンドラーの設定による暗黙的な open() にかかわらず) MIDI メッセージMIDIInputの MIDIMessageEvent ハンドラーに もう伝達されなく事に注意してください。

下層の実装はこの呼び出しに対して何もする必要がないかも知れません。 しかしながら下層の実装によっては MIDI デバイスへの共有アクセスをサポートしていないかも知れないため、明示的な close() 呼び出しは MIDI アプリケーションに他のアプリケーションにデバイスへのアクセス を取得する事が可能な事を保証します。

呼び出された時、このメソッドはユーザーのシステム上で指定の MIDI ポートへのアクセス 要求をあらわす Promise オブジェクトを返します。 ポートがクローズされた時、(つまり排他的なアクセスのシステムではそのポートが他のアプリケーションから使用可能になった時)、Promise はリゾルブされます。 もしポートが切断された場合、Promise はリジェクトされます。

close() メソッドが呼ばれた時、ユーザーエージェントは以下の手順を実行します (MUST) :

  1. promise を新たな Promise オブジェクトとし、resolver をそれに対応するリゾルバーとします。

  2. promise を返却し、以下のステップを非同期に実行します。

  3. port を与えられた MIDIPort オブジェクトとします。

  4. もし、ポートが既に閉じられている (その .connection"closed" - 例えばそのポートがまだ暗黙的にも明示的にもオープンされていない、あるいはこの MIDIPortclose() が既に呼ばれている)場合、下の closed ラベルに飛びます。

  5. もしそのポートが入力ポートの場合、次のステップに進みます。 もし出力ポートで .state"connected" で ない場合、すべての保留されている送信データをクリアし、次のステップに進みます。 タイムスタンプが未来のシステム内に保留されている送信データはすべてクリアし、タイムスタンプが過去または現在のすべての送信メッセージの送信を完了してから次のステップに進みます。

  6. 下層のシステムをオープンしているならポートへのアクセスをすべてクローズし、 下層システムのブロックしているリソースを解放します。

  7. MIDIPort の connection 属性を "closed" に変更し、新しい MIDIConnectionEventMIDIAccessstatechange ハンドラーおよび、MIDIPortstatechange ハンドラーに発行します。

  8. closed : resolveraccept(value) メソッドを port を引数として呼び出します。

  9. これらのステップの実行を終了します。

MIDIPort に対応する MIDI ポートの state 属性が変化した時はいつでも、ユーザーエージェントは以下のステップを実行します (SHOULD) :

  1. portMIDIPort とします。

  2. MIDIConnectionEventport 属性を port に設定し、MIDIPortstatechange という名前のイベントと、MIDIAccessstatechange という名前のイベントを発行します。

5.4.1 MIDIInput インターフェース

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

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

もしハンドラーが設定され、state 属性が "opened" でない場合、下層の実装はポートを使用可能にして、state 属性を "opened" に変更しようとします。 もし成功すれば MIDIConnectionEvent イベントが対応する MIDIPortMIDIAccess に対して発行されます。

MIDIInput に対応する MIDI ポートが 1 つ以上の MIDI メッセージを受信完了した際にはいつでも、ユーザーエージェントは以下のステップを実行しなくてはなりません (MUST) :

  1. portMIDIInput とします。

  2. もし MIDIAccessシステムエクスクルーシブが有効でなく、そのメッセージがシステムエクスクルーシブの場合はこの手順を中断します。

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

特に注意を要するのは、入力ストリームの他のメッセージの途中で MIDI システムリアルタイムメッセージが発生した時です。 この場合、そのシステムリアルタイムメッセージは発生する度にディスパッチされ、通常のメッセージはそれが完結するまでバッファされます (その後ディスパッチされます)。

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 ポートのキューに送信するメッセージを登録します。 下層の実装は (もし必要なら) シーケンスの各メンバーを強制的に unsigned 8 ビット整数にします。 Uint8Array でなくシーケンスを使う事で、開発者は output.send( new Uint8Array( [ 0x90, 0x45, 0x7f ] )); のように Uint8Array を作る事なく、output.send( [ 0x90, 0x45, 0x7f ] ); のような便利な書き方ができます。

data は 1 つ以上の完結した有効な MIDI メッセージを含む事ができます。 下層のシステムがサポートしていない可能性があるのと同様、data 中にランニングステータスは使用できません。

もし、data が有効なシーケンスでない、あるいは有効な MIDI メッセージを含んでいない場合、TypeError 例外が発生します。

もし dataシステムエクスクルーシブメッセージで、MIDIAccessシステムエクスクルーシブへのアクセスが有効でない場合、InvalidAccessError 例外が発生します。

もしポートが "disconnected" 状態の場合、InvalidStateError 例外が発生します。

もしポートが "connected" 状態 であるが接続が "closed" の場合、非同期でポートのオープンを試みます。

sequence<octet> data
キューに登録されるデータで、シーケンスの各エントリーは 1 バイトのデータを表します。
optional DOMHighResTimeStamp timestamp
ポートにデータの送信を開始する時刻です (DOMHighResTimeStamp - ドキュメントへのナビゲーション開始からの相対時間でミリ秒で表されます)。 もし timestamp が存在しない場合、 または 0 (あるいは既に過ぎた時刻) を指定された場合、データは可能な限り即時に送信されます。
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 が表すデバイスがシステムから切断されました。 デバイスがシステムから切断された場合、それは関連する入力や出力のマップには現れません。
connected
MIDIPort が表すデバイスが接続され、入力および出力ポートのマップに現れます。

5.4.5 MIDIPortConnectionState 列挙子

WebIDLenum MIDIPortConnectionState {
    "open",
    "closed",
    "pending",
  };
open
MIDIPort が表すデバイスがオープンされ (暗黙的または明示的にかかわらず)、使用可能になりました。
closed
MIDIPort 表すデバイスがオープンされていません、あるいは明示的にクローズされました。 MIDIPort が ( MIDIPort.open() によって) 明示的にオープンされるか (入力ポートに対して midimessage イベントハンドラーが追加されるか、出力ポートに対して MIDIOutput.send() を呼び出すことで) 暗黙的にオープンされるまで、これはデバイスのデフォルトの状態です。
pending
MIDIPort が表すデバイスが (暗黙的にまたは明示的に) オープンされましたが、 デバイスはその後切断され使用できない状態です。 もしそのデバイスが statechange イベントより前に再接続されれば、 システムはデバイスの再オープンを (MIDIPort をオープンするアルゴリズムに従って) 試みなければなりません。 これによって接続状態は "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

1 つの MIDI メッセージのデータのバイト列を含む Uint8Array です。

5.5.1 MIDIMessageEventInit ディクショナリ

WebIDLdictionary MIDIMessageEventInit: EventInit {
    Uint8Array data;
  };
data

1 つの MIDI メッセージのデータのバイト列を含む Uint8Array です。

5.6 MIDIConnectionEvent インターフェース

新しいポートが使用可能になった時、(例えば MIDI デバイスが最初にコンピューターに接続された時)、 以前使用可能だったポートが使用不能になった時、または再度使用可能になった時、 (例えば MIDI インターフェースが切断され、再度接続された時)、 このインターフェースを実装したイベントオブジェクトが MIDIAccessonstatechange ハンドラーに渡されます。 また (もしあれば) そのポートを参照する全ての MIDIPortonstatechange ハンドラー にも渡されます。

MIDIPort"pending" 状態にあり、そのデバイスがホストシステムに再接続された時は、statechange イベントが 発行される前に MIDIPort オープンのアルゴリズムが実行され、 再オープンを試みます。 もしこの遷移が失敗した場合は (例えばそのポートが下層システムの何かで予約され使用できないなど)、 接続状態は "closed" に遷移し、そうでなければ "open" に遷移します。 これはそのデバイスの状態変化の statechange イベント発行に先立って行わます。そのためイベントではデバイスの状態と同様に最終的な接続状態を反映します。

下層のシステムによってはデバイスの接続状態に関するイベントを提供しない場合があります。そのような システムでは新しいデバイスをまれにしかポーリングせず、大きなディレイが発生するかも知れません。 このような事情で、接続イベントに大きくは依存しないほうが良いでしょう。

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-MIDI デバイスの場合は典型的には、自身の MIDI インターフェイスを持っているために列挙され、USB でホストコンピュータに接続されているほとんどのデバイスがこれに含まれます。 個別のサンプラーやシンセサイザー等の 5 ピン DIN ケーブルで接続されている MIDI デバイスは列挙されません。フィンガープリントの対象となるインターフェースは MIDI "ポート" と同等であり、それぞれの MIDI インターフェースは API によって MIDI インターフェースに対する個別のデバイスの名前、製造者、その内容の詳細が不明な id、が露出します。

ほとんどのシステムでは MIDI インターフェースが接続されていません。大量の MIDI インターフェースを持っているシステムはあまり多くありません。したがって、MIDI デバイスの列挙による付加的なフィンガープリントの露出は、ゲームパッド API のゲームパッド列挙による付加的なフィンガープリントの露出に類似しています : 一般的なユーザーならばせいぜい数台のデバイスを接続し、その構成は変更される可能性があり、露出される情報はインターフェイス自体に関する情報です (つまり、ユーザーが構成したデータはありません)。

7. セキュリティに関する考察

最初の MIDI デバイスは、Web プラットフォームとそのセキュリティ上のリスクが存在する前の 1983 年にリリースされました。 多くの MIDI デバイスは、メーカーがサポートを終了してからも長い間使用され続けています。 MIDI は本来のシリアル接続を超えて、FireWire、USB、Bluetooth などの伝送にも適応しています。 これにより、異なる時代のデバイスが正式にサポートされていないにもかかわらず、設計者が予想していなかった方法でコンピューターや Web に接続され、現在も積極的に使用されているため、セキュリティ上の課題が生じています。

7.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 メッセージは、システムエクスクルーシブメッセージと、ショート (非システムエクスクルーシブな) メッセージに分けられます。システムエクスクルーシブメッセージは更に、一般的に認知されている MIDI タイムコードや MIDI サンプルダンプスタンダードなどのユニバーサルシステムエクスクルーシブメッセージと、他のデバイスには適用されない "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. 7 November 2024. W3C Working Draft. 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. 19 March 2024. W3C Working Draft. URL: https://www.w3.org/TR/permissions/
[permissions-policy]
Permissions Policy. Ian Clelland. W3C. 25 September 2024. W3C Working Draft. URL: https://www.w3.org/TR/permissions-policy-1/
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. March 1997. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. B. Leiba. IETF. May 2017. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc8174
[webaudio]
Web Audio API. Paul Adenot; Hongchan Choi. W3C. 17 June 2021. W3C Recommendation. URL: https://www.w3.org/TR/webaudio/
[WEBIDL]
Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. Living Standard. URL: https://webidl.spec.whatwg.org/