RaspberryPiシリーズでUSB-MIDI INは実用にならない

 RaspberryPiをターゲットにシンセサイザーを作っていたのですが、どうも挙動が不安定なのです。
 MIDIイベントを取りこぼしているようで、NOTE OFFを拾い損ねて音が鳴りっぱなしになることがランダムに発生します。
 自分で作っているのでコードが悪いのだろうと思って、関連しそうなパラメータを変えてみたりいろいろ試行錯誤してみたのですが、挙動は変わらず。
 検索してみると、同様の事象が報告されています。
USB MIDI, missing MIDI events - Raspberry Pi Forums

 そんなわけで、どうにもならんと思い数か月放置していたのですが、改めて調べてみたことを含めてまとめておきます。
 

発生事象

 USB MIDIキーボードをRaspberryPiに接続し、ALSAシーケンサMIDIイベントを拾い、ALSA PCMで発音させるソフトウェアシンセサイザーを書きました。と言っても、使っているのはALSAなのでRaspberryPi以外のシングルボードコンピュータでも、一般的なPCでもLinuxALSAが動く環境ならば動作します。
 PCで開発・動作確認した場合には問題無かったのに、RaspberryPiで動作させると、特に和音を素早く連打したような場合に一部の音が止まらないことがあることに気づきました。拾ったALSAシーケンサイベントをデバッグ出力させてみると、音が止まらない時はNOTE OFFイベントが拾えていないことが判りました。私の自作ソフトウェアシンセサイザーOpenMPで並列処理させているので、その辺りの実装を疑ってみたり、ALSAシーケンサのパラメータを疑ってみたり、カーネルパラメータの設定値を疑ってみたりしたのですが、特に変化は無くいずれも原因では無さそうです。
 

調査1

 当初使用していたのはArturiaのMINILABというUSB MIDIキーボードだったのですが、前掲のフォーラムなどでArturiaのMIDIコントローラはバグだらけだ*1という趣旨の複数の悪評を見かけていたので、USB MIDIバイスとの相性もあるかと思い、AKAI MPK25を買って試してみたりもしたのですが、事態は解消しませんでした。一方で、KORG NANOKEYの初代では問題が起きにくいような気がします。
 単なるキーしかないNANOKEYでは問題が発生しずらく、MINILABやMPK25ではよく問題が起きるということは、ツマミやベロシティパッドなどのコントローラやLEDがたくさん付いてると消費電力が大きくなって、電力不足で不安定になる?という仮説を立て、RaspberryPiのUSBポートにUSB-MIDIコントローラを直接接続するのではなく、セルフパワーのUSBハブを間に挟んでみましたが、特に変化はありませんでした。
 

調査2

 RaspberryPiにUSB-MIDIキーボードではなく、MIDIインタフェース機能付きUSBオーディオインタフェース)にMIDIキーボードを接続した場合の挙動を調べてみました。
 M-Audio Audiophile USB(ACアダプタ駆動), M-Audio QUATTRO(ACアダプタ駆動), Roland UA-25(USBバスパワー)にKORG microKORGを接続して挙動を確認したところ、USB-MIDIキーボードの場合と同様に、和音を連打したような場合にランダムにNOTE OFFイベントを取りこぼす事象が確認できました。

 調査1と調査2の結果を踏まえると、各社個別のUSB-MIDI実装に今回の問題の原因があるとは考えにくいとともに、USBバスパワー駆動による電力不足問題とも考えられないことが判ります。
 

調査3

 RaspberryPiにUSB-MIDIインタフェースを接続し、MIDI INとMIDI OUTをMIDIケーブルでループバック接続し、送信データと受信データの差異を調べました。
 Roland Super-MPU64(UM-4)を接続して、amidiコマンドを叩く都度、単一のMIDIメッセージを送出した場合には送受信データは完全一致しましたが、まとまった複数のMIDIメッセージ*2をamidiコマンド一発で送信した場合、受信データがランダムに欠落する事象が発生しました。何バイト目が欠落するのかや、計何バイト欠落するかはランダムですが、データの欠落が発生すること自体には再現性があります。

 これまでの調査結果を踏まえると、単位時間当たりに受信するMIDIメッセージが多くなると、RaspberryPiではMIDIメッセージの取りこぼしが発生することが判ります*3
 

調査4

 調査3と同じことをRaspberryPiではなく、NanoPiNeoやOrangePiOneで実行した場合、受信データの欠落は全く発生しませんでした。
 つまり、(この問題はAMD64アーキテクチャのPCではもともと発生していませんが、)RaspberryPiと同じARMアーキテクチャでも発生しないシングルボードコンピュータもあるのです。大きな違いとして、RaspberryPiシリーズはBroadcom製のSoCを搭載していますが、NanoPiNeoやOrangePiOneはAllWinner製のSoCを搭載しています。

 ここまでの調査結果を踏まえると、RaspberryPiシリーズが搭載するBroadcom製のSoCに搭載されたUSBホストコントローラ(とLinuxドライバ)に起因する可能性が高いと考えられます。

 なお、この問題が発生することを確認しているのはRaspberryPiシリーズ初代のmodel Bと2Bと3Bで、4B以降は不明です(4BはUSB3.0対応に伴い、ホストコントローラも変わっているでしょうから、改善されている可能性があるかもしれません)。
 

 というわけで、USB-MIDI入力を使用するつもりならRaspberryPiシリーズはお勧めできないという話でした。

 調べた限りでは、シリアル経由で昔ながらの(USBではない)一般的にはDIN5ピン形状のコネクタで扱われるMIDIを扱う場合には、当然ながらこの問題は関係しないようです。このため、RaspberryPiでシンセサイザーを作るなら、事実上MIDIコネクタの実装が必須と言えるでしょう。一方、AllWinnerのSoC搭載機は前述の通り、USB-MIDIに変な制約は無さそうですし、S/PDIFインタフェースを内蔵*4しているモデルも多いので、デジタルシンセサイザーを作る目的に適していると思います。
 



以上。

*1:フォローしておくと、私のWindows10環境やAMD64Linux環境では特に変な挙動は経験していません。

*2:ここでは約831kBのMIDIデータを使用。データの欠落が発生し始める閾値などは特に調べていない。

*3:一方で、よりデータボリュームの大きいUSBオーディオバイスでのPCM再生時には特に問題が発生しておらず不思議ですが。

*4:但し、コネクタは実装されていない。