起動音ジャーンの音量を自在にコントロールしたい!

MacBook Pro Retinaに移行してから、起動音がうるさくなってしまった。MacBook OSX10.6.8までの環境では、StartupSound.prefPaneが素晴らしくって、たいへんお世話になっていた。ところが、OSX Lion以降の環境には残念ながら対応していない...。困っていた。*1

起動音を消音する技

sudo defaults read com.apple.loginwindow
  • 事前に上記コマンドでcom.apple.loginwindowフックが利用されていないことを確認しておく。
2012-12-18 04:53:04.603 defaults[6963:a07] 
Domain /var/root/Library/Preferences/com.apple.loginwindow does not exist
  • 確認できたら、以下のシェルスクリプトで独自のフックを設定するのだ。(コピー&ペーストでOK)
sudo sh -c "cat <<EOS > /Library/Scripts/mute-on.sh
#!/bin/bash
osascript -e 'set volume with output muted'
EOS
"

sudo sh -c "cat <<EOS > /Library/Scripts/mute-off.sh
#!/bin/bash
osascript -e 'set volume without output muted'
EOS
"

sudo chmod u+x /Library/Scripts/mute-on.sh
sudo chmod u+x /Library/Scripts/mute-off.sh

sudo defaults write com.apple.loginwindow LogoutHook /Library/Scripts/mute-on.sh
sudo defaults write com.apple.loginwindow LoginHook /Library/Scripts/mute-off.sh
  • フックを解除する場合は、以下のコマンドを実行する。
sudo defaults delete com.apple.loginwindow LoginHook
sudo defaults delete com.apple.loginwindow LogoutHook
sudo rm /Library/Scripts/mute-on.sh
sudo rm /Library/Scripts/mute-off.sh
  • 上記の仕組みをそのまま利用していると思われるStartNinjaというアプリケーションもあった!


ところで、起動音は単なる飾りではない。電源投入後のPOSTと呼ばれる自己診断テスト(ハードウェアの初期化やメモリテスト)に合格したことを意味している。

仮にMacBookが正常に起動しない状態に陥った時、「ジャーン」が鳴っていれば自己診断テストまでは問題ないことが確認できる。鳴らない時はビープ音によって、エラーの内容を伝えてくれる。(稀にメモリの取り付け不良などで「ジャーン」が鳴らないことなどがある)つまり「ジャーン」はハードウェアに問題がない、という重要なフィードバックなのである。(鳴って当然なのだけど)だから、消音にしてしまうことには若干抵抗があるのだ。

主音量と起動音の関係

  • 今までStartupSound.prefPaneに頼っていたので無頓着になっていたが、起動音の音量はシステム終了時の主音量と連動している。
  • 主音量が小さければ起動音も小さくなり、主音量が大きいと起動音も大きくなる。(MacBook Pro Retina OSX 10.8.2の環境において)
  • ならば、消音のシェルスクリプトを若干変更して、小さな音量を設定するスクリプトにしてしまう、そんなシェルスクリプトを作ってみた。
    • ログアウトの時、現在の主音量を保存して、好みの小さな音量にしておく。
    • ログインの時は、上記で保存した主音量に戻す。
sudo sh -c "cat <<EOS > /Library/Scripts/mute-on.sh
#!/bin/bash
osascript -e \"(get volume settings)'s output volume\" > /Library/Preferences/com.bebekoubou.StartupVolume.txt
osascript -e \"set volume 7/16/100\"
EOS
"

sudo sh -c 'cat <<-EOS > /Library/Scripts/mute-off.sh
#!/bin/bash
osascript -e "set volume 7*\`cat /Library/Preferences/com.bebekoubou.StartupVolume.txt\`/100"
EOS
'

sudo chmod u+x /Library/Scripts/mute-on.sh
sudo chmod u+x /Library/Scripts/mute-off.sh

sudo defaults write com.apple.loginwindow LogoutHook /Library/Scripts/mute-on.sh
sudo defaults write com.apple.loginwindow LoginHook /Library/Scripts/mute-off.sh
  • ややこしい所は、ボリュームの単位が3つあること。
    • AppleScriptの(get volume settings)'s output volumeでは、0-100の音量として返される。
    • AppleScriptのset volume Xで設定するときのXは、0-7の音量として設定する。
    • システム環境設定やボリュームキーで設定する時は、0-16の音量として設定する。
  • 以上のことを考慮すると、以下のような計算式が導かれた。
    • set volume 7/16で、ボリュームキーの1目盛りの音量になる。
    • (get volume settings)'s output volumeで25を取得した場合、
    • set volume 7*25/100で、元どおりの音量になる。

以上のシェルスクリプトを実行しておくと、起動音ジャーンは、ごく小さなささやきの音量にコントロールされた!

ヘッドフォンの問題

  • できた!と喜んだのも束の間、すぐに問題が発覚した...。
  • ヘッドフォンが差し込まれたままでは盛大に「ジャーン」
  • ヘッドフォンを差し込んでいなければ小さな音量にコントロールされる。
  • ほとんどヘッドフォンを差し込むことはないのでまあいいか、とスルーしようと思ったが、
  • よくよく考えれば、外出先でこそヘッドフォンをして使うかもしれない。
  • ヘッドフォンから音が出るだろうと想像して再起動すると、恥ずかしい思いをしてしまう。

ヘッドフォンの音量もコントロールしておきたい!

nvramを利用する起動音のコントロール

  • 起動音をコントロールするには、もう1つの方法がある。
  • nvramコマンドでSystemAudioVolumeを設定するのだ。
sudo nvram SystemAudioVolume=%80
  • 以上の設定をした状態で再起動すると、起動音が消音される、
  • ...はずなのだが、どうもMacBook Pro Retina OSX 10.8.2の環境では、消音されたり、されなかったりと安定しない。気まぐれである...。
  • ちなみに初代MacBook OSX 10.6.8の環境で試すと、常に消音された!どうやら、ハードウェアの仕様が変更されているのかもしれない。
  • 実は、最初はnvramを設定する方法でお手軽にできないかと試行錯誤したのだけど、確実に設定できる方法が見つけられなくて諦めていた。
  • その代わり、何度も試行錯誤する中でシステムの主音量はシステム終了時にnvramに書き込まれている、というストーリーが想像できた。
  • nvramコマンドで設定しても、システム終了時にOS側で書き換えてしまうため、nvramの設定がうまく反映されないのかもしれないと。
  • それを裏付ける現象として、システム起動後にnvram SystemAudioVolumeを実行すると、%80ではない値が出力されてしまうのだ...。
$ nvram SystemAudioVolume
SystemAudioVolume	:
  • 稀に運良く起動音が消音された時は、システム起動後のSystemAudioVolumeは、ちゃんと%80に設定されている。
  • また、システム起動後のSystemAudioVolumeの値は、主音量のボリューム値と連動していることにも気付いた。
  • さらに試行錯誤を続けていると、稀に運良く起動音が消音される条件、というのが分かってきた。
  • どうも、ヘッドフォンを接続した状態で再起動すると、起動音が消音されることが多いのだ。


もしかしたら、nvramの設定はヘッドフォンに有効なのかもしれない!そんな期待をしながら試して理解したこと。

  • nvramによる設定は、内蔵スピーカーには無効である。
  • nvramによる設定は、ヘッドフォンには有効である!
    • ただし、ヘッドフォンを差し替え後の初回の再起動は、音量が制御されない。
    • 2回目以降の再起動は、音量が制御された。
    • また、たとえ初回の再起動であっても、一旦ログアウトしてから再起動すると、音量が制御された。
  • 以上のことを理解して、ログアウトフックの中でnvramの設定もしてみようと思い付いた。
  • set volumeとnvramコマンドの合わせ技である。
  • SystemAudioVolumeの値は、set volumeで設定するボリューム値に連動した値に設定してみた。

すると、ヘッドフォンを差し込んだ状態でも起動音がコントロールされるようになった!

sudo sh -c "cat <<EOS > /Library/Scripts/mute-on.sh
#!/bin/bash
osascript -e \"(get volume settings)'s output volume\" > /Library/Preferences/com.bebekoubou.StartupVolume.txt
osascript -e \"set volume 7/16/100\"
nvram SystemAudioVolume=%03
EOS
"

sudo sh -c 'cat <<-EOS > /Library/Scripts/mute-off.sh
#!/bin/bash
osascript -e "set volume 7*\`cat /Library/Preferences/com.bebekoubou.StartupVolume.txt\`/100"
EOS
'

sudo chmod u+x /Library/Scripts/mute-on.sh
sudo chmod u+x /Library/Scripts/mute-off.sh

sudo defaults write com.apple.loginwindow LogoutHook /Library/Scripts/mute-on.sh
sudo defaults write com.apple.loginwindow LoginHook /Library/Scripts/mute-off.sh
  • ただし、ヘッドフォン差し替え後の初回の再起動だけは、一旦ログアウトしない限り、起動音は制限なく鳴ってしまう...。

起動音をコントロールするAppleScript

  • ヘッドフォンの問題だけは一部残ってしまったが、以上の仕組みをAppleScriptにまとめておくことにした。


property StartupVolume : ""

if StartupVolume_hook() ≠ "" then
activate
"すでにフックが利用されています。
[ OK ]で続けると、以下のフックが無効になります。" & return & return & StartupVolume_hook() display dialog result with title "com.apple.loginwindowフック"
end if

