再起動してもヘッドフォンの音量を保持するMacBookにしておく

MacBookの音量設定は意外と秀逸で、内蔵スピーカーとヘッドフォンの音量を2系統それぞれ設定できる。ヘッドフォン出力の3.5mmミニジャックへの抜き差しに連動して、直前に保持されたそれぞれの音量に自動的に切り替わるのだ。内蔵スピーカーとヘッドフォンでちょうど良いと感じる音量はそれぞれ違うので、2系統別々に音量を保持してくれるこの仕様は、とっても有り難い。
しかーし、一旦MacBookを再起動すると、以下の欠点が見えてガッカリする...。

  • ヘッドフォンなしで再起動、→内蔵スピーカーの音量は、最後の音量設定の値*1。ヘッドフォンの音量は、56%。
  • ヘッドフォンありで再起動、→内蔵スピーカーの音量・ヘッドフォンの音量とも、最後の音量設定の値。(スピーカー・ヘッドフォンの区別なし)

つまり、電源オフで保持されるのは、1系統の音量だけなのである。しかも、ヘッドフォンの音量は、ヘッドフォンを接続した状態で再起動した場合しか設定されないのだ。接続していないと常に音量56%。ヘッドフォンの音量で56%というのは、自分にとってかなりの大音量で、心の準備なしに聴くと、鼓膜、破れそう...。
どうにかしてヘッドフォンの音量を保持したい。もう、大音量事故に遭いたくない。(いい加減、学習するべきなんだけど)そう思って、いろいろ試してみた。

環境

音量の設定ファイルはどこだ?

Preferences
  • 電源オフで保持されるということは、その設定がどこかにファイルとして保存されている可能性がある。
  • そうゆうのは大抵、Preferencesフォルダにある。そうして見つけたのが以下のファイル二つ。
/Library/Preferences/Audio/com.apple.audio.DeviceSettings.plist
/Library/Preferences/Audio/com.apple.audio.SystemSettings.plist



  • com.apple.audio.SystemSettings.plistで、ユーザーごとに入出力の音量設定の保存キーを指定して、
  • com.apple.audio.DeviceSettings.plistで、保存キー別に実際の音量設定を保存する仕組みのようだ。
  • com.apple.audio.DeviceSettings.plistは以下のようなxml形式のplistだ。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>AppleHDAEngineInput:1B,0,1,0:1</key>
	<dict>
		<key>controls</key>
		<array/>
	</dict>
	<key>AppleHDAEngineInput:1B,0,1,1:2</key>
	<dict>
		<key>controls</key>
		<array/>
	</dict>
	<key>AppleHDAEngineOutput:1B,0,1,2:0</key>
	<dict>
		<key>controls</key>
		<array>
			<dict>
				<key>element</key>
				<integer>0</integer>
				<key>scope</key>
				<integer>1869968496</integer>
				<key>selector</key>
				<integer>1836414053</integer>
				<key>value</key>
				<integer>0</integer>
			</dict>
			<dict>
				<key>element</key>
				<integer>0</integer>
				<key>scope</key>
				<integer>1869968496</integer>
				<key>selector</key>
				<integer>1936945763</integer>
				<key>value</key>
				<integer>1769173099</integer>
			</dict>
			<dict>
				<key>element</key>
				<integer>1</integer>
				<key>scope</key>
				<integer>1869968496</integer>
				<key>selector</key>
				<integer>1836414053</integer>
				<key>value</key>
				<integer>0</integer>
			</dict>
			<dict>
				<key>element</key>
				<integer>2</integer>
				<key>scope</key>
				<integer>1869968496</integer>
				<key>selector</key>
				<integer>1836414053</integer>
				<key>value</key>
				<integer>0</integer>
			</dict>
			<dict>
				<key>element</key>
				<integer>1</integer>
				<key>scope</key>
				<integer>1869968496</integer>
				<key>selector</key>
				<integer>1987013741</integer>
				<key>value</key>
				<real>0.19140625</real>
			</dict>
			<dict>
				<key>element</key>
				<integer>2</integer>
				<key>scope</key>
				<integer>1869968496</integer>
				<key>selector</key>
				<integer>1987013741</integer>
				<key>value</key>
				<real>0.19140625</real>
			</dict>
		</array>
		<key>output streams</key>
		<array>
			<dict>
				<key>bits per channel</key>
				<integer>24</integer>
				<key>bytes per frame</key>
				<integer>8</integer>
				<key>bytes per packet</key>
				<integer>8</integer>
				<key>channels per frame</key>
				<integer>2</integer>
				<key>format flags</key>
				<integer>4</integer>
				<key>format id</key>
				<integer>1819304813</integer>
				<key>frames per packet</key>
				<integer>1</integer>
				<key>sample rate</key>
				<real>48000</real>
			</dict>
		</array>
	</dict>
	<key>version</key>
	<integer>1</integer>
