Raspberry Pi Zero WH で USB to Airplay Audio トランスミッターを作る備忘録

Apple
Photo by hyt.

Raspberry Pi Zero で USB to Airplay Audio トランスミッターを作る備忘録です.

Linux の USB Gadget 機能を使って,PCからは USB Audio として認識され,送り込まれた音声を適当な Airplay 再生機器に送信する中継機(トランスミッター)を作ってみた備忘録です.

接続したPCから中継機を操作できるよう,単に USB Audio として認識されるだけでなく,シリアル接続で操作できるようにもしています.

使用機材

使用機材は Raspberry Pi Zero WH と64GBの microSD カードですが,microSD の容量は8GBでも大丈夫なはずです.また,Raspberry Pi Zero 2, Raspberry pi 4, 5 でもできると思います.

https://amzn.to/45b1Iok

Raspberry pi OS の導入

使用した OS は純正の Raspberry Pi OS Lite です.導入自体は,Raspberry Pi Imager

Just a moment...

を使って最新安定版(legacy はダメ)のものを microSD に書き込むだけですし,この辺りの説明は例えば

Raspberry Pi Imager のインストールと使い方 - Qiita
Raspberry Pi Imager は Raspberry Pi 財団が提供している Raspberry Pi の SD カード作成ツールです。無料で利用できます。このツールを使うと、ユーザーは…

などを見れば詳しい解説があるので,本ページでは行いません.また,最近はデフォルトで作られる

  • USER: pi
  • PASSWORD: raspberry

を無効化することが推奨ですが,今回はこのデフォルトのユーザーを使って設定していくことにします.

Raspberry pi OS の最新開発版へのアップデート

Raspberry pi OS の本稿執筆時点の最新安定版は debian 12 bookworm ベースですが,残念ながらこの最新安定版に含まれている pipewire は ver. 0.3.65 で,少なくとも私が試した限りでは,まともに Airplay 機器を認識してくれません.

debian 12 bookworm の backport に含まれている pipewire 1.0.5 も試しましたが,残念ながら今度は pipewire 自体が起動してくれません.したがって今回は已む無く,開発版 debian 13 trixie ベースに OS をアップデートしました.

trixie ベースへのアップデートは簡単で,まず,/etc/apt/sources.list を以下のように書き換えます.

$ sudo vi /etc/apt/sources.list
deb [ arch=armhf ] http://raspbian.raspberrypi.com/raspbian/ trixie main contrib non-free rpi
# Uncomment line below then 'apt-get update' to enable 'apt-get source'
#deb-src http://raspbian.raspberrypi.com/raspbian/ trixie main contrib non-free rpi

その後,以下のとおりアップグレードし,再起動するだけです.

$ sudo apt update
$ sudo apt dist-upgrade;sudo apt autoremove
$ sudo reboot

再起動後,アップグレードできていることを以下で確かめることができます.

$ cat /etc/debian_version
trixie/sid

Pipewire の導入

次は,オーディオを Airplay 再生機器に中継を担う Pipewire と Pulseaudio のコントロールコマンドである pactl(パッケージとしては pulseaudio-utils)を導入します.Pipewire は Pulseaudio の置き換(後継?)なので,pactl と合わせて Pulseaudio 関係のパッケージを入れないように注意してください.

具体的には,以下のように導入してから,再起動します.

$ sudo apt install pipewire-audio pipewire-pulse pipewire-alsa libspa-0.2-bluetooth wireplumber pipewire-media-session- pulseaudio-utils

再起動後,Pipewire が起動していることを以下のとおり確かめることができます.Pipewire ver. 1.0.3 が起動していることが分かります.

$ pactl info
Server String: /run/user/1000/pulse/native
Library Protocol Version: 35
Server Protocol Version: 35
Is Local: yes
Client Index: 30830
Tile Size: 65496
User Name: pi
Host Name: rbpz2
Server Name: PulseAudio (on PipeWire 1.0.3)
Server Version: 15.0.0
Default Sample Specification: float32le 2ch 44100Hz
Default Channel Map: front-left,front-right
Default Sink: alsa_output.platform-20980000.usb.stereo-fallback
Default Source: alsa_input.platform-20980000.usb.stereo-fallback
Cookie: b402:f37b

ローカルネットワーク内の Airplay 機器のチェック

ここで,ローカルネットワークに接続された Airplay 機器が pipewire からどのように認識されるのかをチェックします.

まずやるべきことは pipewire ROAP Discover モジュールの読み込みですが,これについては以下の記事をご覧ください.

