Raspberry Pi Zero W 経由で PC の USB Audio 出力を Bluetooth Audio に変換する

# やりたかったこと
USB Audio に対応しているマシンからの出力を Bluetooth ヘッドフォンで受けたかったが、そんなハードが見つからなかった。
手元に Raspberry Pi Zero W があったので、やってみた。

## 結果
USB Audio → Bluetooth ができることは確認した。自動化まではまだ。音質は時折不安定な気がしている。

# 手順1: Pi Zero W を PC から USB Audio デバイスとして認識させる
- [[メモ] Raspberry Pi ZeroをUSBケーブル一本でインターネット接続！(キーボード・マウス・モニター必要なし)](https://qiita.com/mt08/items/ce5e3911d74d7fad4563)
- [Simple guide for setting up OTG modes on the Raspberry Pi Zero](https://gist.github.com/gbaman/975e2db164b3ca2b51ae11e45e8fd40a)

## OTG と USB Gadget
Raspberry Pi Zero と Zero W の microUSB 端子は USB On-The-Go(OTG) 対応であり、OTG ケーブルを使って Pi Zero を USB ホストとして振る舞わせることが普通であるが、PC と USB ケーブルで接続すると USB デバイスとしても振る舞うことができる。
Linux 的には USB Gadget と呼ばれるフレームワークが機能を提供している。

なお、Pi Zero の電源側ではないほうの micro USB 端子で PC と接続する必要があることに注意。電源側でないほうでも、 Pi Zero への電源供給は可能。普通の USB ケーブルで接続すればOK。

上記リンク先の設定手順が2017年現在の最新の一番手軽な手順で、以下の通り。

1. /boot/config.txt に dtoverlay=dwc2 の行を足す
2. /boot/cmdline.txt の rootwait の後に modules-load=dwc2,<読み込みたい USB Gadget モジュール> を足す

※/boot は PC から microSD カード上のフォルダとして書き込みが可能な特殊な領域

## 余談：イーサネットモジュール(g_ether)について
g_ether を使って、USBケーブル１本で Zero や Zero W に ssh で繋ぎたい場合は、以下のハマりポイントがあるので注意。詳細は上記リンク先参照。

- ssh が標準では開かなくなったので、初回起動から ssh を使いたい場合は /boot/ssh という空ファイルを作っておく
- Windows 10 はシリアルデバイスとして g_ether を認識してしまうので、適当な RNDIS ドライバを適用し直す必要がある
- Windows は Bonjour(mDNS) でのクライアントアプリからの名前引きに標準では対応していない。
    - raspberrypi.local というホスト名で繋ぎたい場合、Bonjour Print Services などをインストールする必要がある
    - Windows 10 の mDNS サポートは情報が錯綜気味だが、少なくとも Fall Creators Update 時点では Bonjour の別途インストールが必要そう
    - なお、Bash on Windows 上で bonjour 対応するには avahi-daemon を install する必要があるが、 https://github.com/Microsoft/WSL/issues/384 の通り、現在は動かない。 Windows Insider Preview build (1709 build 16288.1) で直っている模様。

## USB Audio の設定
USB Gadget として g_ether の代わりに g_audio を指定するだけ。
これは排他である。うっかり g_ether と g_audio を両方指定すると、 udc-core: couldn't find an available UDC などと言われて怒られる。

### 余談： USB Gadget のさらに詳細な設定方法
なお、g_ほげほげ は USB Gadget としてはレガシーな手法で、configfs を使った設定がモダンであるが、Vendor ID などを自前で考えて設定しないといけないのはどう考えてもたいへん。
参考: https://events.linuxfoundation.org/sites/events/files/slides/LinuxConNA2013-andrzej.pietrasiewicz-usb-gadget-configfs_0.pdf

configfs を使えば、複数の機能を持った USB Gadget として Pi Zero/Zero W を振る舞わせることもできる。が、Windows でちゃんと認識されるようにするには、色々細かい注意が必要。
参考: [Raspberry Pi Zero as Multiple USB Gadgets](http://irq5.io/2016/12/22/raspberry-pi-zero-as-multiple-usb-gadgets/)

単に g_ether,g_serial,g_mass_storage を一緒に使いたいだけなら g_multi という手もある。

# 手順2: Pi Zero W に Bluetooth スピーカー / ヘッドセットを繋ぐ
[Raspberry piでbluetoothスピーカーを鳴らす](http://zokibayashi.hatenablog.com/entry/2017/10/10/020902)がもっとも正しそうな手順。
[Raspberry Pi Zero WでBluetoothスピーカーを鳴らす](https://qiita.com/mnishz/items/8d662f904df814b9e474) にも有用な情報あり。

手順が多いので、転記はしません。

## ハマりポイントのメモ
* root と pi をそれぞれ pulse-access グループに突っ込もう。
    * group 追加はそのままだと current session に影響しないため、グループ追加作業後、reboot するのが安心。
    * pactl list して何かおかしなことが起こったら多分この問題。
        * access denied と言われるケース（rootで発生）
        * 無言で Card #1 (bluez_card) が見つからないケース（piで発生）
    * sudo -s -u pulse で pulse ユーザに切り替えて pactl すると、この権限周りの問題切り分けできる。
        * 他のユーザを pulse-access グループに入れるのは、pulse ユーザ以外でもアクセスできるようにするため。
    * 適切に設定すれば、pi ユーザのままで paplay できるようになります。
* ヘッドセットプロファイルもサポートしているスピーカーで A2DP に切り替える方法
    * pactl set-card-profile 1 a2dp とよく書かれています
        * Failure: No such entity と怒られました
    * pactl set-card-profile 1 a2dp_sink が正しい。
        * pactl list で出てくる Profile の名前で指定すべし。
    * どなたか、再起動後もデフォルトで a2dp_sink に設定する方法をご存じだったら教えてください。
* sudo systemctl enable pulseaudio.service しないと、再起動後に pulseaudio が起動しないので注意。
* pulseaudio の system-wide モードは色々とセキュリティ的には望ましくないので、リスクを理解した上でご利用ください。
    * Raspberry PI でシングルユーザで利用している場合は大丈夫だと思いますが。
    * ログの警告にしたがってうっかり pulseaudio の起動パラメータに --disallow-module-loading を追加すると bluetooth 関連モジュールが動かなくなるので注意。
* aplay ではなく、paplay を使うべし、という話が一番役に立ちました。
    * 数回だけは動く、というのが一番困りますよね。。。

# 手順3: USB Audio の入力を Bluetooth Audio の出力に接続する
- [PulseAudio FAQ: How do I connect an input source to an output sink?](https://www.freedesktop.org/wiki/Software/PulseAudio/FAQ/#index39h3)

以下のスクリプトを Zero W 上で叩くことで、Mac の USB Audio の音声出力が Zero W 経由で bluetooth の a2dp で出力されたことは確認しました。

```bash
#!/bin/bash
pactl set-card-profile `pactl list | grep 'Name: bluez_card' | perl -na -e 'print $F[1]'` a2dp_sink
pactl load-module module-loopback source="alsa_input.platform-snd_uac2.0.analog-stereo" sink="`pactl list | grep 'Name: bluez_sink' | grep -v 'monitor' | perl -na -e 'print $F[1]'`"
```

Bluetooth 接続されたことをトリガーにこれを起動できれば、PC に繋ぐだけで USB Audio を bluetooth に転送するガジェットのできあがりですが、そこまではやっていません。