0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Ubuntu + PipeWire で「ゲーム機もPCもiPhoneも全部まとめて聴ける」低遅延ゲーミングオーディオミキサーを作った

0
Posted at

Ubuntu + PipeWire で「ゲーム機もPCもiPhoneも全部まとめて聴ける」低遅延ゲーミングオーディオミキサーを作った

3行でまとめ

  • メインPC・Switch 2・iPhone など 5デバイス以上の音源を1台のUbuntu自宅サーバーでミックスして、遅延8ms で聴けるようにしました
  • ソフトはほぼ PipeWire の標準機能だけ。自分で書いたのは配線スクリプトと、スマホ用のWebミキサー(Python標準ライブラリのみ)です
  • 低遅延のミックスだけでなく、スピーカーやヘッドホンといった複数のDACに同時に音声出力できます

構成・実際のスクリプト・ハマりのポイントをGithubで公開しています → tomohiko-ohhashi/auras-public

動機

メインPC、ゲーム機、仕事用PC、スマホ・・・デスクの上に音の出るデバイスが複数あります。これらの音声入出力を切り替えることが非常に手間でした。

下記のことを同時に満たす市販のオーディオミキサーは見当たらず、今回作成しました。

  • ゲームをするので遅延は一桁msに抑えたい
  • アナログで混ぜたときに乗る「ジー」というノイズ(グラウンドループ)を避けたい
  • 手元のスマホから音量やイコライザーを操作したい

買ったもの・使ったもの

ハードウェア

機材 役割 備考
RME Digiface USB 中核。光デジタル4入力/4出力+ヘッドホン出力のオーディオIF 新規購入(約9万円)。この構成の要。このデバイスとWinかMacだけでも4系統のミキサーとしては成立する
自作Ubuntuサーバー ミックス処理(PipeWire) 手持ち流用(Ryzen 5 8500G / ASRock B850)
ゼンハイザー BTA1 Auracast送信機。ミックスをワイヤレス放送する 新規購入(約2.5万円)。Bluetoothヘッドホンを切り替える手間を抑えるため。Auracast対応している音響機器はまだ市場に少ない。無くても成立
ASTRO HDMIアダプタ Switch 2 の音を光に分離 手持ち流用。Nintendo Switch2 はUSB音声が44.1kHz固定なので48kHzで出力するためにHDMI経由が必要
FX-AUDIO FX-D03J メインPCのUSB音声を光に変換するDDC 手持ち流用(数千円)
光ケーブル ×4〜5本 各機器 → Digiface 数百円/本

必須なのは実質「光入力が複数あるオーディオIF」だけです。テレビのように最初から光出力がある機器はケーブル1本挿すだけで参加できます。

ソフトウェア

ソフト バージョン 役割
Ubuntu Desktop 26.04 LTS サーバーOS
PipeWire 1.6.2 主役。ミックス・配線・EQ、全部これ
WirePlumber 0.5.13 デバイス管理(プロファイルやルール)
shairport-sync 4.x AirPlay受信(iPhoneやMacの音をミックスに入れる)
systemd (user unit) - サーバー起動時のミキサーの自動復元
Python 3.14(標準ライブラリのみ) スマホ用Webミキサーの作成に使用

フレームワークやnpmパッケージは使っていません。あとで書きますが、PipeWireがCLIから何でも操作できるおかげで、Webミキサーは「CLIをHTTPに変換する薄い板」で済みました。

全体構成

設計のポイントは2つです。

① 音は全部「光」で集める。 光ケーブルは電気的に絶縁されているので、グラウンドループが原理的に発生しません。そして、BluetoothやHDMIキャプチャ経由の音(50〜100ms遅れます)と違って、遅延はほぼゼロです。

② ミックスはPipeWireにやらせる。 PipeWireは「Linuxで音を出す基盤」という印象が強いですが、実態は自由に配線できる音声ルーティングマトリクスです。ノード同士を pw-link で繋ぐだけで、ミキサーアプリを書かなくてもミキサーになります。

PipeWire側でやったこと

やったことは実質4つだけです。

1. 全系統を48kHzに固定する

サンプルレートが混ざると変換だらけになって音も遅延も濁るので、設定ファイル1枚で統一します。

/etc/pipewire/pipewire.conf.d/10-auras-48k.conf
context.properties = {
    default.clock.rate          = 48000
    default.clock.allowed-rates = [ 48000 ]
    default.clock.quantum       = 64
}

2. 配線する

光入力のポートを出口(ヘッドホン)に pw-link で繋ぎます。同じ出口に複数の入力を繋ぐと、PipeWireが勝手にミックスしてくれます。これだけでもうミキサーです。

# 光入力1(左右)→ ヘッドホン出力(左右)
pw-link "alsa_input.usb-RME_Digiface_USB…:capture_AUX0"  "alsa_output.usb-RME_Digiface_USB…:playback_AUX32"
pw-link "alsa_input.usb-RME_Digiface_USB…:capture_AUX1"  "alsa_output.usb-RME_Digiface_USB…:playback_AUX33"

ポート名は pw-link -o(出力側)と pw-link -i(入力側)で一覧できます。まずこれを眺めると、自分のマシンの中の配線盤が見えてきて楽しいです。

