🎧

シェルスクリプトでオーディオ出力を切り替える

2024/04/04に公開

Macで音を出力する先としてヘッドフォンとスピーカーがあって、自分はその二つを切り替えることが頻繁にあります。それを簡易に行うためのログです。
やりたいことが単純だから使いたい人がすぐ使えるように書きます。

前提

手順

切り替えたいデバイスの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のような違う何かだった。
https://blog.3qe.us/entry/2022/08/22/131127

JSON Linesになっていると、まずシェルスクリプト等で1行読みつつ、jqを組合わせてデータを取り出す、といったことが簡単にできる:

はえ〜シェル芸人していれば一行読んでjqで取り出すのは嬉しいのかな?
最初から配列で出してくれればjqだけでいいような・・・うーん
https://github.com/deweller/switchaudio-osx/issues/38
だとしたら引数に語弊があるのでは・・・と思わなくはない。

- SwitchAudioSource -a -t output -f json
+ SwitchAudioSource -a -t output -f jsonl

参考

参考,過程で見たもの
https://github.com/deweller/switchaudio-osx
https://zenn.dev/jun2021/articles/a773ac8fdebb00
https://blog.3qe.us/entry/2022/08/22/131127

Discussion