注意 ALSA/Pulseaudio 中的音量变化
Posted
技术标签:
【中文标题】注意 ALSA/Pulseaudio 中的音量变化【英文标题】:Watch for volume changes in ALSA/Pulseaudio 【发布时间】:2016-04-28 11:13:21 【问题描述】:如何在默认声卡的主通道上收听音量变化?我想通过 dbus 或回调或音量发生变化的方式收到通知。
我尝试过查看 ALSA 和 PulseAudio API,它们似乎只允许您设置和获取音量,但不能收听音量变化。
任何编程语言都可以。
【问题讨论】:
【参考方案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
以上是关于注意 ALSA/Pulseaudio 中的音量变化的主要内容,如果未能解决你的问题,请参考以下文章
libeXosip2 + libosip2 + ortp + mediastream2 + alsa-lib + pulseaudio