3. 入力ごとの音量とEQを作る

各入力とヘッドホンの間に「音量とEQを持つ仮想ノード」(PipeWire標準の filter-chain)を1段ずつ挟みます。このノードの音量を wpctl で動かせば入力別フェーダーになり、バイキャッドフィルタを5個直列にすれば5バンドEQになります。遅延コストは1段あたり約1.3msです。

設定はこんな雰囲気です(抜粋)。EQのゲインは実行中に pw-cli で書き換えられるので、後述のWebミキサーからスライダーで操作できます。

{ name = libpipewire-module-filter-chain
    args = {
        filter.graph = {
            nodes = [
                { type = builtin name = eq1 label = bq_lowshelf  control = { "Freq" = 80.0    "Gain" = 0.0 } }
                { type = builtin name = eq2 label = bq_peaking   control = { "Freq" = 250.0   "Gain" = 0.0 } }
                # …1k / 4k / 10k と続く
            ]
        }
        capture.props  = { node.name = "auras_in_opt1" media.class = Audio/Sink }
        playback.props = { node.name = "auras_in_opt1_out" node.autoconnect = false }
    }
}

4. systemdで永続化する

pw-link の配線はPipeWireの再起動で全部消えます。そこで「起動時に配線を張り直すサービス」を systemd のユーザーユニットで常駐させます。これで電源を入れるだけでミキサーが復元されるようになりました。

遅延の実測

光出力→光入力をケーブルで折り返して jack_iodelay で測りました。

バッファ (quantum) 往復遅延 結果
256 16.0ms 安定
64(採用) 8.0ms 安定
48 6.2ms 不安定(→ハマったポイント2)
32 - 処理が破綻して音にならない

quantum は「音をひとすくいする量」で、小さくするほど遅延が減る代わりに、処理の締切が厳しくなります。8msは音速に換算すると「スピーカーから約2.7m離れて聴く」のと同じで、体感では分かりません。

スマホから操作するWebミキサー

「スマホから音量を操作したい」の実装です。Python標準ライブラリの http.server だけで書いた小さなWebサーバーで、やっていることはリクエストに応じて wpctlpw-cli を実行するだけです。それでもこれだけの機能になりました。

webmixer.png

  • 入力ごとの音量・ミュート・5バンドEQ(プリセット保存つき)
  • 出力先の切り替え(USB DAC・AirPlayスピーカー・ワイヤレスドングルなど)と複数出力の同時ON
  • バッファサイズ(quantum)のワンタップ切り替え

共有したい・ハマったポイント5選

作りっぱなしの記事にしても仕方ないので、実際に踏んだ落とし穴を共有します。

1. 既定の出力にミキサー自身を選ぶと世界が壊れる

「ミックスのコピーをOSの既定出力にも流す」機能を作ったところ、既定出力にミキサー自身が選ばれた瞬間、モニター→コピー→同じデバイスという循環グラフが完成して、PipeWire全体がスケジュール不能になりました。xrunが数万回積み上がり、無関係なブラウザの動画までカクカクに。循環を検出して配線を切るガード処理は必須です。

2. EQを1プロセスに5個入れたら音が消えた

5入力分のEQ(filter-chain)を1つのプロセスにまとめたら、1本のデータスレッドが5個ぶんを直列処理することになり、quantum 48(1ms周期)の締切に間に合わず、光入力の音が全出力で無音になりました。1入力=1プロセスに分割したら解決。処理を並列にできる形にしておくのは大事です。

なお、分割後も quantum 48 への切り替え自体がハード依存で不安定だったため(切り替え成功率 約25%)、最終的に既定は 64 に落ち着きました。1.8ms のために不安定さを抱えるより、確実な 8ms です。

3. 「固定遅延」が固定ではなかった

機器側の固定遅延だと思っていた255フレーム、quantumを下げたら201フレームに縮みました。ドライバ内部のバッファがquantumに比例していたようです。思い込みで計算せず、測る。今回一番の教訓かもしれません。

4. マザーボードのオンボード音声が実はUSB接続だった

最近のマザーボード(Realtek ALC4080系)は、オンボード音声が内部的にUSB接続になっています。lsusb に「Generic USB Audio」がいたらそれです。民生USBオーディオと同じく小さいquantumへの追従が苦手なので、低遅延設定と組み合わせるときは注意が必要でした。

5. Auracastが思った以上に便利だった

光出力の1本をAuracast送信機(BTA1)に挿すと、ミックスを台数無制限でワイヤレス放送できます。対応イヤホン・ヘッドホンを持っている家族がそれぞれ勝手に聴ける、小さな放送局のような状態になりました。遅延は約50msあるのでゲームには不向きですが、ながら聴きには十分です。

まとめ

  • 光デジタルで集めて、PipeWireで混ぜる。これだけで「遅延8ms・ノイズなし・スマホ操作可・複数同時出力」のミキサーが組めました
  • 実際に今回作成したミキサーとハマりポイントはGitHubリポジトリで公開しています → https://github.com/tomohiko-ohhashi/auras-public

参考

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?