2025/2/3(月)

libsoundioの調査を進めた。とりあえず公式のexampleに従うことで音は出て、次はミキサとかの手実装を進めようと思ったのだが、その前にデバイス切り替え時の挙動の対処とレイテンシに関する考察をしておくことにした。

デバイス切り替え時の挙動について。頑健なプログラムであればPCに挿さったイヤホンを引っこ抜いたくらいで落ちたりはせず、出力先をスピーカーに切り替えて動き続けて欲しいものである。しかしlibsoundioは別にそういう自動切換えみたいな機能は持たないので、自力でどうにかする必要がある。

SoundIo::on_devices_changeというコールバックがある。こいつでデバイスの状態の変化が検出できる。デバイスが変化したら、直後にSoundIoOutStreamをポーズ・破棄して、現在使用中のデバイスをunrefして、再びデバイスとOutStreamを仕立て直せば鳴り続けることを確認した。公式にはon_backend_disconnectからの復帰のサンプルはあるが、on_devices_changeからの復帰は無かったので自力で書いて実験する羽目になった。

なお、デバイスの挿抜とかでのみon_devices_changeは発火するものと思っていたが、どうも音量調整とかでも発火するようだ。soundio_device_equalという関数でデバイスの同一性が判定できるので、デフォルト出力デバイスが変化していたらそれに従うという処理にした。

次にレイテンシに関する考察。libsoundioのwrite_callbackでは与えられたframe_minからframe_maxまでの間の数だけサンプルをバッファに書き込める。frame_maxまで書き込めば動作は安定するが、遅延は大きい。frame_minに近くなると遅延は小さくなるが動作は不安定になり、バッファアンダーフローが勃発する。

自分の環境(Windows)ではサンプルレート48kHzでframe_minが0、frame_maxが96000=2秒分ということになった。さすがにゲームで2秒の遅延は論外だ。アンダーフローを起こさないラインを探ったところ、1024以下だと落ちまくる。1536あたりが常時安定するギリギリのラインということが分かった。1536/48000=32msで、バッファリングの仕組みを考えると最悪で倍の64msを見込んだ方が良い。これは遅延として許されるラインなのかよく分からなかったが、ネットの研究を見ると恐らくは問題あるかないかギリギリくらいのラインであることが分かった。

https://www.jstage.jst.go.jp/article/itej/69/5/69_408/_pdf/-char/ja

https://note.com/tkykmts/n/n342fc7c52472

https://note.com/adx_kawaguchi/n/n689aa13de720#1NnFR

どうしてこんな長いんだろうと思ったが、恐らくはWindowsのサウンドの仕組みがあまり良くなく、さらにlibsoundioもそこまでWindowsを重視していないことが背景にあるのではないかと思う。

https://github.com/andrewrk/libsoundio/issues/221

まあ今回作っているライブラリはプロユースというより実験・教育用というコンセプトではあるのでわずかな音声遅延にとことん拘る程のつもりもないのだが、実際使ってみて気になるようであればlibsoundio以外の選択肢も考えたいと思う。WindowsでだけOS APIを直接触るとか。しかしそもそもWindowsのオーディオ自体が微妙と考えるとOSのAPIを直接触っても仕方ないかもしれない。

libsoundioはASIOをサポートしていないので、それをサポートしてみる手はある。


ゲーム開発ライブラリについて、何やらGithub Actions上でエラーが出ていたのを対処した。色々な環境でビルドさせているので、自分の環境でエラーが出なくても別の環境でエラーが出ることもある。

ライブラリ本体はC++20でビルドしているのに対し利用側はC++11でも構わないように作っているのだが、どうやらインクルードヘッダの中でC++14か17相当の機能を使ってしまったようだ。戻り値の型推論をさせるとき、C++11ではauto func() { ... }ではなくauto func() -> xxx {}のようにする必要がある。逆に手元環境はなぜ問題なく通っていたのか疑問。

それからもう一つ、Ubuntu AArch64でのビルド環境でなぜかプロセッサがx86_64と認識されており、依存しているライブラリmuFFTがx86の機能を使おうとしてエラーを出していた。CMAKE_SYSTEM_PROCESSORの値がおかしいっぽいが、勝手に設定されるものではないのか。MacのARM64ビルドは実際ちゃんとプロセッサを検出できているのに。

Categories: