在b站摸魚的時候突然想到,萬一有那種需要調低音量的情況,怎麼便捷地實現呢。(至於是怎樣的情況纔會有這個需求,有機會再考慮吧……)如果使用 Gnome 或者 KDE 的話,似乎都有很完整的配件來識別膝上型電腦上各種功能鍵,也有小配件(widgets)能夠展示目前的音量大小。但是爲了這樣簡單的功能,真的需要安裝一個程式麼?
綁定音量鍵
Xorg
的包當中有一個非常實用的功能 xbindkeys
,用於自定義鍵盤設置,其優點在於能夠在每次登錄圖形界面的時候加載配置文件,而不需要重新編譯(和 dwm
本身不同)。配置文件 .xbindkeysrc
在用戶目錄中,基本語法是:
# "command" for example "ls"
# keycode for example "Mod4 + l"
於是我們需要確認兩件事:
- 調整音量的語句
- 鍵盤上的功能鍵的 keycode
ALSA (Linux 高級音響機構)
之所以要把翻譯打出來,大家不覺得這個名字很帥氣麼 Advanced Linux Sound Architecture,帶上了 Advanced 就讓人覺得很賽博時代,實際上 ALSA 的操作界面 amixer
是一個乾脆無比的命令行工具,具體用法是:amixer set <device> <options>
,在我的情況,我只需要調整整體音量就可以,所以 <device>
就是 Master
,而設置的參數可以是 x(dB|%)+
、x(dB|%)-
、(un)mute
和 toggle
,對我來說由於並沒有專業設備,所以按照百分數來設置聲音更直觀一些,而因爲 thinkpad 上只有一個靜音按鈕,所以用 toggle
會比較方便,那麼我用到的命令就是:
# toggle sound status
amixer set Master toggle > /dev/null
# lower volume
amixer set Master 2%- > /dev/null
# raise volume
amixer set Master 2%+ > /dev/null
注意到,我把所有的輸出都丟到了 /dev/null
裏面,這個看上去像文件一樣的東西實際上是一個黑洞,任何丟進去的東西都會消失不見,就方便我們把不想看見的輸出丟棄了。
xbindkeys 找到鍵名
xbindkeys
提供了一個很好的工具,用來找到鍵名,所以說一個款好的工具就是那麼貼心,不會在你不舒服的時候叫你喝熱水。輸入
xbindkeys --multikey
按的任何鍵的鍵名都會被打印在終端裏,我按順序按下了靜音、調低和調高聲響的按鈕,於是得到鍵名(keycode)把它們寫到剛剛的 .xbindkeysrc
中:
# toggle sound status
“amixer set Master toggle > /dev/null”
m:0x0 + c:121
# lower volume
"amixer set Master 2%- > /dev/null"
m:0x0 + c:122
# raise volume
"amixer set Master 2%+ > /dev/null"
m:0x0 + c:123
這樣一來就完成了音量鍵的設置,然後我就突然意識到,剛剛文章開頭的情況,我只要設置靜音鍵就可以了吧?
狀態欄設置
如果我有好好更新部落格的話,本來大家是應該知道 dwm
的狀態欄是通過 xsetroot -name
來更新的,所以我們要做的僅僅是獲取音量情況,然後把信息添加到 xsetroot
後面。
再一次呼喚 amixer
然後這次用 sed
秀操作
前文中,我們用 amixer set <device>
的命令來設置音量的值,這裏我們可以用 amixer sget <device>
來獲得音量的值,結果大致是這樣的:
$ amixer set Master toggle
Simple mixer control 'Master',0
Capabilities: pvolume pswitch pswitch-joined
Playback channels: Front Left - Front Right
Limits: Playback 0 - 65536
Mono:
Front Left: Playback 31730 [48%] [on]
Front Right: Playback 31730 [48%] [on]
我的目標就是最後兩行中的最後兩個括弧中的內容。參考了 Danilo Bargen 的腳本,他的腳本當中同時使用了 grep
和 sed
, 我認爲這就有點 overkill 了,畢竟我們要找的內容結構清晰,單用 sed
足以找到並且打印出來:
首先我們要找到包含 "Front Left:" 的行,仔細觀察可以發現字符序列 ft: 足以區分出這一行,由於我們不需要別的行,因此我們指定 sed -n
來阻止他打印別的行,而後用 p
來指定打印吻合的內容,最後我們尋找需要匹配的內容,來代替(substitute)整行,所以這個代碼應該具有這樣的格式:
sed -n "s/...\(<pattern needed>\).../\1/p"
考慮我們的行,應該是 .*ft:.*
,而後,需要的 pattern 是 \[[0-9].*$
,即從方括弧開始,繼以一個數字,一直到行末的信息。將上述內容組合起來:
amixer sget Master | sed -n 's/.*ft:.*\(\[[0-9].*%\)/\1/p'
上述命令打印出來 [48%] [on]
就是我們需要的內容。
無限循環更新狀態
在 .xinitrc
中加入這個 while true
循環:
while true; do
VOLUME=`amixer sget Master | sed -n '...
'`
xsetroot -name "$VOLUME"
done
當然,我們也可以在 xsetroot
當中加入更多需要的狀態,我的話加入了 date +"%e %b %r"
時鐘和 ibus engine | cut -d: -f4
輸入法狀態。
這樣在利用系統已有的服務而不新安裝包裹的情況下,我們完成了看電視調控聲音的需求。這是一篇做完設定然後突然想起來記錄一下的文章,所以各方面組織就比較隨意了。