pipewire で Airplay する備忘録
pipewire で Airplay する備忘録です. いつもの通り結論から記すと, PipeWire RAOP Discover モジュールを読み込む ことで使えますが,いまのところどの機器を使うのかによって再生できたりできなかったりする...

なお,次回以降自動的に ROAP Discover モジュールが読み込まれるよう,~/.config/pipewire/pipewire.conf.d/raop-discover.conf を以下の通り設定してください.

$ vi ~/.config/pipewire/pipewire.conf.d/raop-discover.conf
context.modules = [
   {
       name = libpipewire-module-raop-discover
       args = {}
   }
]

ROAP Discover モジュールを読み込むことで,ローカルネットワーク内の Airplay 再生機器を pipewire に認識させることができます.認識されている Airplay 再生機器としてどのようなものがあるかは,pactl list sinks を用いて調べることができます.

$ pactl list sinks
...
Sink #173
  State: SUSPENDED
  Name: raop_sink.iPhone4sw.local.192.168.1.100.5000
  Description: AirSpeaker
  Driver: PipeWire
  Sample Specification: s16le 2ch 44100Hz
...

上の例だと,再生機器として,raop_sink.iPhone4sw.local.192.168.1.104.5000 が認識されていることが分かります(Airplay 機器は必ず roap_sink から始まる名前になるようです).

認識されない場合はそもそも Raspberry pi がローカルネットワークに接続されているか否かやファイヤーウォールの設定を確かめてください.

Linux USB Gadget 機能の有効化

次は,PC等から Raspberry pi が UAB Audio として認識されるように,Linux USB Gadget 機能を有効化します.

まず,/boot/firmware/config.txt ファイルを以下の内容を追記します(以前は /boot 直下のファイルを編集していましたが,現在は /boot/firmware 以下になっているようです).

$ sudo vi /boot/firmware/config.txt
...
[all]
# Enable DWC2 USB Driver
dtoverlay=dwc2

また,/etc/modules に以下の内容を追記します.

$ sudo vi /etc/modules
...
[all]
# Enable DWC2 USB Driver
dwc2
libcomposite

ここで再起動し,モジュールが読み込まれていることをチェックしておきます.

$ sudo reboot
$ lsmod | grep dwc2
dwc2                  172032  0
roles                  16384  1 dwc2

読み込めていることを確認できたら, /usr/local/bin/on.MFCGadget ファイルを以下の内容で作成し,実行権限をつけます(44,100 Hz/16bit の USB Audio として認識されるよう設定しています.理由は次々節をご覧ください).

$ sudo vi /usr/local/bin/on.MFCGadget
#!/bin/sh
cd /sys/kernel/config/usb_gadget
/usr/bin/mkdir g1
cd g1
/bin/echo 0x1d6b > idVendor
/bin/echo 0x0104 > idProduct
/bin/echo 0x0100 > bcdDevice
/bin/echo 0x0200 > bcdUSB
/usr/bin/mkdir strings/0x409
/bin/echo "24051401"> strings/0x409/serialnumber
/bin/echo "labohyt" > strings/0x409/manufacturer
/bin/echo "Multifunction Composite Gadget" > strings/0x409/product
/bin/echo "0xEF" > bDeviceClass
/bin/echo "0x02" > bDeviceSubClass
/bin/echo "0x01" > bDeviceProtocol
/usr/bin/mkdir -p configs/c.1
/usr/bin/mkdir -p configs/c.1/strings/0x409
/usr/bin/mkdir -p functions/acm.GS0
/usr/bin/mkdir -p functions/uac2.usb0
/bin/echo 44100 > functions/uac2.usb0/c_srate
/bin/echo 2 > functions/uac2.usb0/c_ssize
/bin/echo 3 > functions/uac2.usb0/c_chmask
/bin/echo 44100 > functions/uac2.usb0/p_srate
/bin/echo 2 > functions/uac2.usb0/p_ssize
/bin/echo 3 > functions/uac2.usb0/p_chmask
/usr/bin/ln -s functions/acm.GS0 configs/c.1
/usr/bin/ln -s functions/uac2.usb0 configs/c.1
/usr/bin/ls /sys/class/udc > UDC

$ sudo chmod +x /usr/local/bin/on.MFCGadget

また,このコマンドが起動時に自動的に実行されるよう /etc/rc.local に以下の内容を追記します.

$ sudo vi /etc/rc.local
.....
# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

/usr/local/bin/on.MFCGadget

exit 0

この状態で,いったん Raspberry pi の電源を切り,以下の写真のようにしてPC等につなぐと,Raspberry pi が USB Audio かつ Serial 接続可能機器として認識されるはずです.

Photo by hyt.