</dict>
</plist>
  • 音量等を変更させながら、どの値が変化するのか、差分をとってみた。
$ diff -u ~/Desktop/com.apple.audio.DeviceSettings_スピーカ2.plist ~/Desktop/com.apple.audio.DeviceSettings_イヤホン1.plist 
--- ~/Desktop/com.apple.audio.DeviceSettings_スピーカ2.plist
+++ ~/Desktop/com.apple.audio.DeviceSettings_イヤホン1.plist
@@ -34,7 +34,7 @@
 				<key>selector</key>
 				<integer>1936945763</integer>
 				<key>value</key>
-				<integer>1769173099</integer>
+				<integer>1751412846</integer>
 			</dict>
 			<dict>
 				<key>element</key>
@@ -64,7 +64,7 @@
 				<key>selector</key>
 				<integer>1987013741</integer>
 				<key>value</key>
-				<real>0.129150390625</real>
+				<real>0.0625</real>
 			</dict>
 			<dict>
 				<key>element</key>
@@ -74,7 +74,7 @@
 				<key>selector</key>
 				<integer>1987013741</integer>
 				<key>value</key>
-				<real>0.129150390625</real>
+				<real>0.0625</real>
 			</dict>
 		</array>
 		<key>output streams</key>

上記から以下のことがわかった。

  • AppleHDAEngineOutput:1B,0,1,2:0 >> controls >> item 1のvalueが音量設定した時の出力機器の違い。
    • 1769173099=スピーカー
    • 1751412846=ヘッドフォン
  • AppleHDAEngineOutput:1B,0,1,2:0 >> controls >> item 4・item 5のvalueが音量。
    • 0.0625=音量1(音量設定ファンクションの1目盛り)
    • 0.129150390625=音量2(音量設定ファンクションの2目盛り)
NVRAM
  • それから、昔PRAM、今NVRAMと呼ばれる電源オフでもデータが保持されるメモリにも、音量設定に連動する項目があった。
  • NVRAMの内容は、ターミナルからNVRAMコマンドで読み書きできる。
$ nvram -xp
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>SystemAudioVolume</key>
	<data>
	HA==
	</data>
	<key>boot-args</key>
...(中略)...
</dict>
</plist>
  • テキストに保存して、PropertyList Editorで開くと、以下のように見えた。


挫折

  • 本来は2系統以上の音量を保存する性能を持っているのではないか?と淡い期待を抱きながら、自らxmlを適当に弄っては再起動を繰り返し、試してみた。
  • しかし、自分の想像力では、どうあがいても現状の1系統の壁を越えることはできず、再起動時にヘッドフォンの音量をコントロールすることはできなかった...。

AppleScriptで音量操作

  • 設定ファイルからコントロールする方法は諦めた...。
  • こうなったら、いつもの最後の手段、AppleScriptだ!


set volume 1 --(0-7の8段階、少数指定OK)
get volume settings
--結果:{output volume:14, input volume:50, alert volume:100, output muted:false}

set volume 7 --(0-7の8段階、少数指定OK)
get volume settings
--結果:{output volume:100, input volume:50, alert volume:100, output muted:false}

