bluetooth_audio_switch: Bluetooth オーディオデバイスの接続管理用 CLI

bluetooth_audio_switch について

bluetooth_audio_switch は Bluetooth オーディオデバイス (ワイヤレスイヤホンなど) との接続を操作するための CLI プログラムです。

bluetooth_audio_switch のソースコードとバイナリは https://github.com/inzkyk/bluetooth_audio_switch で公開されています。

使い方

bluetooth_audio_switch のバイナリは GitHub のリリースページからダウンロードできます。

引数を付けずに bluetooth_audio_switch を実行すると、Bluetooth オーディオデバイスの一覧が現在の状態と共に出力されます:

# Windows が認識している Bluetooth オーディオデバイスを表示する
$ bluetooth_audio_switch
WF-1000XM5 Hands-Free AG Audio (connected)
WF-1000XM5 Stereo (connected)
WH-CH400 Hands-Free AG Audio (not connected)
WH-CH400 Stereo (not connected)

こうして取得した名前を引数に付けて bluetooth_audio_switch XXX を実行すると、Bluetooth オーディオデバイス XXX の接続状態が変更 (接続されているなら切断、切断されているなら接続) されます。XXX にはワイルドカードを使用でき、マッチした全てのデバイスが処理対象となります:

# 上記の実行結果が得られた状態で...
# XF-1000XM5 から切断する
$ bluetooth_audio_switch WF-1000XM5*
Successfully sent KSPROPERTY_ONESHOT_DISCONNECT signal to WF-1000XM5 Hands-Free AG Audio.
Successfully sent KSPROPERTY_ONESHOT_DISCONNECT signal to WF-1000XM5 Stereo.
# XF-1000XM5 に接続する
$ bluetooth_audio_switch WF-1000XM5*
Successfully sent KSPROPERTY_ONESHOT_RECONNECT signal to WF-1000XM5 Hands-Free AG Audio.
Successfully sent KSPROPERTY_ONESHOT_RECONNECT signal to WF-1000XM5 Stereo.

bluetooth_audio_switch connect XXX または bluetooth_audio_switch disconnect XXX を実行すると、それぞれデバイスの接続または切断が実行されます。最初から接続または切断されている場合は何もしません。

その他の話題

思い出話とか。

作った経緯

GUI で同じことをするのが面倒なので作りました。Windows でペアリング済みのワイヤレスイヤホンに接続するには、タスクトレイの Bluetooth アイコンをダブルクリックして、出現したウィンドウの中からデバイスを選んで「接続」のボタンをクリックする必要があります。これは Windows 10 では四クリック、Windows 11 では三クリックの操作であり、ウィンドウの出現と押すべきボタンの発見にも多少の時間がかかります。

参考にしたレポジトリ

Bluetooth オーディオデバイスとの接続を管理する処理の実装では m2jean/ToothTray を参考にしました。Google で「Windows Bluetooth headphone connect programmatically」などと検索しても欲しい情報は得られませんでしたが、このレポジトリを見れば使うべき API と使い方がバッチリ分かりました。

何の手掛かりもない状態で同じ API を見つけられる自信はありません。Windows の「設定」アプリを API Monitor で監視したりしてもよく分からず、AI に聞いても Bluetooth デバイスそのものを有効化・無効化する API を答えるだけでした。「Bluetooth オーディオデバイスとの接続・切断を実行する」という一言で表せる処理の API がここまで見つかりにくいのは意外でした。

Windows の低レベル API

IKsControl などの低レベル API は bluetooth_audio_switch で初めて使いました。使うからには理解しようと思ってドキュメントを読みましたが、ドライバ開発者向けのドキュメントが多くて完全に理解したとはとても思えません。そのため使い方が間違っていて、デバイスでメモリリークなどが起きたりする可能性があります。MIT ライセンスにある通り無保証なので、自己責任で使ってください。

環境よって反応が異なる

bluetooth_audio_switch XXX を実行すると、XXX にマッチする名前を持つデバイスに対して KSPROPERTY_ONESHOT_DISCONNECT または KSPROPERTY_ONESHOT_RECONNECT が指示されます。これらの指示に対する動作は環境によって異なるようです。ワイヤレスイヤホンの接続または切断は必ず実行されるのに対して、それを知らせる通知音がワイヤレスイヤホンで鳴る環境もあれば鳴らない環境もありました。Bluetooth ドライバの行儀の良さ (?) が関係しているのだと思います。接続または切断が実行されない環境があっても不思議ではありません。

ワイルドカード

処理対象のデバイスを選択する処理ではワイルドカードが利用できます。ワイルドカードを実装する中で、正規表現ではなくワイルドカードが使われることのある理由の一つを実感しました: ワイルドカードの方が簡単に実装できます (参考: ix の実装)。単純なバックトラックが必要なだけで、前処理も特になく受け取った二つの文字列を頭から走査していけば結果が分かります。

出力のソート

bluetooth_audio_switch を実行したときの出力はソートされます。この処理は、出力に似た文字列 (XXX Hands-FreeXXX Stereo など) が含まれることが多いために追加されました。この処理のためだけに ix_StringArena.hppix_sort.hpp といったファイルが必要になりますが、出力は見やすくなるので実装してよかったと思います。Unix 哲学に従うなら実装すべきでない?

とても短いファイルがある

今回初めて各ソースファイルの先頭に MIT ライセンスの全文を入れました。こうしたところ、ライセンスよりソースコードが短いソースファイルがいくつかあることに気が付きました (ix_min_max.hpp, ix_polyfill.hpp など)。さすがにダサいので ix.hpp にでも統合しようと思います。

amalgamator.py

amalgamator.py はプライベートのコードベースから Amalgamation Build 用の amalgam.cpp を生成する Python スクリプトです。通常のビルド用のソースツリーと CMakeLists.txt の作成なども行います。

amalgamator.py はもともと textunpack を公開するにあたって作成しました。急ごしらえのプログラムで一般性はあまり考えていなかったのですが、bluetooth_audio_switch でも大きな問題なく使えたので良かったです。bluetooth_audio_switchamalgamator.py ではパスの扱いを改善する最適化も実装できました。

広告