PCの OS が Linux (Ubuntu 22.04)の場合は以下の通りです.

$ lsusb
...
Bus 001 Device 008: ID 1d6b:0104 Linux Foundation Multifunction Composite Gadget
...

$ pactl list sinks
...
シンク #61
状態: SUSPENDED
名前: alsa_output.usb-labohyt_Multifunction_Composite_Gadget_24051401-02.analog-stereo
説明: Multifunction Composite Gadget Analog Stereo
ドライバー: PipeWire
サンプル仕様: s16le 2ch 44100Hz
チャンネルマップ: front-left,front-right
...

$ ls /dev/ttyACM*
/dev/ttyACM0

Windows の場合は USB Audio 昨日は Source/Sink,USB Serial ポートは COM3 として認識されましたが,特に Serial の認識については機器によりポート番号が違ってくるのではないかと思います.

PC から Serial 接続可能にする

前節で Linux USB Gadget の Serial を有効化しましたが,このままだと Raspberry pi OS を Serial ポート経由で操作できないので,これを可能にするための設定を行います.なお,本件について詳しくは以前の以下の記事をご覧ください.ここでは最低限の手順のみ示します.

Linux USB Gadget シリアル接続を Raspberry pi で使う備忘録
Linux の USB Gadget シリアル接続を Raspberry pi で使う備忘録です. 動作確認したデバイスとOS 動作することを確認した環境は以下の通りですが,USB Gadget に対応した SBC なら恐らく動くと思います...

まず,systemctl edit コマンドを用いて,systemd の設定ファイルを以下のように編集します.注意として,必ず ## Edits below this comment will be discarded の上に [Service] 以下の内容を記してください.

$ sudo systemctl edit [email protected]
### Editing /etc/systemd/system/[email protected]/override.conf
### Anything between here and the comment below will become the contents of the drop-in file