set volume with output muted --消音
get volume settings
--結果:{output volume:100, input volume:50, alert volume:100, output muted:true}

set volume without output muted --消音解除
get volume settings
--結果:{output volume:100, input volume:50, alert volume:100, output muted:false}

  • set volumeによって、現在アクティブな出力装置に対しての音量を設定できるようだ。

音量設定するタイミング

  • AppleScriptによる音量設定の方法は分かったが、問題はどのタイミングで実行するかということだ。
  • 起動項目に入れて、起動時に実行しても、おそらく内蔵スピーカーの音量が設定されるだけ。
  • 自分の使い方として、通常はヘッドフォンは使わない。
  • Webページからリンクする動画を見たり、音楽を聴いたり、そのような場合にヘッドフォンを接続して視聴している。
  • 視聴が終われば、ヘッドフォンは外す。
  • だから、MacBookの再起動時は、ほとんどの場合ヘッドフォンは接続していない。ヘッドホンの使用は一時的なのだ。
  • 以上のことを考慮すると、ヘッドフォンの音量を設定するには、ヘッドフォンを接続した時にAppleScriptを実行する必要がある。
  • しかし、ヘッドフォンが接続されたことは、どうやったら知ることができるのだろう?
  • コンソール.appを起動して、「すべてのメッセージ」を確認してみたが、ヘッドフォンの接続は記録されていなかった...。
    • 記録されていたら、ログを監視すればヘッドフォンの接続を知ることが出来るのに...。
  • 確実なのは、システム環境設定 >> サウンド >> 出力 の「サウンドを出力する装置を選択:」の部分なら「内蔵スピーカー」か「ヘッドフォン」に切り替わる。
  • GUIスクリプティングで上記の項目を監視すれば、ヘッドフォンの接続を知ることができそう。
  • ところが、そのためには常にシステム環境設定 >> サウンドを開いておく必要があるのだ...。
  • ちょっとスマートな方法ではない。MacBookの負荷も大きくなりそう。

Breakaway

すると、今の自分にピッタリの素晴らしいアプリケーションだった!

  • なんと!PluginsにAppleScript Triggerというのがあるではないか!



(* headphone_volumes_control.scpt *)

set volume 0.4375 --7/16で音量ファンクションの1目盛り分を計算
activate
display dialog "音量1を設定しました。" buttons {"OK"} giving up after 5
--do shell script "/usr/local/bin/growlnotify -m '音量1を設定しました。' 'Breakaway'"

  • AppleScript Triggerを選択して、+ボタン、iボタン、矢印の部分を設定した。
  • AppleScriptは、ファイルをScriptアイコンにドラッグ&ドロップした。
  • 最後に、AppleScript Triggerのnameに「ヘッドフォンの音量設定」と入力して、チェックマークも入にした。

さて、ヘッドフォンを差し込んでみると...

  • ちゃんとダイアログが表示された!
  • 音量も1になってる!

注意すること

  • AppleScriptのファイルフォーマットは、スクリプト(.scpt)で保存した。
  • AppleScriptを変更したら、ドラッグ&ドロップをやり直す必要がある。
  • iTunesが起動していないと、正常に動作しなかった。(先にBreakawayを起動して、その後iTunesの順に起動する必要がありそう)
  • AppleScript Triggerの名前を入力しないと、Breakawayを終了するとTriggerが削除されてしまう。
  • せっかく登録してもチェックマークを入にしないと、そのTriggerは無効。AppleScriptは起動しない。

動作確認

  • BreakawayのAutomatically start as login のチェックを入にして、
  • iTunesもシステム環境設定 >> アカウント >> ログイン項目に追加して、
  • MacBookを再起動して、
  • ヘッドフォンを差し込んでみる。

