我知道如何敲钟echo -ne '\a'
(或者,如果您不介意依赖外部命令,那就更好了:)tput bel
。然而,它实际上做的是向输出发送一些特殊代码;然后,如果该输出恰好是一个终端,则终端解释代码并响铃。
我的问题是我想从后台脚本发出警告噪音,其输出不会被任何终端读取。我可能会播放一些和声或类似的.wav
文件paplay
,但我更喜欢那种简单、刺耳的哔哔声。而且我不想依赖外部程序。
所以,问题:
- 终端发出的铃声是否在某些方面是特殊的(例如使用专用硬件),还是像其他声音文件一样?
- 如何重现那种声音?
在研究了程序的源代码之后
beep
,我想出了一个最小的 C 程序来敲钟。它使用 PC 扬声器,Linux 内核将其作为通常命名的 evdev 设备提供
/dev/input/by-path/platform-pcspkr-event-spkr
(指向实际位置的符号链接)。有一个事件以给定频率打开扬声器(即开始产生声音),另一个事件关闭扬声器(即停止声音)。我们只需发送第一个事件,然后休眠所需的持续时间,然后发送第二个事件。
这需要对设备的写访问权限,可以通过 udev 规则、程序的 setuid 位或以 root 身份运行程序来设置,如
beep
. 而且,这不能控制音量。不知何故,xset
程序(来自 X 服务器套件)和终端仿真器没有这些限制。在 C 中
(为了简洁明了,上面的源代码跳过了所有的安全检查(检查文件是否存在,检查
open
成功,检查文件描述符是字符设备,检查频率和持续时间是允许范围内的整数,检查write
成功,检查是否nanosleep
成功……)。在实际应用中,应该解决这个问题。)在 shell 脚本中
上面的C程序可以用来观察事件的形状;然后,可以从 shell 脚本编写相同的事件。不幸的是,二进制格式很可能是特定于平台的。在我的带有 Linux 内核 5.2 的 x86-64 机器上,声音事件具有以下结构:
type
具有值的字段EV_SND
)code
具有值的字段SND_TONE
)value
)这个二进制结构可以用 Perl 和函数
pack
. 二进制整数也可以使用纯 Bash输出,但这更冗长。如果您的脚本以 root 身份运行,您可以简单地执行以下操作: