AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 531324
Accepted
Maëlan
Maëlan
Asked: 2019-07-22 09:41:19 +0800 CST2019-07-22 09:41:19 +0800 CST 2019-07-22 09:41:19 +0800 CST

无终端敲响电脑铃声

  • 772

我知道如何敲钟echo -ne '\a'(或者,如果您不介意依赖外部命令,那就更好了:)tput bel。然而,它实际上做的是向输出发送一些特殊代码;然后,如果该输出恰好是一个终端,则终端解释代码并响铃。

我的问题是我想从后台脚本发出警告噪音,其输出不会被任何终端读取。我可能会播放一些和声或类似的.wav文件paplay,但我更喜欢那种简单、刺耳的哔哔声。而且我不想依赖外部程序。

所以,问题:

  1. 终端发出的铃声是否在某些方面是特殊的(例如使用专用硬件),还是像其他声音文件一样?
  2. 如何重现那种声音?
bell
  • 2 2 个回答
  • 699 Views

2 个回答

  • Voted
  1. Best Answer
    Maëlan
    2019-08-31T10:28:13+08:002019-08-31T10:28:13+08:00

    在研究了程序的源代码之后beep,我想出了一个最小的 C 程序来敲钟。

    它使用 PC 扬声器,Linux 内核将其作为通常命名的 evdev 设备提供/dev/input/by-path/platform-pcspkr-event-spkr(指向实际位置的符号链接)。

    有一个事件以给定频率打开扬声器(即开始产生声音),另一个事件关闭扬声器(即停止声音)。我们只需发送第一个事件,然后休眠所需的持续时间,然后发送第二个事件。

    这需要对设备的写访问权限,可以通过 udev 规则、程序的 setuid 位或以 root 身份运行程序来设置,如beep. 而且,这不能控制音量。不知何故,xset程序(来自 X 服务器套件)和终端仿真器没有这些限制。

    在 C 中

    #include <linux/input.h>  // struct input_event
    #include <fcntl.h>  // open
    #include <unistd.h>  // write, close
    #include <time.h>  // nanosleep
    
    /* frequency (Hz): */
    static unsigned int const default_frequency = 440;
    /* duration (ms): */
    static unsigned int const default_duration = 200;
    /* file path of PC speaker: */
    static char const * const default_device = "/dev/input/by-path/platform-pcspkr-event-spkr";
    
    void start_beep(int fd, int freq)
    {
        struct input_event e = { 0 };
        e.type = EV_SND;
        e.code = SND_TONE;
        e.value = freq;
        write(fd, &e, sizeof(e));
    }
    
    void stop_beep(int fd)
    {
        start_beep(fd, 0);
    }
    
    void sleep_ms(unsigned int ms)
    {
        struct timespec duration =
            { .tv_sec  = ms / 1000U,
              .tv_nsec = (long)(ms % 1000UL * 1000UL * 1000UL) };
        nanosleep(&duration, NULL);
    }
    
    int main(void)
    {
        int fd = open(default_device, O_WRONLY);
        start_beep(fd, default_frequency);
        sleep_ms(default_duration);
        stop_beep(fd);
        close(fd);
        return 0;
    }
    

    (为了简洁明了,上面的源代码跳过了所有的安全检查(检查文件是否存在,检查open成功,检查文件描述符是字符设备,检查频率和持续时间是允许范围内的整数,检查write成功,检查是否nanosleep成功……)。在实际应用中,应该解决这个问题。)

    在 shell 脚本中

    上面的C程序可以用来观察事件的形状;然后,可以从 shell 脚本编写相同的事件。不幸的是,二进制格式很可能是特定于平台的。在我的带有 Linux 内核 5.2 的 x86-64 机器上,声音事件具有以下结构:

    • 16 字节:零
    • 2 字节,小端:0x12(type具有值的字段EV_SND)
    • 2 个字节,小端:0x2(code具有值的字段SND_TONE)
    • 4 个字节,little-endian:以 Hz 为单位的频率,或 0 停止声音(字段value)

    这个二进制结构可以用 Perl 和函数pack. 二进制整数也可以使用纯 Bash输出,但这更冗长。

    # frequency (Hz):
    default_frequency=440
    # duration (ms):
    default_duration=200
    # file path of PC speaker:
    default_device='/dev/input/by-path/platform-pcspkr-event-spkr'
    
    function start_beep()
    {
        declare -i freq="$1"
        perl -e 'print pack("qqssl", 0, 0, 0x12, 2, '$freq')'
    }
    
    function stop_beep()
    {
        start_beep 0
    }
    
    # USAGE: beep [FREQUENCY [DURATION [DEVICE]]]
    function beep()
    {
        declare -i freq="${1-$default_frequency}"
        declare -i dur="${2-$default_duration}"
        declare dev="${3-$default_device}"
        # convert milliseconds to seconds
        declare dur_sec=$( printf '%u.%03u' $(( dur / 1000 )) $(( dur % 1000 )) )
        # write the sound events
        {
            start_beep $freq
            sleep $dur_sec
            stop_beep
        } >> "$dev"
    }
    
    • 3
  2. telcoM
    2019-08-31T10:52:02+08:002019-08-31T10:52:02+08:00

    如果您的脚本以 root 身份运行,您可以简单地执行以下操作:

    echo -ne '\a' >/dev/console
    
    • 1

相关问题

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve