Lambdaカクテル

京都在住Webエンジニアの日記です

Invite link for Scalaわいわいランド

ラズパイに接続した音声デバイスを、ネットワーク内の別のマシンから利用することができる(特殊な機器は不要)

Bluetoothトランスミッタの高級版みたいなことをやりたくて試行錯誤した結果うまくいったのでメモ。

元気に動いています

この記事では、ラズパイを用いて音声入出力デバイスをネットワーク越しに共有し、PulseAudioまたはPipeWireを使う他のLinuxマシンから利用する方法を解説する。

結果としてはほぼ成功し、マイク入力に若干の不安定さがあるものの、ヘッドホンとしての利用にはほぼ問題なく使えることが分かった。Bluetoothトランスミッタといった特殊な機器を使わず、ラズパイ、一般的なWi-Fiドングル、一般的なUSB音声機器、一般的なLinuxマシンという普遍的な構成を用いて音声デバイスを共有できるという、意義ある結果が得られた。

マイクを繋ぎ替える手間を無くしたい

自分はLogicoolのG733というゲーミングヘッドセット(もちろん、七色に光る)を使っていて、仕事のミーティングからApexのチーム戦にまで幅広く使っている。

このヘッドセット自体の品質はめちゃくちゃ良くて、音質の面でも遜色無いのだけれど、Bluetoothではなく専用のドングルを刺す必要があるというウィークポイントを抱えている。というのも、ヘッドホンとマイクという広帯域の伝送を行うためにはBluetoothのプロファイルは力不足で帯域が足りず、おそらく独自のプロトコルで2.4GHz帯域のあたりを伝送する必要があるのだろうと思う。

そして、今自分はリモートワークで働いており基本的に物理的に出社せずに家で仕事をしている。そして仕事が終わったらプライベートマシンにログインしてはてブをするなりFactorioをするなりScalaを書く、という暮らしをしている。

そういう事情があって、マシンを切り替えるときにはガチャガチャとUSBポートから機器を取り外して差し替えるという手間が発生するのだが、ちょっとスマートじゃないよなあ、という思いが頭に蓄積していき、「これってネットワーク越しになんとかできるんじゃないの?Linuxだし」と思い立って、ネットワーク越しに音声ストリームを伝送する方法について調べ、実際に使えるか試してみることにした。家にたまたまRaspberry Pi 3B+が転がっていたので、これを音声デバイスの接続先として使うことにした。

最近はラズパイ4が出ているので、これを使えばよいと思う。もちろん、Intel NUCを使っても良いし、他の適当なLinuxが動くマシンであればなんでも良い。

ゼロから音声入出力を構築するのは大変なので、ラズパイはランレベルを上げて最初からデスクトップ環境が立ち上がるようにしておくのが吉。

どういうプロトコルを使うか

ところで、今家のネットワークは1GbpsのEthernetにWi-Fi 6規格の無線LANとが組み合わさった形態になっている。というわけで、帯域の占有に関してはあまり制約が無い。これがもし10Mbpsしか出ないネットワーク環境だったら一度エンコードするといった手間が必要だったかもしれないが、広帯域を確保できるので最初からエンコーディングしない生のストリームを流してよいと判断した。

家のマシンはほぼLinuxで、共通の音声インフラとしてPulseAudioというものが利用できる。そして、PulseAudioにはTCPを使ってネイティブに音声デバイスを透過的に共有するための機能があるため、これを利用して音声入出力を共有することにした。

wiki.archlinux.jp

今回は、module-native-protocol-tcpというモジュールを使ってネットワーク上を伝送することにした。

もし仮にエンコーディングが必要だった場合は、ffmpeg等を使ってエンコードしたストリームを使っていたかもしれないが、おそらくこの方式はとても大変なものになりそう!!

システム構成

構成を以下に示す:

PulseAudio native protocolを使ってRaspberry Pi B+に刺さっているUSB音声デバイスを、同一ネットワークセグメントにある自作PCへ共有する。音声デバイスのコントロールは全てPulseAudio native protocol経由で行うことができる。また、PulseAudio native protocolはサービスディスカバリの機能を持たないため、Zeroconf (mDNS)を用いて互いを発見し、接続を確立する。これによりほぼ全自動でデバイスの共有が行われる。

前提条件

  • 互いのマシンがネットワーク的に疎通しなければならない。
    • EthernetでもWi-Fiでもよい。自分はWi-FIで動作させて良好な結果を得ている。
  • 互いのマシンがTCP 4713(mDNS)とUDP 5353(PulseAudio Native Protocol)を許可できなければならない。
  • 互いのマシンが音声デバイス基盤としてPulseAudioまたはPipeWireを使っていなければならない。
  • 互いのマシンの時刻は正確に同期しなければならない。
    • 時刻がおかしいと音声が狂うらしい*1

ラズパイ側の下準備

最新のラズパイでなければPulseAudioが使われていないので、まずOSを最新に更新する必要があった:

$ apt update
$ apt full-upgrade

これにより自動的にPulseAudioが有効化される。

