Raspberry pi zero を USB to Bluetooth トランスミッターにする備忘録

Gadget
Photo by hyt.

Raspberry pi zero を USB to Bluetooth トランスミッターにする備忘録です.

前回記事で,Raspberry pi zero の Raspbian を LDAC,apt-X,AAC などの高音質なコーデックに対応させることができましたので,さらに,これを USB-DAC としてある程度実用になるように設定してみた備忘録です.

現在のところ仕様は以下の通りです.ただし,いまのところ Android と Linux,MacOS (多分 iPadOS も動くと思う)だときちんと動きますが,Windows 10 ではうまく動きません.

仕様概要

上記の通りベースとなるのは Raspbian (Bluster) です.

以下のように接続することで USB Audio デバイスとして認識され,Raspbian に接続された A2DP 接続のスピーカー等に音が流れます.

Photo by hyt.

対応コーデックは,LDAC, apt-X-HD, apt-X, AAC, SBC です.また,これは多分ですがハイレゾも対応してます(詳細は以下の設定詳細をご覧ください).ただし,DSD なんかには対応してませんし,どうやったら対応できるのかも全く分かりません.

なお,いまのところ WEB インターフェース等には対応しておらず,Bluetooth スピーカー等の登録は ssh を利用して行わなければならない漢仕様です.

動作の概要

Raspberry pi zero WH のコンパクトさを損ねないよう,上の写真の通り USB 接続するだけで使えるよう一応工夫しています.動作は以下の通り遷移します.

PC 等にUSB接続され電源 ON になると,まず,一定の時間(回数),登録された Bluetooth 接続スピーカー等への接続が試みられます.

この後,動作は2つに分岐します.まず,事前登録したスピーカーに接続できた場合です.

この場合,自動的に USB Audio Device として PC 等から Raspberry pi zero が認識され,スピーカーとの接続が終了すると,自動的に電源 OFF となります.

逆に,事前に登録されたスピーカーに(一定時間内に)接続できなかった場合は,Raspberry pi zero WH は USB Ethernet デバイスとして PC 等から認識され,さらに,この Ethenet デバイス経由で ssh 接続できる状態となります.つまり,新たな Bluetooth 接続スピーカー等を登録したい場合はこの状態で行います.接続端末が Linux (Ubuntu) の場合は以下のようにすれば接続できます.

まず,Raspberry pi zero WH を USB 接続した端末で以下のように USB Ethernet を有効にします.

$ sudo nmcli c add type ethernet con-name rbpz1-usb ifname enp0s20u2
$ sudo nmcli c modify rbpz1-usb ipv4.method shared
$ sudo nmcli c up rbpz1-usb

少し待ってから,

