■ 問題の概要
実行時間がクソ長いコマンドを実行しているときに、終わったら音で知らせてほしいと思った。
そこで昔、よく使っていた beep コマンドを思い出して使おうと実行したところ、以下のようなエラーが出た。
$ beep
beep: Error: Could not open any device
exit 1
当然、音も鳴らない。
これを鳴るようにしたい。
■ TL;DR
ビープ音を出すカーネルモジュール(pcspkr)を扱うための権限が必要。
これは udev を使って設定する。
基本的に以下の記事を踏襲するだけでよい。
【参考】 PC スピーカー - ArchWiki
https://wiki.archlinux.jp/index.php/PC_スピーカー#root_以外のユーザーによる使用
以下のように udev のルールファイルを作って設定、および pcspkr をリロードする。
$ sudo -E vim /etc/udev/rules.d/70-pcspkr-beep.rules
$ sudo cat /etc/udev/rules.d/70-pcspkr-beep.rules
ACTION=="add", SUBSYSTEM=="input", ATTRS{name}=="PC Speaker", ENV{DEVNAME}!="", TAG+="uaccess"
$ sudo udevadm control --reload && sudo rmmod pcspkr && sudo modprobe pcspkr
環境によるが、 Arch Linux を最低限のインストールしただけの状態なら、通常 ALSA がサウンドを担当しているため、これでビープ音が鳴るようになっているはず。
PulseAudio を入れている場合は、残念ながら beep コマンドは動作しない。
しかし、代わりに X-Window System 環境では X11 Bell Event を利用して類似コマンド xkbbell を使うことは可能。
これは /etc/pulse/default.pa 、または ~/.config/pulse/default.pa に以下を記述してから再起動する。
load-sample x11-bell /usr/share/sounds/freedesktop/stereo/bell.oga
load-module module-x11-bell sample=x11-bell
beep コマンドの代わりには xkbbell コマンドを使う。
なお、音声ファイルは変更が可能である。wave ファイルも使える。
以下は経緯を含めた詳細ログ+おまけ。長い。
■ beep コマンドがエイリアスとかになってないよね?
と思ったけど、 beep がおかしいわけではない。
$ type beep
beep はハッシュされています (/usr/bin/beep)
$ pacman -Qo /usr/bin/beep
/usr/bin/beep は beep 1.4.12-1 によって所有されています
$
プレーンな beep コマンドだ。
■ sudo するとどうなる?
デバイスが開けないということなんで、これを開く権限か?
と思って sudo すると、
$ sudo beep
[sudo] 9hnder のパスワード:
beep: Error: Running as root under sudo, which is not supported for security reasons.
beep: Error: Set up permissions for the pcspkr evdev device file and run as non-root user instead.
exit 1
Google 翻訳::
beep: Error: sudo で root として実行しますが、これはセキュリティ上の理由からサポートされていません。
beep: Error: pcspkr evdev デバイス ファイルのアクセス許可を設定し、代わりに非 root ユーザーとして実行します。
むう。怒られた。
sudo で root しちゃダメらしい。
■ カーネルモジュールの pcspkr が必要らしい
もしやデバイスがないのか?壊れてたりは?
いや、BIOS (UEFIだけど)とかでビープ音自体は鳴るし、デスクトップ環境でも MP3 なんかは Audacious で普通に再生できるぞ。
ってことで調べてみると、カーネルモジュールが必要らしい。
先程怒られたときに表示されてた pcspkr (注:PCスピーカーと読む)だな。
やってみる。
$ sudo modprobe pcspkr
$ beep
beep: Error: Could not open any device
exit 1
$
だめじゃん。。。
■ /etc/beep.conf にデバイスを定義すると良いらしい
同じ Arch Linux を使っている人の記事で以下が見つかった。これか!
【参考】beep コマンドが使えない時の対処法 #beep - Qiita
https://qiita.com/H-goto16/items/0d2ade41be1f1b5514ca
ふーん。
$ beep --verbose
beep: Verbose: evdev: driver_detect 0x561dac0e60c0 (nil)
beep: Verbose: lib: could not open(2) /dev/input/by-path/platform-pcspkr-event-spkr: Permission denied
beep: Verbose: console: driver_detect 0x561dac0e5e60 (nil)
beep: Verbose: lib: could not open(2) /dev/tty0: Permission denied
beep: Verbose: lib: could not stat(2) /dev/vc/0: No such file or directory
beep: Error: Could not open any device
exit 1
$
同じエラーだな。
じゃあ、早速やってみる。
$ sudo -E vim /etc/beep.conf
$ sudo cat /etc/beep.conf
# 9hnder: Make this file. beep error fix.
# @see: https://qiita.com/H-goto16/items/0d2ade41be1f1b5514ca
device /dev/input/by-path/platform-pcspkr-event-spkr
$ ll /etc/beep.conf
-rw-r--r-- 1 root root 156 2月 11 00:28 /etc/beep.conf
$ beep
beep: Error: Could not open any device
exit 1
あれれ?
ダメだな…。
■ input グループへの参加が必要だった
日本ではいい情報が見つからなかったので、アメリカン情報を漁る。
以下が見つかった。
【参考】sound - beep not working since upgrade to 20.04.1 LTS - Ask Ubuntu
https://askubuntu.com/questions/1289288/beep-not-working-since-upgrade-to-20-04-1-lts
ん?
input グループを追加すんの?
$ ll /dev/input/by-path/platform-pcspkr-event-spkr
lrwxrwxrwx 1 root root 10 1月 16 00:05 /dev/input/by-path/platform-pcspkr-event-spkr -> ../event10
$ ll /dev/input/event10
crw-rw---- 1 root input 13, 74 1月 16 00:05 /dev/input/event10
$
ああ、確かに。
ビープのデバイスは input グループに属しているね。
ではやってみよう。
$ sudo usermod -aG input $USER
$ beep
beep: Error: Could not open any device
exit 1
$ groups
http clamav users lp wheel
$ grep input /etc/group
input:x:994:9hnder
$ bash
$ groups
http clamav users lp wheel
$ exit
exit
$
$ su 9hnder -
パスワード:
$ groups
http clamav users lp input wheel
$ beep
$
ああ、なるほど。
su - することでグループ追加が更新されるのねー。
これでエラーなくなったし、めでたく鳴る、、、
…と思ったんだけど、鳴らず。。
何らかの理由でビープ音がオフられてるくさい。
alsamixer にも Beep の項目が出てこないし。。
…そういえば、なんか初期の Arch Linux インストール時にビービー鳴ってうざかったからオフったような記憶が、なくもない。
■ /etc/beep.conf はやっぱいらなかった
そもそも beep --verbose したときに既にデバイスのパスは正しく参照されており、 Permission だけエラーになっていたのでいらんだろ。たぶん。
ってことで消した。
$ sudo rm /etc/beep.conf
[sudo] 9hnder のパスワード:
$
$ beep
$
ちゃんと動く。
うむ。やはりな。
カーネルモジュール pcspkr についても、通常、必要に応じて自動的にロードされるはずなので手動で追加しなくともよいはずだ。
■ しかし、input グループを補助グループにするのって大丈夫なの?
タイトル通りなのだが、よくよく考えたら input グループって入力機器全般を扱う権限を持っているグループなので、それをユーザーの補助グループに追加してしまうのは微妙な気がする。例えば、キーボードやマウスの入力についてもこのグループの権限があれば読み出しできてしまうような…。
もちろん、スタンドアロンで使っている分にはあまり問題はなさそうな気がするが…。
うーん、でも Beep 音を聞きたいってことはサーバーじゃないだろうから、問題ないのか?
…まあ、でも気持ち悪いよね。input みたいな大きすぎる権限を与えるのは。
「大いなる力には、大いなる責任も伴う」By.スパイダーマン。
って、憧れのヒーローも言ってるしね。
Ubuntu だか何だかは最初からグループ追加されてたような気もせんでもないけど…。
■ ってことで別の方法を取る。udev を使う
更に検索してみたら、Arch Linux Wiki にジャストな記載があった。以下。
【参考】PC スピーカー - ArchWiki
https://wiki.archlinux.jp/index.php/PC_スピーカー#root_以外のユーザーによる使用
デバイス管理ツール udev のルールファイルを作って、それに以下のように書き込めばいいらしい。
ACTION=="add", SUBSYSTEM=="input", ATTRS{name}=="PC Speaker", ENV{DEVNAME}!="", TAG+="uaccess"
簡単に解説すると、以下のようになる。
最後の += 演算子を使って書かれた TAG 以外は作動のための条件定義である。
全条件にマッチした場合のみ、作動する。
- ACTION ............ 指定したイベントのアクション名とマッチ。ここでは add を
指定しているのでデバイスが追加された場合に作動。
- SUBSYSTEM ......... イベントデバイスのサブシステムとマッチ。ここでは input
を指定しているので入力デバイスに対して作動。
- ATTRS{filename} .... システムファイル属性値。ここでは name を指定しているの
で名称が "PC Speaker" と一致した場合に作動。
- ENV{key} .......... デバイス・プロパティ key の値。ここでは DEVNAME に対し
!="" としているので『デバイス名のプロパティ値が空でなけ
れば』という意味。
- TAG ............... デバイス・タグ。ここでは +="uaccess" としているので、
uaccess タグが追加されるという意味になる。これは udev
が動的にユーザーがデバイスへアクセスできるように権限を
付与するというもの。
【参考】udev - ArchWiki
https://wiki.archlinux.jp/index.php/Udev
ルールファイルを書いたら、以下のコマンドを実行する。
$ sudo udevadm control --reload && sudo rmmod pcspkr && sudo modprobe pcspkr
これは udev の設定をリロードし、Beep音を司る pcspkr カーネルモジュールを一旦削除してから再度追加するというコマンドである。さっきのルールファイルで設定した udev の条件が ACTION=="add" であるため、 pcspkr の再追加が必要となっている。
…代わりにシステム再起動でもいいが。
これをやってみると、確かに beep コマンドを使ってもエラーは出なかった。
そして input 補助グループは削除しておいた。
$ sudo usermod -rG input $USER
■ エラーは解消した…しかし、音は鳴らず
やっぱり Xfce 上では音が鳴らない。
Xfce4 Terminal でも URxvt でも mlterm でも xterm でもダメ。
色々検索してみたところ、どうも ALSA だけなら alsamixer に Beep の項目が出てきてボリューム調節でき、鳴るが、 PulseAudio を加えるとコントロールを奪うので鳴らないみたいな情報が英語圏にチラホラあった。
といっても、ちゃんと検証可能な公式情報とかじゃないんだけど。
そういえば、Xfce を入れて最初の状態では普通にビープ音が鳴りまくってて「うぜー、、消してー」と思ってた記憶がある。でも Xfce のパネルから音量をコントロールしたいなと思って xfce4-pulseaudio-plugin を入れたあたりから、鳴らなくなって「よくわからないけど、鳴らなくなった。やったー♥」みたいになった気がする。
なんか記憶がおぼろげだが…。
この説、 PulseAudio を消してみれば、証明できそうだけど。
なんかそれは嫌。
もう音声関連は PulseAudio に一任することにしたので。
公式情報でこれを証明する情報がないのか漁ったけど探し方が悪いのか、見当たらない。。
まあ、仕方ない。
ALSA + PulseAudio 環境でなんとかビープ、または それに類似した音を鳴らせるようにするしか無い気がする。
【参考】PulseAudio - ArchWiki
https://wiki.archlinux.jp/index.php/PulseAudio
【参考】PulseAudio 公式サイト
https://www.freedesktop.org/wiki/Software/PulseAudio/
■ X Window System 上で Beep を鳴らすには PulseAudio 側で設定が必要
Wiki を漁っていたら、以下の記事を見つけた。
【参考】X11 ベルイベント - ヒントとテクニック / PulseAudio - ArchWiki
https://wiki.archlinux.jp/index.php/PulseAudio#X11_ベルイベント
なぬ? X11 ベルイベントとな?
【注意】
https://www.x.org/ を漁ったけど、公式情報っぽいのが見つからなかった。なので以下はいくらか推測が混じっている。…たぶん、あってると思うけども。
どうやら ベル=ビープ音 のことで、X11 上では通常なら PCスピーカー・デバイス(Beep音のデバイス)に送られる処理がインタラプト(横取り)されるらしい。
X11 はそれを X11 ベルイベントとして通知を発し、あとは ALSA なり、PulseAudio なりのサウンドを担当するサービスが好きに処理してくださいよ、ということらしい。
つまり、俺ちゃんの場合、X11 上でビープ音を鳴らすには PulseAudio の方でビープ音を設定してやる必要があるようだ。そしてそれは初期状態では設定されていない。←ここが問題。
早速、上述のサイトに従って設定してみる。
その前に音声ファイル bell.oga がちゃんとあるかどうか、確かめておく。
$ ls -l /usr/share/sounds/freedesktop/stereo/bell.oga
-rw-r--r-- 1 root root 8495 5月 22 2022 /usr/share/sounds/freedesktop/stereo/bell.oga
$
あるようだ。では。
$ pactl upload-sample /usr/share/sounds/freedesktop/stereo/bell.oga x11-bell
$ pactl load-module module-x11-bell sample=x11-bell display=$DISPLAY
21
$ xset b 100
$
1 行目では、上記音声ファイルをサンプルとして x11-bell という名前を付け PulseAudio に登録(アップロード)している。
2 行目では、 module-x11-bell という PulseAudio のモジュールを読み込み、先ほどの x11-bell をサンプルに指定している。対象となる画面は $DISPLAY、つまり現在表示中の画面である。1
xset b 100 はベル(ビープ音)の音量を最大値、100%にするという指定。
これを試してみたところ、動作した!!
$ xkbbell
$ echo -e '\a'
でビープ音が鳴る。
シェルで ↓ボタン を押してしまったときなどにも鳴る。
…ただし、beep コマンドでは鳴らなかった。。
どうやら beep コマンドは ALSA や PulseAudio を使わずに直接 pcspkr を使ってビープ音を鳴らそうとするために機能しないのではないかと思われる。
まあ、いいや。
■ 設定ファイルに書き込んで自動的に設定されるようにする
あとはこれを毎回起動時に処理されるようにすればいい。
上述の Arch Linux Wiki では systemd の自動起動スクリプトが紹介されているが…。
PulseAudio のデフォルトの各種設定が書かれたファイルが /etc/pulse/default.pa にあるので、そっちを使うほうがスマートだ。
pactl コマンドは PulseAudio を手動でコントロールするためのコマンドだが、ちょっと書式を変えてやれば default.pa ファイルでも使えるようだ。
詳細は manpage の default.pa(5)、および pulse-cli-syntax(5) に載っている。
これを参考にして、先ほどのコマンドと同等の記述をすると以下の二行のようになる。
load-sample x11-bell /usr/share/sounds/freedesktop/stereo/bell.oga
load-module module-x11-bell sample=x11-bell
これを /etc/pulse/default.pa ファイルの末尾に追加すれば OK。
$ sudo -E vim /etc/pulse/default.pa
$ tail -5 /etc/pulse/default.pa
### 9hnder: add: enable Bell(Beep) sound for X11.
# @see: https://wiki.archlinux.jp/index.php/PulseAudio
# pactl(1), pulse-cli-syntax(5)
load-sample x11-bell /usr/share/sounds/freedesktop/stereo/bell.oga
load-module module-x11-bell sample=x11-bell
これで再起動してみたが、うまく再生された。
もしも /etc/pulse/default.pa を編集するのが嫌なら、ユーザーごとの設定ファイルが ~/.config/pulse/default.pa にあるようなので、こちらで設定してもよいだろう。
音声ファイルも好きに変えることができる。
### 9hnder: create: user definition file.
#
# This file is part of PulseAudio.
# override on /etc/pulse/default.pa file.
#
# 1st, include system config file.
.include /etc/pulse/default.pa
### 9hnder: add: enable Bell(Beep) sound for X11.
# @see: https://wiki.archlinux.jp/index.php/PulseAudio
# pactl(1), default.pa(5), pulse-cli-syntax(5)
#load-sample x11-bell /usr/share/sounds/freedesktop/stereo/bell.oga
load-sample x11-bell /home/9hnder/Music/SE/SE48_Pyui.WAV
load-module module-x11-bell sample=x11-bell
# vim:set ts=4 tw=0 ff=unix ft=conf : This is vim modeline #
コツは最初に .include 命令を使ってシステムのデフォルトの default.pa を呼ぶこと。
これをやっておかないと /etc/pulse/default.pa は読み込まれないので、そこで設定されているデフォルトの設定の数々が読み込まれず、PulseAudio がうまく起動しない。
その後に上述のファイルで設定された内容を上書きしたい項目や、追加したい設定項目を書いてゆく。
以上。
■ エピローグ:当初の目的は達成できたの?
イエス。
当初の目的だったクソ長いコマンドが終了したときに音声で知らせるのは以下のようになった。
$ sleep 10; xkbbell
『sleep 10』の部分は『クソ長いコマンド』に置き換えること。
…ただ、ビープ音が小さく、短いので割とわかりにくかった。。。
大きい音や長い音にすることもできたが、そうすると操作を誤ったりしてビープ音が鳴る場面で「うるせーーー!」となってしまう。
ので、結局 mocp コマンドに変えて長めの音声ファイルを流すようにした。
$ alias overnotice='mocp -l ~/Music/MISSION_COMPLETE.wav'
$ sleep 10; overnotice
mocp コマンドは MOC (music on console) と呼ばれる一種の音楽プレーヤーである。
コンソール上からコマンドラインで指定して音声ファイルの再生操作ができる。
2024現在 moc-pulse パッケージが AUR にあり、それに含まれるコマンドとなっている。
依存パッケージをいくつか入れることで、 ogg, wav, mp3, aac ... などなど様々な音声ファイルを再生できる。パッケージ名にある通り、 PulseAudio にも対応している。
最初 audacious -Hq でやろうとしたが、なぜか音声ファイルが短い曲だと、途中で切られてしまってうまくいかなかった。長い曲だと起こらないので、たぶんバグっぽい。
なお、mocp はサーバープログラムを常住させて、そこに命令を発して音声ファイルを再生するタイプのプレーヤーである。このため、
$ mocp -S
として事前にサーバーを起動しておく必要がある。
(もしくは、単に mocp として TUI 画面を起動するか)
これを自動起動するようにしておくと良い。
俺ちゃんの Xfce @ Arch Linux の場合、「メニュー」>「設定マネージャー」>「セッションと起動」を選択して「自動開始アプリケーション」に登録しておいた。
トリガーは「ログイン時」で。
すると、下記の設定ファイルが生成された。
$ cat .config/autostart/mocp.desktop
[Desktop Entry]
Encoding=UTF-8
Version=0.9.4
Type=Application
Name=mocp
Comment=Music of Console server.
Exec=mocp -S
OnlyShowIn=XFCE;
RunHook=0
StartupNotify=false
Terminal=false
Hidden=false
$
これでいつでも overnotice コマンドを打てば通知音が鳴る!
なお、コマンドを打ってしまってからクソ長いことに気づいてしまったとき、後から通知音を鳴らしたかった… orz
という場合もあるだろう。
そんなときは、CTRL+Z キーでジョブを一旦サスペンドさせて、fg コマンドと overnotice を使うことで終了時に通知音を鳴らすことができる。
$ sleep 30
^Z
[1]+ 停止 sleep 30
$ fg; overnotice
sleep 30
$
fsck とか make とかをやっていて、実行時間がクソ長いときに超便利。
ただし、コマンドを && や || や ; などで複数つなげていると、意図したとおりに動かないので注意。
例えば、次のようにすると echo コマンドがちゃんと実行されない。
$ make && echo "正常に終わったお\(^o^)/" || echo "失敗しましゅた(;´Д`)"
^Z
[1]+ 停止 make
失敗しましゅた(;´Д`)
$ fg; overnotice
make
$
これは CTRL+Z キーでジョブをサスペンドさせた瞬間、コマンドは 終了ステータス=148 で終了したと見なされるため。
因みにこの 148 とは何かというと、キーボードからの何らかの割り込みシグナルを受け取ったという値=128 + CTRL+Z で発行したシグナル SIGTSTP の番号=20 の合計値である。2
【参考】manpages signal(7), jobs(1p)
【参考】command line - Why does bash set $? (exit status) to non-zero on Ctrl-C or Ctrl-Z? - Unix & Linux Stack Exchange
https://unix.stackexchange.com/questions/251996/why-does-bash-set-exit-status-to-non-zero-on-ctrl-c-or-ctrl-z
■ おまけ:リモート環境だと鳴らないのをなんとかする
リモートのサーバーなどにログインしていると、上述の overnotice エイリアスは無いので当然だけど通知音が使えない。というか、 mocp も無いし、X-Window System でもないコンソール Only な環境だったりするので、音自体が鳴らないことがほとんど。
beep コマンドは動作することもあるが、サーバー機器と物理的に離れていたら、鳴っても聞こえない…。
そんなとき、唯一動作するのが echo -e '\a' でのベル。
これは手元の仮想端末に対して「ベルを鳴らせ」という命令を送るコマンドなのでリモート側が何であろうが関係なく手元のマシンのベルが鳴る。
しかし、短かすぎて聞こえないという問題が再び浮上してくる。
そこで何度かベルを鳴らして、無理やり気づかせるという力技を思いついた。
Bash シェルの場合は以下のようにする。
$ alias overnotice="for i in {1..5}; do echo -ne '\a'; sleep 1; done"
$ sleep 5; overnotice
この方法ではベルを 5 回鳴らす。
for 文を使って 5回ループを回し、その中で echo コマンドを呼んでいる。
sleep 1 を入れているのは、そのままだと あまりにもループが速すぎて、ベルが何度も鳴らないため。 X11 ベルイベント に設定している音声ファイルの長さよりも長く sleep する必要があるだろう。
以上。 🫶 Chimi-Changa!
-
module-x11-bell モジュールについては以下を参照。
【参考】module-x11-bell / Modules – PulseAudio
https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/Modules/#module-x11-bell ↩ -
Bash の場合、ソースコードの sigint_sighandler() 関数で定義されている。
以下の付近。
http://git.savannah.gnu.org/cgit/bash.git/tree/sig.c#n675 ↩