PC+Bluetoothヘッドフォンで高音質で音楽を聴く

# 背景

[Bose QuietComfort 35 II](https://www.bose.com/en_us/products/headphones/over_ear_headphones/quietcomfort-35-wireless-ii.html) (以下QC35)を入手したのだが、Windows PCはAACコーデックをサポートしておらず残念な音質になってしまうことがわかった。ここでへこたれず、PC上の音楽をBluetoothヘッドフォンのサポートする最高のコーデックで聴けるようにしてみよう。

詳しい背景はこちらを参照。
https://tomoto335.hatenablog.com/entry/bose-qc35-ii-aac

# 方針

マリー・アントワネットの「WindowsがダメならLinuxを使えばいいじゃない」の言葉に習う。LinuxはAACその他のコーデックを使えるとの噂があるので、Windows上にLinuxのVMを作り、そいつにBluetoothを叩かせて音楽再生専用VMにしてみる。ちょっと贅沢だがCPUとメモリが余っていれば問題ないはずである。

# 手順

## LinuxのVMを作る

私はVMware Workstation 15 Playerを使っているので以下それを前提とする。

また私は(特に強い理由なく)Ubuntu派なので、現時点の最新のUbuntu 20.04を使ってみる(18.04以降であればOKらしい)。ホストをあまり圧迫しないスペックでVMを作ってUbuntuをインストールし、以下の設定を行う。

* Windows上にある音楽データを参照できるように共有フォルダを有効化し、/etc/fstab にいつもの設定を追加する([参照](https://kb.vmware.com/s/article/60262)):

```
vmhgfs-fuse /mnt/hgfs fuse defaults,allow_other 0 0
```

* 追加の音声コーデック(特許やライセンスの都合でOSにバンドルできないもの)をインストールする:

```
sudo apt install ubuntu-restricted-extras
```

## Bluetoothヘッドフォンの接続設定を行う

ひとつ大事なことは、「BluetoothドングルをLinux側に直接接続し占有させる必要がある」ということである。Bluetoothをゲスト側とホスト側で共有する設定にしてしまうと、Bluetoothの処理がホスト側、つまりWindows任せになりコーデックの問題が解決しない。

ということは、ドングルが1つしかなければ高音質で音楽を聴いている間はWindows側はBluetoothを使えない。「ドングルを2つ挿せばいいじゃない」とも言えるのだが、そうするとVMが起動していない間はWindowsにBluetoothドングルが2つ見えることになり、それはそれで扱いが面倒になる。幸い私はWindows側に常時接続が必要なBluetoothデバイスを持っていないし、実験はシンプルな方がよいので、1つのドングルを2つのOSで切り替えて使う方式で行く。

また、一般に1つのドングルで2つのOSから同じデバイスを同時にペアリングすることはできない(2つ目のOSでペアリングすると1つ目のペアは無効になってしまう)。さらにQC35に至っては「1つ目のペアが有効な間は2つ目のOSでペアリングすらできない」ようなので(確証はないがそのような挙動に見えた)、Windows側のペアリングをあらかじめ解除しておくことを前提とする。なおこの問題の回避策は存在するのだが、難しいので後述することにする。

以上を踏まえて接続手順は次の通り。

* VM設定内のUSB設定から「Bluetoothデバイスの共有」オプションをオフにし、その後「取り外し可能デバイス」からBluetoothドングルを探してLinux側に接続する
    * この時点でWindows側ではBluetoothが無効になる

* BLEを(一時的に)オフにする
    * 参照: https://askubuntu.com/questions/833322/pair-bose-quietcomfort-35-with-ubuntu-over-bluetooth
    * QC35はBLEが有効だとペアリング時に問題が起こるので、その回避策。ペアリング後は戻してよい。
    * /etc/bluetooth/main.conf に `ControllerMode = bredr` を書き足し、Bluetoothサービスを再起動(`sudo service bluetooth restart`)する。

* ペアリングする
    * GUIからでも可能だが、私はうまくいかないことが多くてコマンドライン(`bluetoothctl`)を併用している。([参照](https://bbs.archlinux.org/viewtopic.php?id=197469))
    * ここでWindows側とペアリング済み、つまりデバイスがドングルのアドレスを既に知っているとデバイスがdiscoveryされないように見える。

* 接続をテストする
    * Bluetooth設定画面あるいは `bluetoothctl` から接続する。ヘッドフォンが "<ホスト名> connected" としゃべれば成功。
    * Sound設定画面のOutput Deviceからヘッドフォンを選べるはず。
    * その下のConfigurationからHigh Fidelity Playback (A2DP)を選べるはず。
    * Rhythmboxなどで音楽を再生してヘッドフォンから音が出ることを確認する。
    * 注意: この時点ではまだAACコーデックにはなっておらず、音質はWindowsと同等(のはず)。

## 拡張PulseAutioモジュールをインストールする

下記のページに従いpulseaudioの拡張モジュールをインストールする。
https://github.com/EHfive/pulseaudio-modules-bt/wiki/Packages#ppaberglhpulseaudio-a2dp

```
sudo add-apt-repository ppa:berglh/pulseaudio-a2dp
sudo apt update
sudo apt install pulseaudio # 念の為アップデート
sudo apt install pulseaudio-modules-bt libldac

pulseaudio -k
```

この後ヘッドフォンの再接続などを行うと、Sound設定からAAC等の高音質コーデックを選べるようになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/465152/86bf5a3e-cd0f-4463-2c0f-ac9f4f8a7bf1.png)

なお、ネットを調べて回ると、PulseAudioの設定で音質を上げるTIPSがあるようだ([参照](https://www.reddit.com/r/linuxmasterrace/comments/7l95yk/psa_change_default_shitty_pulseaudio_settings_for/))。あまり細かいところは追っていないが、 /etc/pulse/daemon.conf の `resample-method: speex-float-1` を変えるというのは定番らしい。試しに `speex-float-7` にしてみたら確かにクリアになった気がする。

# 評価

* これでPCからQC35をAAC接続で使えるようになり、AACをサポートしたスマフォと同等の、満足いく音質が得られるようになった。
* Linuxでの音楽再生中、やはりときどき音が途切れることがある。優先度の高い・負荷が競合する処理が起こってしまうことがあるのだろう。
* 高音質で音楽を聴くためだけにVM起動やBluetoothドングルの接続先変更等が必要というのは、一般には決してバランスのいいソリューションではないと思うが、私的には新しい技術を使うための手間としてはまあこんなものかなと思う。

# 上級編: Windows/Linux両方からBluetoothヘッドフォンをペアリングする方法

先に「1つのドングルで2つのOSから同じデバイスを同時にペアリングすることはできない(前のペアは無効にされてしまう)」「QC35に至っては1つ目のペアが有効な間は2つ目のOSでペアリングすらできないように見える」という問題を提示したが、この問題には解決策がある。つまり、Linux VMを立ち上げずにWindows側で同じBluetoothヘッドフォンを使うこともできる(ただしSBC音質になる)という設定が可能である。ただし少々めんどくさい。

基本的にはこの人が言っているやり方のバリエーションであるが、
https://askubuntu.com/questions/240577/ubuntu-12-10-and-windows-8-dual-boot-bluetooth-pairing/1086255#1086255
次のように少々玄人志向である。

* 一時的にBluetoothドングルがもう一つ必要
* Windows上でSYSTEMユーザーしか見られないレジストリキーを見る必要がある
* Linux上で自動的に作られたBluetoothペア設定ファイルを改竄する必要がある

難しいので手順を説明する。

* 本番用のBluetoothドングルを差して、Windowsでペアリングする。
* Windows上で "link key" なるものを取得する。これはペアリング時にペアに対して付与される暗号鍵のようなものであり、レジストリに記録されているが、SYSTEMユーザーにしか見られない。これを「盗み見る」ために、一時的にサービスを作って叩くという強硬手段を用いる。管理者権限でコマンドプロンプトを開き、次を実行する。

```
sc create cmdsvc binpath= "REG EXPORT HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\services\BTHPORT\Parameters\Keys c:\keys.reg" type= own
sc start cmdsvc
```

* `[SC] StartService FAILED 1053: (...)` というエラーが出るが、それで正常なので無視する。c:\keys.reg が作られていたら成功である。成功したらこのサービスは `sc delete cmdsvc` を実行して削除しておく。
* c:\keys.regから目的のペアの link key を見つけ出す。

```
[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\services\BTHPORT\Parameters\Keys\<本番用ドングルのアドレス>]
"<デバイスのアドレス>"=hex:xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx
```

* サブのBluetoothドングルに差し換えてLinuxを起動し、Linuxでペアリングする。
* /var/lib/bluetooth/<サブドングルのアドレス>/<デバイスのアドレス> というディレクトリができているはずなので、これを /var/lib/bluetooth/<本番用ドングルのアドレス>/ の下にコピーする。
* その下にある info というファイルに `[LinkKey]` というエントリーがあるはずなので、それをWindowsから取得した link key で置き換える。カンマは抜く。

```
sudo vi /var/lib/bluetooth/xx\:xx\:xx\:xx\:xx\:xx/yy\:yy\:yy\:yy\:yy\:yy/info 

```

```
[LinkKey]
Key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

* サブのBluetoothドングルはこの時点で不要になる。/var/lib/bluetooth/<サブドングルのアドレス> はディレクトリごと消してよく、またヘッドフォン側のプロファイルも削除してよい。
* ドングルを本番用に戻す。

これで本番用ドングルをLinuxに接続すれば、あたかも最初からそれでペアリングされたかのように動作するはずである。

# おまけ

* Rhythmboxのライブラリをクリアする方法: https://askubuntu.com/questions/56328/how-does-one-clear-the-rhythmbox-music-library-database