[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin pi --keep-baud 115200,57600,38400,9600 - $TERM

### Edits below this comment will be discarded
....

その後,[email protected] を以下の手順で自動起動するよう設定します.

sudo systemctl enable [email protected]
sudo systemctl start [email protected]

正常に起動していることを以下のように確認します.

$ sudo systemctl status [email protected][email protected] - Serial Getty on ttyGS0
     Loaded: loaded (/usr/lib/systemd/system/[email protected]; enabled; preset: enabled)
    Drop-In: /etc/systemd/system/[email protected]
             └─override.conf
     Active: active (running) since Thu 2024-06-06 11:25:28 JST; 25min ago
       Docs: man:agetty(8)
             man:systemd-getty-generator(8)
             https://0pointer.de/blog/projects/serial-console.html
   Main PID: 729 (login)
      Tasks: 0 (limit: 387)
        CPU: 212ms
     CGroup: /system.slice/system-serial\x2dgetty.slice/[email protected]
             ‣ 729 /bin/login -f --

これで Linux や Windows から Raspberry pi のシェルにユーザー名,パスワードの入力なしで接続ができるようになっているはずです.

loopback を設定する

次は Pipewire のサンプリングレートと loopback の設定を行います.

まず,Pipewire のデフォルトサンプリングレートを 44,100 Hz に設定します.これは以下のように行います(~/.config/pipewire/pipewire.conf の方が良いかもしれません).

$ sudo vi /usr/share/pipewire/pipewire.conf
...
    ## Properties for the DSP configuration.
    default.clock.rate          = 44100
    default.clock.allowed-rates = [ 44100 ]
    #default.clock.quantum       = 1024
    #default.clock.min-quantum   = 32
    #default.clock.max-quantum   = 2048
....

理由は,私が所有している Airplay 再生機器が 44,100 Hz/16bit のみにしか対応していないことです.なお,Airplay 2 対応機器の場合は 48,000Hz/24bit にも対応しているようなので,この場合は 44,100 ではなく Pipewire のデフォルトの値のままでも良いかもしれません(音源のサンプリングレートやビット深度の問題もあるので一概には言えないと思います).

次に Pipewire の loopback module の設定を行うための実行ファイル on.AirPlayTransmitter を以下の内容で作成し,実行権限を付けます(前々々節で取りあげた ROAP Discover モジュールで見つかった Airplay 再生機器 raop_sink.iPhone4sw.local.192.168.1.100.5000 の場合の設定です.IPアドレスが変わる可能性を考慮した形にしています).

$ sudo vi /usr/local/bin/on.AirPlayTransmitter

#! /bin/bash

while [ "$SOURCE_NAME" = "" -o "$SINK_NAME" = "" ]; do
  SOURCE_NAME=`pactl list sources|grep -n "Name: alsa_input.platform"|cut -d":" -f3|sed 's/ //g'`
  SINK_NAME=`pactl list sinks|grep -n "Name: raop_sink.iPhone4sw"|cut -d":" -f3|sed 's/ //g'`
done

pactl load-module module-loopback source=$SOURCE_NAME sink=$SINK_NAME

$ sudo chmod +x /usr/local/bin/on.AirPlayTransmitter

また,この実行ファイルがユーザー権限で1度だけ実行されるよう systemctl edit を用いて設定します.これは以下のように行います.

$ systemctl --user edit pipewire-pulse.service
### Editing /home/pi/.config/systemd/user/pipewire-pulse.service.d/override.conf
### Anything between here and the comment below will become the contents of the drop-in file

[Service]
ExecStartPost=/usr/local/bin/on.AirPlayTransmitter

### Edits below this comment will be discarded
......

自動ログインの設定

最後にユーザー pi で自動ログインする設定を行います.Pipewire サービスがユーザー権限で動作されることが理由です.

この自動ログインの設定は,前々節と同様に行うことももちろんできますが,raspi-config を使ってやる方が簡単です.実際の手順については,以下のページが分かりやすいと思います.

Raspberry Piで自動ログイン(auto-login)を設定する
sudorasipi-configで設定画面に入ります。1SystemOptionsより、S5Boot/AutoLoginを選択。B2ConsoleAutologinを選択。Escよりraspi-configを出てsudorebootにて再

利用方法と使用雑感

以下の写真のように USB で PC につなぐだけで使えます.

Photo by hyt.

PCから見ると,UAC2 ドライバに対応した USB Audio Interface として見えますので,接続後しばらく待ってから音声の出力先を適切に選べば,設定した Airplay 再生機器から音声が出力されるはずです.

実際に試したところ,Windows や Linux だけでなく macOS, iPad OS, iOS でも使えました(もちろんこれらの機器ははじめから Airplay に対応しているので実用上の意味はありません).また,Android や ChromeOS でも使えるようです.

また,本トランスミッターの設定の変更が必要なら,Linux の場合は /dev/ttyACM0,Windows の場合は COM* 経由でシリアル接続すれば Raspberry pi OS のシェルにログインできます.

使い勝手ですが,挿すだけで使えるので,結構実用的です.私の場合は,Windows を自宅で使うときに利用する USB Dock に本アダプターを挿しっぱなしにしていて,使いたいときはパッと Audio の出力先を切り替えて使うことが多いです.もちろん,アダプターが起動してくるまでしばらく待たないといけない(Raspberry pi zero 2W を使えばかなり改善されますし,Raspberry pi Zero って低消費電力なので常時起動でも大した電気代にはなりません)ことや,Youtube とかで使うと映像にかなり遅れて音声が出力されるなどの問題はありますが(Airplay のバッファを調整することができる機器ならかなりましにはなる),手軽に使えるのでそこまで気にはなっていません.

なお,本アダプター,この blog を定期的にみておられる方ならお気付きの方もおられると思いますが,以前の記事で作成した USB to Bluetooth Audioトランスミッターの進化形です.

Raspberry pi zero を USB to Bluetooth トランスミッターにする備忘録
Raspberry pi zero を USB to Bluetooth トランスミッターにする備忘録です. 前回記事で,Raspberry pi zero の Raspbian を LDAC,apt-X,AAC などの高音質なコーデックに...

この USB to Bluetooth Audio トランスミッターの場合はほとんど実用にはならなかったのですが,今回は結構実用的ですし,設定方法も以前よりは改善されていると思います.また,USB to Bluetooth Audio トランスミッターなら,より使い勝手のよい製品がいろいろ市販されていますが,今回は類似の製品がほとんど見つかりません(唯一 WiiM Pro が Airplay 送信機能に対応しているらしい).

WiiM Pro and Plus | Versatile Audiophile Streaming: Unleash the Brilliance
Unveil the brilliance of your music with WiiM Pro and Plus. Embrace versatile audiophile streaming that redefines your l...
https://amzn.to/3RfGVKx

WiiM Pro って決してお安くはありませんので,このためだけに購入はためらわれると思います.本 USB to Airplay トランスミッターなら全部合わせても4000円程度,失敗しても Raspberry pi zero WH を別用途に使えるので,まぁ悪くはないのではないかと思います.もちろん,類似製品がほとんどないのは,需要が全くないからかもしれないことは心に棚を作って考えないことにしているんですけどね!

以上!

 

AppleGadget
スポンサーリンク
Following hyt!
タイトルとURLをコピーしました