しかし、無反応...。
おかしい、さっきはちゃんと反応したのに...。

  • 試行錯誤すること数十回、BreakawayがAppleScriptを起動する条件がかなり限定的なことに気付いた。
    • まず、上記にも書いたとおり、Breakawayを起動する。
    • Preferences... >> Plugins >> AppleScript Trigger を選択した状態にしておく。
    • iTunesを起動する。
  • ヘッドフォンを差し込んだとき、確実にAppleScriptを起動するためには、上記の条件が必要であった。
  • しかし、再起動のたびにこれらの操作を行うことは面倒だし、それなら手動でヘッドフォンの音量を設定すれば良い訳で...。
  • こうなったらログイン項目に頼らずに、BreakawayとiTunesも上記の条件で起動させるAppleScriptを作ってしまう。
  • ログイン項目からBreakawayとiTunesを削除して、代わりに以下のBreakaway_enabler.appを入れた。


(* Breakaway_enabler.app *)

--タイミングによっては、GUIが描画されるまでの処理時間が予想外に必要だったりして、
--GUIスクリプティングがうまく処理されないことがある。
--それを回避するため、本来の処理をmain()に入れて、10回再試行するようにしている。
repeat 10 times
if my main() then exit repeat
end repeat

on main() try
tell application "Breakaway" to activate
tell application "System Events"
tell process "Breakaway"
keystroke "," using command down --Preferences...を表示
delay 0.5
click window 1's tool bar 1's button "Plugins"
delay 0.5
tell application "Breakaway" to activate
select window 1's scroll area 1's table 1's row 0
keystroke "w" using command down
end tell
end tell
delay 1
tell application "iTunes" to launch
true
on error
do shell script "/usr/local/bin/growlnotify -m 'エラー発生、再試行します。' " & my name
beep
false
end try
end main

  • それから、ヘッドフォンを差し込むたびに、ちょうど良い音量まで変更してしまうのは余計なので、音量が大き過ぎる時だけ調整するようにした。
  • headphone_volumes_control.scptを修正して、BreakawayのAppleScript Trigger「ヘッドフォンの音量設定」を開いて、再度ドラッグ&ドロップした。


(* headphone_volumes_control.scpt *)

do shell script "/usr/local/bin/growlnotify -m 'Headphone Connection' 'Brakeaway'"
if (get volume settings)'s output volume > 20 then
delay 1
set volume 0.4375 --7/16で音量ファンクションの1目盛り分を計算
activate
display dialog "音量1を設定しました。" buttons {"OK"} giving up after 5
end if

  • 再び、MacBookを再起動して、
  • ヘッドフォンを差し込んでみる。


出た!


これで、ヘッドフォンの大音量事故を回避できそう!



音量設定のことを調べている時に、ついでに知ってしまったこと

微音量設定

(以下の操作すべてに、キーボードの設定によってはfnキーの併用も必要)

  • option-shift-音量ファンクションキー
    • 1/4刻みで音量設定する
  • 音量0にして、ミュートファンクションキー(消音を解除する)
    • 最小音量(1/4音量よりも小さい)
  • shift-音量ファンクションキー
    • フィードバック音(ピャ、ピャ、)なしで音量設定する。
  • option-音量ファンクションキー
    • システム環境設定 >> サウンド を起動する。

上記のキー操作は、システム環境設定 >> サウンド >> 出力タブで、主音量のスライダーをマウスで微妙に操作することと同等。

光デジタル出力

  • なんと!あのヘッドフォンジャックからは、光デジタル信号も出力されていたのだ。
  • 光デジタル出力と言えば、光ファイバーの両端が四角いプラグしか知らなかった。(昔持っていた安物のでっかいステレオに付いていた)
  • 実は光デジタル出力のコネクタにはもう一種類、丸型のプラグあって、それがいつもヘッドフォンを差し込んでいたジャックと同じ形だったのだ。
  • 音質にこだわりたい人は、無劣化のデジタル信号を取り出して、自分の納得するDAコンバーターとアンプで音に変換できるのであった。

この光デジタル出力は、初代MacBookからの仕様であった。
4年以上も使っていて、今さら気付くとは...。(みんな知ってるのだろうか?)

*1:内蔵スピーカー・ヘッドフォンの区別なく音量設定した最後の値が、次回起動時の音量になるという意味