Raspberry Pi をA2DPのsinkにして携帯やタブレットから音楽を再生する

以前の記事で、Raspberry Piをリモートスピーカーとして使用するためにpulseaudioを使った方法を紹介しました。
そこでも少し触れましたが、BluetoothA2DPを使用すればスマートフォン等多くのクライアントから手軽に接続できるようになります。
というわけでRaspberry PiA2DP sinkとしてセットアップしてBTスピーカーにしてみたので紹介します。
またこちらの記事のように出力はデジタルでステレオに出しているので、スマートフォンからステレオをコントロールしている感覚でなかなか悪くないです。

(参考にしたサイト)
http://www.instructables.com/id/Turn-your-Raspberry-Pi-into-a-Portable-Bluetooth-A/

準備

USBのBluetoothアダプタをRaspberry Piに接続します。
なんでもいいと思いますが、買ったのはこれです。

iBUFFALO Bluetooth USBアダプター 3.0+EDR対応 class2 ブラック BSHSBD04BK

iBUFFALO Bluetooth USBアダプター 3.0+EDR対応 class2 ブラック BSHSBD04BK

また、必要なパッケージをインストールします。

% sudo apt-get install pulseaudio-module-bluetooth python-gobject python-gobject-2

pulseaudio設定

pulseaudioを使って、bluetoothで受信した音声をオーディオ出力にルーティングします。
ここでは、pulseaudioをシステムデーモンとして起動する方向で説明します。

pulseaudioを走らせるユーザー(pulse)をgroup lp に追加

% sudo usermod -a -G lp pulse

/etc/init.d/puleaudio 修正
pulseaudioの起動オプションから --disallow-load-module を削除する。
注:DISALLOW_MODULE_LOADING=1 を 0 に変えても効かないので、以下のように起動コマンドから--disallow-module-loading=$DISALLOW_MODULE_LOADINGを削除します。

(pulseaudio_start () 内)
        #start-stop-daemon -x $DAEMON -p $PIDFILE --start -- --system --disallow-exit --disallow-module-loading=$DISALLOW_MODULE_LOADING --daemonize --log-target=syslog --high-priority
        start-stop-daemon -x $DAEMON -p $PIDFILE --start -- --system --disallow-exit --daemonize --log-target=syslog --high-priority 

/etc/default/pulseaudio で以下の行を変更してシステムデーモンとして起動

PULSEAUDIO_SYSTEM_START=1

/etc/pulse/system.pa に以下を追加

.ifexists module-bluetooth-discover.so
load-module module-bluetooth-discover
.endif

リサンプル対応
/etc/pulse/daemon.conf に以下の行を追加

resample-method = trivial 

以上の設定で、pulseaudioのサービスを起動しておきます。(リブートでもOK)
pulseというユーザーでpulseaudioが起動していること確認。

% sudo service pulseaudio start
Starting system PulseAudio Daemon:.
% ps aux  | grep pulseaudio
pulse     3623  0.5  1.0 105980  4824 ?        S<l  14:43   0:01 /usr/bin/pulseaudio --system --disallow-exit --daemonize --log-target=syslog --high-priority

Bluetooth設定

/etc/bluetooth/audio.conf の [General]セクション直下に以下の行を追加します。
どれが何に必要なのかいまいち分かってませんが、例えばAndroidのクライアントのバージョンによってはこれがないとダメとかあるらしいんでとりあえず全部入りで。

Enable=Source,Sink,Headset,Gateway,Control,Socket,Media

必須ではありませんが、SDPで検索したときにコンピュータではなくPortrable audio deviceとして見せたければ以下の設定をします。
/etc/bluetooth/main.conf のClassを修正

Class 0x20041c

/var/lib/bluetooth//config も同様にclassを修正

class 0x20041c

次に、bluetoothの接続が行われたときに自動でpulseaudioの設定をして所望の出力から音を出すための設定をします。
まず /usr/lib/udev/bluetooth というスクリプトを以下の内容で作成し、実行パーミッションを付加します。
(オリジナルはここでしょうか?)
内容は、module-loopbackというpulseaudioのモジュールをロードし、bluezのデバイスから出力デバイスにルーティングしてやるというものです。
繰り返しますが、pulseaudioをシステムデーモンとしてpulseというユーザーで実行する前提です。
別ユーザーでpulseaudioを走らせる場合は、下記スクリプト内でpactlの実行ユーザーをpulseaudioを実行させるユーザーに変更してください。
また AUDIOSINK= の行は sudo -u pulse pactl list short sinks で得られるリストから適切なものを選んで設定してください。
ここでは前回紹介したUSBオーディオバイスに出力する設定です。

