Open3

Desktop-MIDI: ゲームパッドをMIDIに載せたいの会

okuokuokuoku

ゲームのテスト環境は入力レイヤにテスト用の仮想入力機能を付けて対応している。これを実padに限りなく近づけたい。

なぜMIDI?

MIDIをゲーム用のリアルタイムネットワークとして使うのはMIDI Maze( https://en.wikipedia.org/wiki/MIDI_Maze )の昔から有るが、独自のプロトコルを作りたくない場合に:

  1. トランスポートが豊富。 特に、 RTP-MIDI がパケット落ち等も想定したネットワークプロトコルを提供しているため、ネットワーク透過である。RTP-MIDIはAppleのiOSもサポートしている。
  2. リアルタイム性を想定 している。良いAPIは、イベントに対してタイムスタンプを提供する。
  3. 1本のトランスポートで複数チャンネルを最初から取り扱える

といった特徴が適している。

MIDIの不味い点

なら最初からゲームパッドはHIDじゃなくてMIDIとして表現されるのが標準になってても良いじゃんという気はするが、

  1. 精度が全然足りない 。MIDIは1983年に規格となった8bitインターフェースであり、14ビット以上の精度を持つジャイロセンサ等を直接的に表現できない。
  2. 入出力を1本で行えない 。ハプティックやLED制御のために PC → コントローラ 方向の通信も発生するが、そのためには追加の通信路が必要になる。
  3. 帯域が細い 。PSのコントローラのようにスピーカーを持っていたり、マイクが付属するようなケースではそれらの信号は別の経路で転送する必要がある。

このため、これらのcaveatと天秤に掛けてどの程度実用的なところまで持っていけるのかを考察する必要がある。

Open Sound Control ?

OSCで良いじゃんというのもあるかもしれないが、OSCの開発自体2011年あたりで消滅してしまっており、Bluetooth LEトランスポートのようなその後に登場したものにキャッチアップできていないという問題がある。

OSCは最初から32bitのコントロールとTCP/IPを想定してデザインされていて、かつ、MIDIメッセージも規格としては想定しているため真面目に作り込むならOSCかもしれないが。。

okuokuokuoku

W3C Gamepad API

とりあえずベースラインとしてW3CのGamepad APIを元にキーアサインする。MIDIの制約により、アナログは16,384段階(NRPNで表現できるレベル)となる。これは画面の絶対位置の表現に使用したとしても、現在の4K〜8Kディスプレイを正確に表現できるレベルの精度があるため、多くのケースでは十分と予想される。

Googleはゲーム機各社のコントローラをリバースエンジニアリングして自社のChromeで対応している。これらの対応内容がextensionsとして提案されている現状となっている。

https://github.com/chromium/chromium/blob/master/device/gamepad/nintendo_controller.cc

Valveも同様のリバースエンジニアリングを行いSDLに提供しているが、表現される内容はChromeの方が少々多い。

https://github.com/libsdl-org/SDL/blob/main/src/joystick/hidapi/SDL_hidapi_switch.c

okuokuokuoku

アナログコントローラの扱いが割と難しい

近代的なコントローラは、加速度や角速度のような形でアナログ入力を持つ。これらをどうするかが非常に悩ましい。。

スティック入力

アナログスティックやトリガ(PSで言うところのL2、R2)はアナログ値を出力するが、これは 個体によって1.0を越える出力を出す可能性がある 。このため、MIDIの16,384段階に(0 〜 1.0)をそのままあてはめるとクリッピングを起こしてしまう。

これらに関しては、今回は容赦なくクリッピングしてしまうことにした。

The state is a value ranging from -32768 to 32767. Triggers, however, range from 0 to 32767 (they never return a negative value).

のように、APIレベルで上限を決めていることが殆どなため、実用上は問題無いと判断。

IMUデータ(加速度およびジャイロ)

一般の携帯電話およびSwitchやPSのコントローラは、コントローラ(電話の場合本体)の"姿勢"および"動作"を取得できるように配慮されている。

生のセンサ出力として:

  • 加速度 : コントローラを静止した状態では鉛直下向き(= 地球の重力)であり、コントローラを動かすとその方向が取れる。
  • 角速度 (ジャイロ): コントローラを静止した状態ではゼロであり、コントローラを動かすとその動作の角速度が取れる。

そしてこれらを積算することでコントローラの 姿勢 を得ることができる。

まず、 加速度と角速度は物理量であり センサの能力に依存しているためこれを簡単に16,384段階にマップする良い方法が無い。これはもうある種の浮動小数点を実装するしか方法が無いだろう。。つまり、観測値と共に最大値の情報も同時に送信する必要がある。

姿勢はシステムがソフトウェア的に導出している(センサフュージョン)。これを送るべきかは難しい問題だが、送る前提で考えた方が良いだろう。。