シェルスクリプトでオーディオ出力を切り替える
Macで音を出力する先としてヘッドフォンとスピーカーがあって、自分はその二つを切り替えることが頻繁にあります。それを簡易に行うためのログです。
やりたいことが単純だから使いたい人がすぐ使えるように書きます。
前提
- zsh
- zsh固有の書き方はないはず 他環境は適宜読み替えて
- jq
-
brew install jq
する
-
- switchaudio-osx
- https://github.com/deweller/switchaudio-osx
-
brew install switchaudio-osx
する
手順
切り替えたいデバイスのuid
を調べる
SwitchAudioSource -f json -t output -a
{
"name": "LG HDR 4K",
"type": "output",
"id": "124",
"uid": "xxxxx-xxxx-xxxx-0B20-xxxxxxxxx"
}
{
"name": "GSX 1000 Main Audio",
"type": "output",
"id": "139",
"uid": "xxxxx-xxxx-xxxx-0B21-xxxxxxxxx"
}
{
"name": "MacBook Airのスピーカー",
"type": "output",
"id": "110",
"uid": "BuiltInSpeakerDevice"
}
デバイスのname
でも使用できるが、Macの本体言語を切り替えるとname
は相応に変更されるため一意のuid
が望ましい。変えることがなければname
でも良い。
試しに切り替えてみる。
SwitchAudioSource -u "1E6D7F77-0000-0000-0B20-0xxxxxxxxxx8"
切り替えが確認できたら本題のスクリプトで2出力をトグルするようにする。
#!/bin/zsh
# 2つの出力先のUIDを定義
output1="xxxxx-xxxx-xxxx-0B20-xxxxxxxxx"
output2="xxxxx-xxxx-xxxx-0B21-xxxxxxxxx"
# 現在のデフォルトの出力先を取得
current_output=$(SwitchAudioSource -c -f json | jq -r .uid)
# 現在の出力先がoutput1と一致するかを確認し、適切な出力先に切り替える
if [[ "$current_output" == "$output1" ]]; then
SwitchAudioSource -u "$output2"
echo "Switched to Headphone"
elif [[ "$current_output" == "$output2" ]]; then
SwitchAudioSource -u "$output1"
echo "Switched to speaker"
else
echo "Unknown current output"
fi
このスクリプトをAutomatorで実行しても良い、Stream Deckの一つに割り振っても良いだろう。
自分はショートカット(純正のアプリの方)に登録してそれをRaycastでグローバルショートカットで呼べるようにした。適当にアラートを出せるのでわかりやすい。
余談
ところで自分でスクリプトを書いたとき
if [[ "$current_output" == "$output1" ]]; then
の部分で一生比較が通らずシェル素人はとても困った。
ChatGPTに聞いたところ大凡同じコードが出てきた。(ここで自分でシェル書くなんてもうやめよう、と思った。できたほうが良いのは分かっているが・・・)
# 現在のデフォルトの出力先のUIDを取得
current_output=$(SwitchAudioSource -c -f json | jq -r '.device.uid')
とGPTは出してきてここでjq -r
に思い至ったわけだ。自分は-r
なしで実行していたのでクォートされて出力されてしまっていたのだ。
何度も実行してクォートされていることは見ていたのにどうしてかそのまま比較できると思っていた。ほとほと自分には呆れるがここに残したので二度とこれで詰まることは無い。
uidが一意ではなかったかもしれない
気づいたらディスプレイのuidに0000000なるsuffixが追加されていてコケるようになった。これ使っちゃダメなのか・・・? このせいで結局環境変えなければ変動しないnameの方がいいんじゃないか、と思うようになったというオチ。
jsonじゃなくてjsonl
切り替えたいデバイスの
uid
を調べる
ここで出力されているモノをnameで拾おうとしてjqが上手く書けない〜〜と思っていたんだけどよくよく見たらjsonのような違う何かだった。
JSON Linesになっていると、まずシェルスクリプト等で1行読みつつ、jqを組合わせてデータを取り出す、といったことが簡単にできる:
はえ〜シェル芸人していれば一行読んでjqで取り出すのは嬉しいのかな?
最初から配列で出してくれればjqだけでいいような・・・うーん
だとしたら引数に語弊があるのでは・・・と思わなくはない。
- SwitchAudioSource -a -t output -f json
+ SwitchAudioSource -a -t output -f jsonl
参考
参考,過程で見たもの
Discussion