#!/bin/bash

#change if you don't use default analog audio out.
#AUDIOSINK="alsa_output.platform-bcm2835_AUD0.0.analog-stereo"
AUDIOSINK="alsa_output.usb-0d8c_USB_Sound_Device-00-Device.analog-stereo"

echo "Executing bluetooth script...|$ACTION|" >> /var/log/bluetooth_dev

ACTION=$(expr "$ACTION" : "\([a-zA-Z]\+\).*")
if [ "$ACTION" = "add" ]; then
   # set volume level here, if necessary
   # amixer set Master 100%
   # pacmd set-sink-volume 0 65537
   for dev in $(find /sys/devices/virtual/input/ -name input*); do
      if [ -f "$dev/name" ]; then
         mac=$(cat "$dev/name" | sed 's/:/_/g')
         bluez_dev=bluez_source.$mac

         sleep 1

         CONFIRM=`sudo -u pulse pactl list short | grep $bluez_dev`
         if [ ! -z "$CONFIRM" ]; then
            echo "Setting bluez_source to:  $bluez_dev" >> /var/log/bluetooth_dev
            echo pactl load-module module-loopback source=$bluez_dev sink=$AUDIOSINK rate=44100 adjust_time=0 >> /var/log/bluetooth_dev
            sudo -u pulse pactl load-module module-loopback source=$bluez_dev sink=$AUDIOSINK rate=44100 adjust_time=0 >> /var/log/bluetooth_dev
         fi
      fi
   done
fi

if [ "$ACTION" = "remove" ]; then
   CONFIRM=`sudo -u pulse pactl list short | grep 'module-loopback\ssource=bluez_source'`
   if [ ! -z "$CONFIRM" ]; then
      module_id=`echo $CONFIRM | sed 's/\s.*//'`
      echo pactl unload-module $module_id >> /var/log/bluetooth_dev
      sudo -u pulse pactl unload-module $module_id >> /var/log/bluetooth_dev
   fi
fi

そしてこのスクリプトbluetoothのコネクションが確立したときに自動で実行する設定を行います。
/etc/udev/rules.d/99-input.rules に以下の行を追加

KERNEL=="input[0-9]*", RUN+="/usr/lib/udev/bluetooth"

最後にbluetooth-agentを自動起動してコネクションを待ち受けるサービスを /etc/init.d に作成します。
/etc/init.d/bluetooth-agent (新規、パーミッション0755)

### BEGIN INIT INFO
# Provides: bluetooth-agent
# Required-Start: $remote_fs $syslog bluetooth pulseaudio
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Makes Bluetooth discoverable and connectable to 0000
# Description: Start Bluetooth-Agent at boot time.
### END INIT INFO
#! /bin/sh
# /etc/init.d/bluetooth-agent
USER=root
HOME=/root
export USER HOME
case "$1" in
        start)
                #echo "setting bluetooth discoverable"
                #sudo hciconfig hci0 piscan
                sudo hciconfig hci0 pscan
                start-stop-daemon -S -x /usr/bin/bluetooth-agent -b -- 0000
                echo "bluetooth-agent startet pw: 0000"
                ;;
        stop)
                echo "Stopping bluetooth-agent"
                start-stop-daemon -K -x /usr/bin/bluetooth-agent
                ;;
        *)
                echo "Usage: /etc/init.d/bluetooth-agent {start|stop}"
                exit 1
                ;;
esac
exit 0
% sudo update-rc.d bluetooth-agent defaults

ここで bluetooth サービスを再起動(パッケージインストールしてから一度もリブートしてなければ起動してないかもしれませんが)& bluetooth-agent サービスを起動します

% sudo service bluetooth restart
Stopping bluetooth: rfcomm /usr/sbin/bluetoothd.
Starting bluetooth: bluetoothd rfcomm.
% sudo service bluetooth-agent start
bluetooth-agent startet pw: 0000