set script_volumes to {"0", "7/16/100", "7/16/4", "7/16", ""} set nvram_volumes to {"%80", "%03", "%0e", "%1d", ""} set v to (get volume settings)'s output volume
repeat
activate
{"1. □□□□□□□□", "2. ■□□□□□□□", "3. ■■□□□□□□", "4. ■■■□□□□□", "5. 調整なし"} set res to (choose from list result with title "音量設定" default items StartupVolume OK button name "設定" cancel button name "閉じる") if res's class is list then
set StartupVolume to res
set vol_str to script_volumes's item (res's item 1's item 1) set sys_str to nvram_volumes's item (res's item 1's item 1) else
exit repeat
end if
if vol_str = "" then
uninstall_hook() else
install_hook(vol_str, sys_str) end if
simulate_startup_sound(vol_str) activate
display dialog "起動音: " & (res as text)'s items 3 thru -1 buttons {"OK"} with icon 1 giving up after 3
end repeat
set volume 7 * v / 100




--LoginHook LogoutHookをアンインストール
on uninstall_hook() "
fpath=/Library/Preferences/com.bebekoubou.StartupVolume;

rm $fpath.txt;
rm $fpath.set_volume.sh;
rm $fpath.restore_volume.sh;
defaults delete com.apple.loginwindow LoginHook;
defaults delete com.apple.loginwindow LogoutHook;"
try
do shell script result with administrator privileges
end try
end uninstall_hook

--LoginHook LogoutHookをインストールする
on install_hook(vol_str, sys_str) "
fpath=/Library/Preferences/com.bebekoubou.StartupVolume;

cat <<EOS > $fpath.set_volume.sh;
#!/bin/bash
osascript -e \"(get volume settings)'s output volume\" > $fpath.txt
osascript -e \"set volume " & vol_str & "\"
nvram SystemAudioVolume=" & sys_str & "
EOS
chmod u+x $fpath.set_volume.sh;
defaults write com.apple.loginwindow LogoutHook $fpath.set_volume.sh;

cat <<EOS > $fpath.restore_volume.sh;
#!/bin/bash
osascript -e \"set volume 7*\\`cat $fpath.txt\\`/100\"
EOS
chmod u+x $fpath.restore_volume.sh;
defaults write com.apple.loginwindow LoginHook $fpath.restore_volume.sh;"
do shell script result with administrator privileges
end install_hook

--設定した音量で起動音を再生してみる
on simulate_startup_sound(vol_str) set fpath to "/Applications/Mactracker.app/Contents/Resources/Chimes/PCI based Power Mac 2.m4a"
try
do shell script "test -e " & quoted form of fpath
do shell script "(
v=`osascript -e \"(get volume settings)'s output volume\"`;
osascript -e \"set volume " & vol_str & "\";
afplay " & quoted form of fpath & "
osascript -e \"set volume 7*$v/100\"
) >& /dev/null &"
on error
do shell script "(
v=`osascript -e \"(get volume settings)'s output volume\"`;
osascript -e \"set volume " & vol_str & "\";
afplay '/System/Library/Sounds/Sosumi.aiff'
osascript -e \"set volume 7*$v/100\"
) >& /dev/null &"
end try
end simulate_startup_sound

--com.apple.loginwindowフックの状態を返す
on StartupVolume_hook() activate
{"/Library/Preferences/com.bebekoubou.StartupVolume.restore_volume.sh", ¬ "/Library/Preferences/com.bebekoubou.StartupVolume.set_volume.sh"} set hook_paths to result
try
set LoginHook to ""
"defaults read com.apple.loginwindow LoginHook"
set LoginHook to do shell script result with administrator privileges
if LoginHook is in hook_paths then set LoginHook to ""
end try
try
set LogoutHook to ""
"defaults read com.apple.loginwindow LogoutHook"
set LogoutHook to do shell script result with administrator privileges
if LogoutHook is in hook_paths then set LogoutHook to ""
end try
if LoginHook & LogoutHook"" then
"defaults read com.apple.loginwindow"
do shell script result with administrator privileges
else
""
end if
end StartupVolume_hook

  • 起動すると、まず管理者の認証が必要。

f:id:zariganitosh:20121219120658p:image:w500

  • すでにStartNinjaなどのフックを利用するアプリケーションを利用している場合は、以下のような警告が出る。
  • 独自のフックを利用している方は、バックアップを忘れずに。
  • [ OK ]を押すと処理は続行され、フックの設定は上書きされる。

f:id:zariganitosh:20121219120922p:image:w368

  • これが起動音の音量を設定するウィンドウだ。
  • 設定した時に、その音量で起動音が鳴るのだ!
      • ただし、起動音を鳴らすにはMactrackerのインストールが必要。

f:id:zariganitosh:20121219120246p:image:w260


やっとできた。ここまで、いったい何十回再起動したことか。

*1:ほとんど持ち出すことはないのだけど、外出先で起動する時、盛大に「ジャーン」を鳴らしてしまうのは格好悪いと思っている。なんだかMacBookをちゃんとコントロールできていない人みたいなイメージ。控えめに他人に聞こえるか、聞こえないかのごく小さな「ジャーン」で使っている方に出会うと、一目置いてしまう。