【问题标题】:Watch for volume changes in ALSA/Pulseaudio注意 ALSA/Pulseaudio 中的音量变化
【发布时间】:2016-04-28 11:13:21
【问题描述】:

如何在默认声卡的主通道上收听音量变化?我想通过 dbus 或回调或音量发生变化的方式收到通知。

我尝试过查看 ALSA 和 PulseAudio API,它们似乎只允许您设置和获取音量,但不能收听音量变化。

任何编程语言都可以。

【问题讨论】:

    标签: alsa pulseaudio


    【解决方案1】:

    编辑:在第二个示例中,当音量低于 5% 或高于 100% 时,不会为我生成事件。据我所知,第一个示例运行良好。

    pactl subscribe 将在音量变化时打印出有关接收器的数据。我现在正在做的是将输出传送到一个将运行脚本的小型 C 程序。

    运行.sh:

    pactl subscribe | grep --line-buffered "sink" | ./prog
    

    或针对特定的接收器,例如3:

    pactl subscribe | grep --line-buffered "sink #3" | ./prog
    

    prog.c:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char* argv[]){
        while(1){
            while(getchar() != '\n');
            system("./volume_notify.sh");
        }
    }
    

    当接收器的音量改变时,pactl 会打印一行,这将导致程序运行脚本。

    -或-

    这是一个基于amixer monitor 的示例,由 CL 引用。 每次音量变化时,while 循环都会迭代,所以把你的回调放在那里。

    #include <stdio.h>
    #include <alsa/asoundlib.h>
    
    #define MAX_CARDS 256
    
    int monitor_native(char const *name);
    int open_ctl(const char *name, snd_ctl_t **ctlp);
    void close_all(snd_ctl_t* ctls[], int ncards);
    
    int main(int argc, char* argv[]){
    
        const char *ctl_name = "hw:0";
    
        while(monitor_native(ctl_name) == 1){
            //volume has been changed, do something
            system("~/.volume_notify.sh");
        }
    
        return 0;
    }
    
    int monitor_native(char const *name) {
        snd_ctl_t *ctls[MAX_CARDS];
        int ncards = 0;
        int i, err = 0;
    
        if (!name) {
            int card = -1;
            while (snd_card_next(&card) >= 0 && card >= 0) {
                char cardname[16];
                if (ncards >= MAX_CARDS) {
                    fprintf(stderr, "alsactl: too many cards\n");
                    close_all(ctls, ncards);
                    return -E2BIG;
                }
                sprintf(cardname, "hw:%d", card);
                err = open_ctl(cardname, &ctls[ncards]);
                if (err < 0) {
                    close_all(ctls, ncards);
                    return err;
                }
                ncards++;
            }
        } else {
            err = open_ctl(name, &ctls[0]);
            if (err < 0) {
                close_all(ctls, ncards);
                return err;
            }
            ncards++;
        }
    
        for (;ncards > 0;) {
            pollfd* fds = new pollfd[ncards];
    
            for (i = 0; i < ncards; i++) {
                snd_ctl_poll_descriptors(ctls[i], &fds[i], 1);
            }
    
            err = poll(fds, ncards, -1);
            if (err <= 0) {
                err = 0;
                break;
            }
    
            for (i = 0; i < ncards; i++) {
                unsigned short revents;
                snd_ctl_poll_descriptors_revents(ctls[i], &fds[i], 1, &revents);
                if (revents & POLLIN) {
                    snd_ctl_event_t *event;
                    snd_ctl_event_alloca(&event);
    
                    if (snd_ctl_read(ctls[i], event) < 0) {
                        continue;
                    }
                    if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM) {
                        continue;
                    }
    
                    unsigned int mask = snd_ctl_event_elem_get_mask(event);
                    if (mask & SND_CTL_EVENT_MASK_VALUE) {
                        close_all(ctls, ncards);
                        return 1;
                    }
                }
            }
        }
    
        close_all(ctls, ncards);
        return 0;
    }
    
    int open_ctl(const char *name, snd_ctl_t **ctlp) {
        snd_ctl_t *ctl;
        int err;
    
        err = snd_ctl_open(&ctl, name, SND_CTL_READONLY);
        if (err < 0) {
            fprintf(stderr, "Cannot open ctl %s\n", name);
            return err;
        }
        err = snd_ctl_subscribe_events(ctl, 1);
        if (err < 0) {
            fprintf(stderr, "Cannot open subscribe events to ctl %s\n", name);
            snd_ctl_close(ctl);
            return err;
        }
        *ctlp = ctl;
        return 0;
    }
    
    void close_all(snd_ctl_t* ctls[], int ncards) {
        for (ncards -= 1; ncards >= 0; --ncards) {
            snd_ctl_close(ctls[ncards]);
        }
    }
    

    【讨论】:

      【解决方案2】:

      这可以通过 ALSA API 实现。

      如果您有控制设备,请致电snd_ctl_subscribe_events() 以启用事件。 然后使用snd_ctl_read()读取事件;要等待他们,请使用阻塞模式或poll()。 如果事件的类型为SND_CTL_EVENT_ELEM,并且其事件位掩码包含SND_CTL_EVENT_MASK_VALUE,则该元素的值已更改。

      有关示例,请参阅 the implementation of amixer monitor

      【讨论】:

      • 如果有人想知道这变成了一个 node.js 插件here
      【解决方案3】:

      类似于@sealj553 的回答,但不需要 C 程序:

      pactl subscribe | grep --line-buffered "sink" | xargs -n1 ./volume_notify.sh
      

      【讨论】:

      • 这个单行代码几乎可以很好地工作,但它会在每个事件中多次调用volume_notify.sh。我建议改用这个:shell pactl subscribe | grep --line-buffered "sink" | while read -r UNUSED_LINE; do ./volume_notify.sh; done
      猜你喜欢
      • 2016-01-12
      • 2016-03-13
      • 1970-01-01
      • 2015-05-28
      • 2012-01-09
      • 2017-08-03
      • 2020-08-27
      • 2017-11-20
      • 2011-05-04
      相关资源
      最近更新 更多