接続

さていよいよ接続してみます。
ペアリングする時は、inquiry scanを有効にします。

$ sudo hciconfig hci0 piscan

この状態でスマートフォン等から検索すると発見できるはずなので、ペアリングします。

ペアリングできたらinquiry scanは無効にしておきます。

$ sudo hciconfig hci0 pscan

これでコネクトできているはずなので、携帯のオーディオプレイヤーなどで音楽を再生してみてください。Have Fun!

(補足)

参考までに、コネクションが確立している状態のpulseaudioのlistは以下のようになっています。
USBオーディオを出力にしているので全く同じにはならないかもしれませんが、ポイントはBluetoothのコネクションにより bluez_source. と module-bluetooth-device が存在することと、/usr/lib/udev/bluetooth スクリプトが起動されて module-loopback がロードされ、bluez_sourceから指定したsinkにルーティングされていることです。

% sudo -u pulse pactl list short
0       module-alsa-card        device_id="1" name="usb-0d8c_USB_Sound_Device-00-Device" card_name="alsa_card.usb-0d8c_USB_Sound_Device-00-Device" namereg_fail=false tsched=yes fixed_latency_range=no ignore_dB=no deferred_volume=yes card_properties="module-udev-detect.discovered=1"
1       module-alsa-card        device_id="0" name="platform-bcm2835_AUD0.0" card_name="alsa_card.platform-bcm2835_AUD0.0" namereg_fail=false tsched=yes fixed_latency_range=no ignore_dB=no deferred_volume=yes card_properties="module-udev-detect.discovered=1"
2       module-udev-detect
3       module-native-protocol-unix
4       module-stream-restore
5       module-device-restore
6       module-default-device-restore
7       module-dbus-protocol    access=local
8       module-rescue-streams
9       module-always-sink
10      module-suspend-on-idle
11      module-position-event-sounds
12      module-bluetooth-discover
18      module-bluetooth-device address="58:17:0C:55:C9:4E" path="/org/bluez/3969/hci0/dev_58_17_0C_55_C9_4E" profile="a2dp_source" auto_connect=no
19      module-loopback source=bluez_source.58_17_0C_55_C9_4E sink=alsa_output.usb-0d8c_USB_Sound_Device-00-Device.analog-stereo rate=44100 adjust_time=0
0       alsa_output.usb-0d8c_USB_Sound_Device-00-Device.analog-stereo   module-alsa-card.c      s16le 2ch 44100Hz       RUNNING
1       alsa_output.platform-bcm2835_AUD0.0.analog-stereo       module-alsa-card.c      s16le 2ch 44100Hz       SUSPENDED
0       alsa_output.usb-0d8c_USB_Sound_Device-00-Device.analog-stereo.monitor   module-alsa-card.c      s16le 2ch 44100Hz       IDLE
1       alsa_input.usb-0d8c_USB_Sound_Device-00-Device.analog-stereo    module-alsa-card.c      s16le 2ch 44100Hz       SUSPENDED
2       alsa_output.platform-bcm2835_AUD0.0.analog-stereo.monitor       module-alsa-card.c      s16le 2ch 44100Hz       SUSPENDED
6       bluez_source.58_17_0C_55_C9_4E  module-bluetooth-device.c       s16le 2ch 44100Hz       RUNNING
2       0       -       module-loopback.c       s16le 2ch 44100Hz
2       6       -       module-loopback.c       s16le 2ch 44100Hz
19      protocol-native.c       pactl
0       alsa_card.usb-0d8c_USB_Sound_Device-00-Device   module-alsa-card.c
1       alsa_card.platform-bcm2835_AUD0.0       module-alsa-card.c
5       bluez_card.58_17_0C_55_C9_4E    module-bluetooth-device.c

まとめ

以上でRaspberry PiBluetoothスピーカーとして仕立てることができました。

しかしBluetoothの設定はややこしく、またツールも貧弱でディストリビューションによって使える物があったりなかったりとなかなか難儀です。
ここでまとめたステップも、途中で思ったように動かない場合はこうすればよいという明確なインストラクションも書きづらいです。
一応クリアな状態から動作確認しているので大丈夫だと思いますが、もう少しなんとかならないものかと思ってしまいますね...