PulseAudioによるデバイスの共有

ラズパイ側で行わなければならない事は以下の3つである:

  • PulseAudioにおいてmodule-native-protocol-tcpを有効化する
  • PulseAudioにおいてmodule-zeroconf-publishを有効化する
  • Zeroconfを担当するデーモンであるavahi-daemonを有効化・起動する

PulseAudioでモジュールを有効化する

一般的にPulseAudioのモジュール設定は/etc/pulse/default.paに格納することになっており、Raspberry Pi OSもこの例に漏れない:

...

load-module module-native-protocol-tcp
load-module module-zeroconf-publish
...

また、Zeroconf用モジュールは標準外のモジュールであるため、パッケージのインストールが必要である:

$ apt install pulseaudio-module-zeroconf

設定はマシン再起動時、またはログイン時に反映される。

Zeroconfを有効化する

Zeroconfを喋るデーモンであるavahi-daemonを起動して有効化する:

$ systemctl enable avahi-daemon
$ systemctl start avahi-daemon

PulseAudio = Avahi間のネゴシエーションは勝手にmodule-zeroconf-publishがやってくれるので、特になにもする必要はない。

対向マシンによるデバイスの利用

さて、ラズパイ側の準備は完了した。対向マシン側で行わなければならない事は以下の3つである:

  • PulseAudioにおいてmodule-native-protocol-tcpを有効化する
  • PulseAudioにおいてmodule-zeroconf-discoverを有効化する
    • discoverになっている点に注意
  • Zeroconfを担当するデーモンであるavahi-daemonを有効化・起動する

PulseAudioでモジュールを有効化する

ラズパイでやったのと同じように、/etc/pulse/defualt.paを編集する:

...

load-module module-native-protocol-tcp
load-module module-zeroconf-discover
...

また、こちらのマシンでもpulseaudio-module-zeroconfパッケージをインストールしておく必要がある。OSによりパッケージ名が異なる可能性がある。

Zeroconfを有効化する

ラズパイ同様に、avahi-daemonを起動して有効化する:

$ systemctl enable avahi-daemon
$ systemctl start avahi-daemon

チェックポイント

互いのマシンを再起動させれば、ラズパイに刺したデバイスが対向マシンから見える状態になっている、つまり具体的にはオーディオデバイス一覧に表示される状態になっているはずである。

  • 互いのマシンのファイアウォールがTCP4713とUDP5353を許可していることを確認する。
    • デフォルト状態ではラズパイにファイアウォールはかかっていないはず
  • 音声デバイスを探す側のマシンでavahi-browse --all --ignore-local --resolve --terminateなどを実行し、Zeroconf経由でデバイスが見えていることを確認する。

自作PCからラズパイ上の音声デバイスが見えている状態

トラブルシューティング

  • マイクの音が入らない
    • 音量が異常に小さく設定されていることが殆どなので、alsamixerを使ってマイク音量を最大に上げる。
  • マイクの音のピッチがどんどん上がっていく(怪奇現象)
    • 原因不明。ラズパイ側でpulseaudio -kし、pulseaudio --startすることでPulseAudioを再起動して対処した。
  • 音声が途切れる
    • ネットワークの帯域が詰まっているおそれがある。
  • 同じデバイスがなぜか2つ見える
    • IPv4/v6のデュアルスタック構成で動作させている場合、Zeroconfは両方のアドレスを広告するためデバイスが2つ見えてしまう。
  • デバイスを見られるマシンを制限したい
    • PulseAudioの設定ファイルで許可範囲を狭めてください

PipeWireで使う

いくつかの最新のLinuxディストリビューションでは、PulseAudioの代わりにPipeWireが使われていることがある。PipeWireはPulseAudio native protocolを(どこかの互換性レイヤの中で)サポートしているので、pactl load-module module-zeroconf-discoverを実行するか、~/.config/pipewire/pipewire.confに適切な設定を行うことでネットワーク越しのデバイスが見えるようになる。

wiki.archlinux.jp

実際に使ってみた感想

そもそも高速な自宅内LANで共有しているので、わずかなレイテンシがあるもののほぼ気にならない。FPSなどを利用するときは問題になるかもしれないが、殆どのユースケースで問題なく使えると思う。ラズパイに刺した音声デバイスがそのまま見えるというのがポイントで、ヘッドセット上で音量調整をしてもちゃんと反映されるなど、かなり行儀の良い振舞いをしている。単に音をどこかに飛ばしたいときは別途オーディオインターフェイスを使えばよいだろう。帯域が問題にならない場合は、この手法がそのまま使えるはずだ。

音声デバイスによっては、前述のマイク音声のピッチが時々おかしくなるという問題が発生するが、どこの箇所で問題が発生しているのかは突き止められなかったため、PulseAudioを再起動するという対症療法を見付けるに留まっている。

参考文献

こちらに先行研究があり、参考にしました。

kakurasan.hatenadiary.jp

★記事をRTしてもらえると喜びます
Webアプリケーション開発関連の記事を投稿しています.読者になってみませんか?