PicoのSDKにはTinyUSBライブラリが含まれており、これを使って比較的簡単にUSB デバイスを作ったり、USB デバイスを接続することができます。
今回はこれを使って、USB経由で受信したMIDIメッセージをUARTに出力するMIDIデバイスを作ってみました。TinyUSBにはサンプルコードも色々入っており、MIDIデバイスも含まれています。サンプルコードはMIDIメッセージをUSBへ出力するシーケンサーのようなデバイスでしたので、それとは違うものにしました。
ちなみに、現在TinyUSBがサポートしているUSBデバイスは、ドキュメントによれば以下の通りです。
●デバイス
・USBオーディオクラス2.0 (UAC2) (開発継続中)
・Bluetoothホストコントローラインタフェース(BTH HCI)
・Communication Device Class(CDC)
・Device Firmware Update(DFU): ランタイムのみ
・Human Interface Device (HID): 汎用(入力と出力)、キーボード、マウス、ゲームパッド等
・マスストレージクラス(MSC): 複数のLUNをサポート
・MIDIデバイス
・RNDIS, CDC-ECMによるネットワーク(開発継続中)
・USB Test and Measurement Class (USBTMC)
・汎用の入出力エンドポイントを持つベンダ固有のクラス。INFファイル無しでwinUSBドライバをロードするためにMS OS 2.0互換デスクリプタと組み合わせることが可能。
・ベンダ固有のクラスを用いるWebUSB
●ホスト
・Human Interface Device (HID): キーボード、マウス、汎用
・マスストレージクラス(MSC)
・ハブは1段のみサポート
今回作成したのはMIDIデバイスです。ソフトウェアをPicoに書き込み、PC に接続すると、PicoがMIDIデバイスとして認識されます。PC からMIDIメッセージを送ると、その内容がUARTにテキストで出力されます。
コードは以下のリポジトリにアップロードしてあります。
最初にMIDIについて簡単に説明しておきます。
MIDIは楽器を制御するための通信プロトコルで、通常1~3バイトのメッセージを送受信します。メッセージの先頭バイトが機能を表し、2バイト目以降はパラメータです。
先頭バイトはMSBが常に1であり、2バイト目以降はMSBが常に0です。
例外としてシステムエクスクルーシブ(SysEx)メッセージでは、先頭が0xF0、末尾が0xF7、途中はMSBが0の不定長のバイト列となっています。
MIDI規格の詳細は以下のドキュメントが公開されています。
MIDI over USBでは、MIDIメッセージは全て4バイトのデータにエンコードされます。先頭バイトがデータ種別を表し、2バイト目以降にMIDIメッセージが入っています。不定長である SysExメッセージも3バイト以下に分割されて上記のフォーマットでエンコードされます。もっとも、このデータはTinyUSBがデコードしてくれますので、ユーザプログラムは意識する必要はありません。
TinyUSBの使い方はシンプルです。
tusb_config.hとusb_descriptors.cの二つのファイルが必要ですが、これはTinyUSBの付属のサンプルからコピーしてきて若干の編集をすれば使えます。
tusb_config.hは、その名の通りTinyUSBのコンフィグファイルです。ポイントは
#define CFG_TUD_MIDI 1
です。その他、バッファサイズの設定(CFG_TUD_MIDI_RX_BUFSIZE、CFG_TUD_MIDI_TX_BUFSIZE)も必要になります。
usb_descriptors.cはデバイス種別によって異なりますので、TinyUSB付属のUSB-MIDIのサンプルのものをコピーしてきます。編集の必要はほとんどありませんが、PCに接続したとき、デバイスの名称が表示されますが、その名称はこのファイルの中で定義されています。
あとは
tusb_init();
で初期化したあとメインループの中で
tud_task();
をひたすら呼び出し、
uint32_t tud_midi_n_read(uint8_t itf, uint8_t jack_id, void* buffer, uint32_t bufsize)
でMIDIメッセージを読みだして処理します。ここでitfはインタフェース、jack_idは仮想的なMIDIジャックですが、今回はどちらも1つしかないので0です。
jack_idなどを含むUSB-MIDIにおけるMIDI機器のモデル化については、詳細はUSB MIDIデバイスクラスの仕様書(下記)の3章で解説されています(私もちゃんと読んでいませんが。)
また、あわせてUSBデバイス自体のモデルについても見ておくとよいかと思います。
実際にやることはMIDIメッセージを受け取って処理する(今回はメッセージ種別に応じた文字列をprintfでUARTに出力する)ことだけですので、プログラムそのものはシンプルです。
出力は以下のような感じになります。左から
生データ | Ch:MIDIチャンネル | MIDIイベント : パラメータ
の順に並んでいます。
904565 | Ch: 00 | Note On : key=69 velocity=101 804540 | Ch: 00 | Note Off : key=69 velocity=64 904023 | Ch: 00 | Note On : key=64 velocity=35 804040 | Ch: 00 | Note Off : key=64 velocity=64 903e33 | Ch: 00 | Note On : key=62 velocity=51 803e40 | Ch: 00 | Note Off : key=62 velocity=64 b01402 | Ch: 00 | Control : num=20 value=2 b01407 | Ch: 00 | Control : num=20 value=7 b0140b | Ch: 00 | Control : num=20 value=11
今回はUARTに出力しているだけですが、前回のシンセサイザ等と組み合わせれば、USB-MIDIで動作するシンセサイザをPicoで作ることができるでしょう。
コメント