ラズパイPico USB-DDC Feedbackを機能するように実装(1)
USBオーディオの規格にはUSB Audio Class 1.0(UAC 1.0)とUSB Audio Class 2.0(UAC 2.0)があるというのはご存じの方も多いとは思いますが、今一度USBオーディオについておさらいしておきましょう。
PCなどの「ホスト」とUSB-DACなどの「ペリフェラル」との間の同期にはこれら3つの方式が規定されています。
USBの転送方式
コントロール転送
USB接続したとき、どんなデバイスであるかを認識するために使われます。
バルク転送
USBメモリやUSB-HDDなど大容量のデータ転送に使われます。エラー時には再送されます。
インターラプト転送
キーボードやマウスなど小容量のデータ転送に使われます。インターラプトという名称ですがホスト側からポーリングされてデータを転送しています。
アイソクロナス転送
動画や音声などリアルタイム性が必要なデータ転送です。バルク転送などよりも優先的に帯域を確保して転送されます。Full-Speedでは1ms間隔。Hi-Speedでは125us間隔で一定のデータレートで送られます。エラーが発生しても再送しません。
USBオーディオの同期方式
ホストとペリフェラル機器はそれぞれ独立した基準クロックを持っていますので、音切れなく再生するには何かしらの方法で再生速度の同期が必要になります。
Asynchronous
USB-DAC内の高精度クロックを基準にDACを動作させることができて高音質化が望めます。ただし、非同期なのでデータのやり取りでペリフェラル側からFeedback値を返してホストに適切なパケット数のデータを送ってもらう必要があります。
Windowsでは、USB-DACのメーカーがそれぞれ専用ドライバを用意しなければならない時代が続きましたが、Win10にてやっと標準搭載されました。
Synchronous
1msのSOFを基準にペリフェラル側がPLLにて同期する方法です。ホスト側のUSBクロックが基準になります。これを実装しているUSBオーディオ機器を見たことありません。
Adaptive
ホスト側が送り出すサンプリング周波数が基準になります。ペリフェラル側はPLLにて同期クロックを生成します。USB-DAC ICであるPCM2704が採用していてUSBスピーカー黎明期では多くの機器に搭載されました。 現在も安いUSBスピーカー、USBドングル型オーディオ機器の多くがこの方式と思われます。(Windowsが長らくAsynchronous方式をサポートして来なかった影響もある)
どのUSBオーディオ通信もアイソクロナス転送が使われます。Full-Speed時は1ms間隔でパケットが転送され、
サンプリング周波数 48kHz時では、48,000 ÷ 1ms = 48サンプルですが、
44.1kHzでは割り切れないので、44サンプルが9回に45サンプルが1回という割合で送られます。(ここがイヤらしいデス)
※Bulk Petだけはバルク転送を使ってオーディオデータを送信しています。
USB Audio Class 1.0 と USB Audio Class 2.0 の差
じつはAsynchronousもAdaptiveもUAC 1.0からあります。UAC 2.0との違いは対応するUSBバージョンとサポートする最大レートになります。
UAC 1.0はUSB 1.1以上で、最大96kHz/24bit
UAC 2.0はUSB 2.0以上で、最大192kHz/24bit らしいです。
らしいと書いたのはホントはチャンネル数やAC3、WMA、DTSなど圧縮データの条件もあってその限りではないっぽいです。
詳細を知りたい方はUSB本家から仕様をダウンロードして読んでください。
https://www.usb.org/document-library/audio-devices-rev-20-and-adopters-agreement
Asynchronousのfeedback
Asynchronous方式はデータ転送量が可変で、USB-DAC側のバッファの残量に応じて増減します。
1ms間隔で送られてくるバケツに例えるとこんな感じです。 USB-DAC側には一定で出力され続けるバッファがあり、バッファ残量を見てfeedback値を戻してバケツに入れる水の量を調整してもらうことでバッファのアンダーフロー、オーバフローを防ぎます。
実はパケット量の増減には規定があり通常時の±1サンプル分までです。
サンプリング周波数48kHzであれば1ms間に48サンプル±1サンプルです。つまり47~49サンプルまで可変できます。
同様に44.1kHzでは44サンプルと45サンプルなので、増減しても43~46サンプルです。
それ以上増減したfeedback値を返すとWindows側に無視されるらしいので気を付けなければいけません。
ラズパイ公式のUSB-DDCのソースは
https://github.com/raspberrypi/pico-playground/blob/master/apps/usb_sound_card/usb_sound_card.c
ラズパイ公式でのフィードバックは以下のように実装されています。
static void _as_sync_packet(struct usb_endpoint *ep) {
struct usb_buffer *buffer = usb_current_in_packet_buffer(ep);
buffer->data_len = 3;
// todo lie thru our teeth for now
uint feedback = (audio_state.freq << 14u) / 1000u;
buffer->data[0] = feedback;
buffer->data[1] = feedback >> 8u;
buffer->data[2] = feedback >> 16u;
// keep on truckin'
usb_grow_transfer(ep->current_transfer, 1);
usb_packet_done(ep);
}
デバッグコードを消すとシンプルです。ここで 赤文字の1行に注目です。
audio_state.freq が 48000 のとき、feedbackは「786432(0x0C0000)」
44100 のとき、「722534(0x0B0666)」という固定値が返されています。
あらら、バッファ残量をみてフィードバックしていませんね。「lie thru our teeth for now」ってコメントが悔しさを物語っているようです。
音を聴いていると、ホストとUSB-DACのクロックの差によるオーバーフロー、アンダーフローが発生して時々「プチっ」というノイズが確認できます。
私の自宅の環境ではPCは40~50秒で音とびが発生しますが、iPadは2~3分は平気なようです。頻度は低いけどレコードのパチって感じのノイズが時々発生します。
最近レコードが流行っているらしいので、そういう雰囲気を味わうのもいいか?
なんてね。
対策を考えます。
« チップトランジスタのhfet測定ツール | トップページ | ラズパイPico USB-DDC Feedbackを機能するように実装(2) »
「Raspberry Pi pico」カテゴリの記事
- RPiZeroRP2040 USB-DDC基板試作(2024.09.01)
- ラズパイPico USB-DDC Feedbackを機能するように実装(3)(2024.08.24)
- ラズパイPico USB-DDC Feedbackを機能するように実装(2)(2024.08.23)
- ラズパイPico USB-DDC Feedbackを機能するように実装(1)(2024.08.22)
- Raspberry Pi Pico 2 発表されました。(2024.08.09)
« チップトランジスタのhfet測定ツール | トップページ | ラズパイPico USB-DDC Feedbackを機能するように実装(2) »
コメント