$ sudo arp-scan -I enp0s20u2 -l
Interface: enp0s20u2, datalink type: EN10MB (Ethernet)
Starting arp-scan 1.9 with 256 hosts (http://www.nta-monitor.com/tools/arp-scan/)
10.42.0.211     aa:aa:aa:aa:aa:aa       (Unknown)

1 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9: 256 hosts scanned in 2.423 seconds (105.65 hosts/sec). 1 responded

のようにすれば,IP アドレスが分かります(arp-scan が入ってない場合は apt install arp-scan のような感じで入れて下さい).あとは ssh で実際に接続してやれば良いだけです.

$ ssh -l pi 10.42.0.211

なお,Raspbian に適当なプライベートアドレスを事前に振って置いた方が楽だったかもしれませんが,そんな頻繁に Bluetooth スピーカー等を追加することなどないので,今のところはこれで良いかなと思っています.

設定の詳細(Raspbian の導入とBluetooth スピーカーの登録)

難しいことはしていません.Linux の USB-OTG 機能を利用してやっているだけです.また,適当に組んだ(泥縄式に組んだ)動けば良い,という程度のものなので,エラー処理等全くしていません.

まず,Raspberry pi zero に LDAC 等に対応した Raspbian を導入する必要がありますが,これについては以下の記事をご覧ください.

Raspbian を LDAC と AAC に対応させる備忘録
Raspbian を LDAC と AAC に対応させる備忘録です. はじめに Raspbian on Raspberry pi zero WH を LDAC, apt-X HD, apt-X, AAC などの高音質なコーデックに対応させて...

次に Bluetooth スピーカーを Raspbian に登録しますが,これは bluetoothctl コマンドで以下のように行なっておきます(Sony SRS-HG10 の例です).

$ bluetoothctl
Agent registered
[bluetooth]# scan on
Discovery started
...
[NEW] Device AA:AA:AA:AA:AA:AA hear go 2
[bluetooth]# pair AA:AA:AA:AA:AA:AA
Attempting to pair with AA:AA:AA:AA:AA:AA
...
[CHG] Device E0:37:BF:21:88:A1 ServicesResolved: yes
[CHG] Device E0:37:BF:21:88:A1 Paired: yes
Pairing successful
[hear go 2]# trust AA:AA:AA:AA:AA:AA
[CHG] Device AA:AA:AA:AA:AA:AA Trusted: yes
Changing AA:AA:AA:AA:AA:AA succeeded
...
[hear go 2]# exit

また,確実に接続ができるよう,次の接続用シェルスクリプトを用意しておきます(複数機器ある場合は機器ごとに準備しておきます.また,実際に接続できるかどうか試しておきます.

$ sudo vi /usr/local/bin/c.SRS-HG10
#!/bin/sh
bluetoothctl << EOL
power on
connect E0:37:BF:21:88:A1
quit
EOF

$ sudo chmod +x /usr/local/bin/c.SRS-HG10
$ c.SRS-HG10

設定の詳細(USB-DAC 機能の有効化)

次は USB-DAC 機能を有効化します.これは次のスクリプトで行います.

$ sudo vi /usr/local/bin/on.usb_dac
#!/bin/bash
sudo modprobe -r g_audio

ASINK_LN=`pacmd list-sinks|grep -n "* index"|cut -d":" -f1`
SINK_NAME=`pacmd list-sinks|head -n $(($ASINK_LN +1))|tail -n 1|awk -F'[<>]' '{print $2}'`
SINK_DP=`pacmd list-sinks|tail -n +$ASINK_LN|grep "sample spec"|head -n 1|awk -F'[^0-9]+' '{print $2}'`
SINK_SR=`pacmd list-sinks|tail -n +$ASINK_LN|grep "sample spec"|head -n 1|awk -F'[^0-9]+' '{print $4}'`

sudo modprobe g_audio c_srate=$SINK_SR c_ssize=$(($SINK_DP /8)) p_srate=$SINK_SR p_ssize=$(($SINK_DP /8))
sleep 3

ASOURCE_LN=`pacmd list-sources|grep -n "* index"|cut -d":" -f1`
SOURCE_NAME=`pacmd list-sources|head -n $(($ASOURCE_LN +1))|tail -n 1|awk -F'[<>]' '{print $2}'`

echo "SINK NAME:" $SINK_NAME
echo "SINK AUDIO DEPTH:" $SINK_DP
echo "SINK SAMPLING RATE:" $SINK_SR
echo "SOURCE NAME:" $SOURCE_NAME

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

念の為,それぞれの変数は以下の通りです.

  • SINK_NAME: Bluetooth 接続されたスピーカーの pulseaudio 名
  • SINK_DP: Bluetooth 接続されたスピーカーのビット深度
  • SINK_SR: Bluetooth 接続されたスピーカーのサンプリングレート
  • SOURCE_NAME: USB 入力の pulseaudio 名

中段の sudo modprobe g_audio … が USB-OTG 機能を用いて USB audio device となるよう設定している部分ですが,その際,Bluetooth 接続されたスピーカーから取得したビット深度とサンプリングレートと同じものを USB audio device に適用しています.つまり,Bluetooth 接続されたスピーカーのサンプリングレートが 96000Hz なら,同じサンプリングレート 96000Hz の USB Audio Device となるようにしています.また,取り込んだデータをなるべくそのままの形で Bluetooth スピーカーに送るよう(ダメな場合でもなるべく高音質に再サンプリングされるよう),/etc/pulse/daemon.conf を以下の通り設定します.

$ sudo vi /etc/pulse/daemon.conf
...
resample-method = soxr-vhq
avoid-resampling = yes
...

設定の詳細(待受部分)

Bluetooth スピーカーの接続の待受は次のスクリプトで行います.なお,以下のスクリプトは,手持ちの Bluetooth スピーカーとイヤフォンの SRS-HG10 と WF-1000XM3 に接続を試みるものとなっています.

$ sudo vi /usr/local/bin/ss
#!/bin/sh

CN=0

while true; do
  /usr/local/bin/c.SRS-HG10
  sleep 2
  /usr/local/bin/c.WF-1000XM3
  sleep 2
  CF=`pacmd list-sinks|grep a2dp.sink`
  if [ -n "$CF" ]; then
    break
  elif [ $CN -ge 33 ]; then
    sudo modprobe g_ether
    exit 0
  fi
  CN=`expr $CN + 1`
done

/usr/local/bin/on.usb_dac

while true; do
  CF=`pacmd list-sinks|grep a2dp.sink`
  if [ -z "$CF" ]; then
    sudo poweroff
    break
  fi
  sleep 15
done

また,このスクリプトが自動起動されるよう,次の2つの設定を行います.

まず,ユーザー pi で自動ログインされるように設定しますが,これは,

$ sudo raspi-config

として,3. Boot Options ⇒ B1. Desktop / CLI ⇒ B2. Console Autologin の順に選択することで行なえます.

次に,~/.bashrc に次の設定を付け加えます.これは,ページ

bash/zsh SSHでログインした時だけ実行する - Qiita
Ubuntuはログインしたときこんな情報が表示されてちょっといいなと思った。Welcome to Ubuntu 14.04.3 LTS (GNU/Linux 3.13.0-48-generic x…

を参考にさせて頂きました.

$ vi ~/.bashrc
...
if [ -f /proc/$PPID/cmdline ]; then
  if [ "$(command cut -d : -f1 < "/proc/$PPID/cmdline")" != "sshd" ] && [[ $- == *i* ]]; then
    /usr/local/bin/ss 
  fi
fi

設定はこれで全てです.あとは,再起動して,実際に動作を確認してみてください.

実用性について

当たり前ですが,同様の機能を持つ市販の製品,例えば,

GENKI Bluetooth を Volumio2 で使ってみた備忘録
GENKI Bluetooth を Volumio2 で使ってみた備忘録です. 前置き Volumio2 を使う動機ってのは,安価かつ手軽に音の良い音楽視聴環境を作れるってとこだと思います.しかし,Bluetooth 接続のスピーカー等は例...

などよりも利便性はないです.特に,Raspbian の起動にある程度時間がかかるので,実際に使えるようになるのに少し時間がかかるのが問題です.また,Windows 10 で動かないのが痛いです.しかし,これさえ我慢できるのなら,実用性はそれなりだと思います.

と言うのも,本アダプターの場合,LDAC, apt-X HD, apt-X, AAC, SBC に対応していますが,ある程度安価な製品でここまで多くのコーデックに対応しているものはいまのところありません.また,いまのところ,たまに最初の接続に失敗することはあるのですが,正常に接続されればその後は非常に安定して動作します.

なお,コーデックやサンプリングレートによって CPU の利用率はかなり変化します.

例えば,LDAC と AAC だと LDAC の方が10%ほど CPU の使用率が高いですし,サンプリングレートも高い方がより CPU の使用率が上がります.しかし,今のところ最も高くても60%台で,CPU の能力不足で音が途切れたりしたことはありません.

これ,Web インターフェースを付け加えたり,小さな液晶画面を追加したりすればもう少し実用度は上がると思いますが,この辺りについては気が向いたらまたボチボチと対応できたらなぁ……と思っています.


2023年10月1日追記:Linux USB Gadget UAC2 ですが,現在は Windows にも対応しているっぽいです.


なお,Windows 10 の UAC2 Audio Device のドライバーでは動かないというのは既知の問題のようで,最新のカーネルだと解決されてるんだかされてないんだかよくわからない状態のようです.

UAC2 gadget not recognized on Windows 10. · Issue #24 · linux-usb-gadgets/libusbgx
Using gadget-import (initial config retrieved via gadget-uac2 + gadget-export) to initialize a UAC2 audio device, but it...

UAC1 Audio Device として認識するよう設定すれば,ドライバは正常に動作するようですが,音が途切れ途切れだったり,音程がおかしいなど,現状,とても実用的とは言えない状態です.この辺りは,まだ時間がかかりそうだなぁ……と思っています.

以上!

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