C语言控制树莓派系统音量

树莓派 杨博, 卫 657次浏览 0个评论

本来晚上是不想发贴的,但是这个问题搞了整整一天才算解决,我认为必须要记录下来。

想用树莓派搞个语音报时,想在报时的时候调大系统音量,奈何这个问题怎么也搞不定。。。于是一番折腾开始了。

一.配置环境

系统是Raspbian Jessie,声音架构是默认的ALSA,编译器gcc,USB声卡(树莓派自带的音频接口噪声太大,谁用谁知道 :???:

二.更改默认声卡

根据我的前一篇帖子将默认声卡改为USB声卡,或者直接在/etc/modules里不让snd_BCM2835加载。

三.读出音量

改音量之前最好知道目前的音量是多少
参考帖子http://blog.yjl.im/2009/05/get-volumec.htmldwm.suckless.org/dwmstatus/volume.c(只能找到这两个了)

先安装必要的C语言库

apt-get install libasound2-dev

原封不动的copy第一个帖子中的源码,保存为getvol.c
compile with
gcc -o getvol ./get.c -lasound
运行一下,报错“Mixer simple element register error”(不然也没有这篇文章了)

再原封不动的copy后者源码,保存为getvol.c
再compile with
gcc -o getvol ./get.c -lasound
当然还是报错,那应该是我本地配置的问题了。卡了很长时间没搞好,Google了好长时间,甚至退回默认声卡,依然无解。

四.转机

此处省略我Google了无数帖子。。。刚才才发现,貌似声卡分为Master和PCM两种,注意到这两个程序都有“Master”字样,但是通过运行

amixer

2016-08-17

发现我的USB声卡的声音输出名称是“Speaker” :!: ,果断将源文件里面的Master全部换成Speaker。(不同的设备声音输入源的名称可能不一样,比如树莓派自带的音频输出,就是”PCM”)。

看看问题是否解决,重新编译,运行。结果第一个程序依然不行,但是第二个已经有输出了,”Volume set to -2783%”,excuse me?负的?

五.改进

发现输出是这个语句

printf("%s %ld%%\n", "Volume set to", (vol * 100) / max);

怎么会有负数?
添加两句

printf("%s %ld%%\n", "Max Volume is",  max);
printf("%s %ld%%\n", "Min Volume is",  min);

结果发现,音量最大值是200,最小值是-3700多,我去,这树莓派果然不走寻常路啊。
改输出为

printf("%s %ld%%\n", "Volume set to", (vol-min) * 100 / max-min);

这样就能显示正常的百分比了。

六.控制音量

太晚了,我还要洗澡洗衣服,我就直接贴代码
http://stackoverflow.com/questions/6787318/set-alsa-master-volume-from-c-code

这个代码没有错误提示,姑且看看能不能用吧,保存为setvol.c,改Master为Speaker,再加个主函数,gcc -o setvol ./set.c -lasound。然后再运行./getvol,果然管用。

七.最后贴我的代码

前面两个代码是供大家测试的,下面贴出的代码是上面的整合,但是没有错误信息,请悉知。
国二C差点不及格,各位看官见笑了,写得不好多包涵。

注意:用amixer检查你的声音输出源,自行替换代码里面的”Speaker”字段。

#include <alsa/asoundlib.h>
#include <stdio.h>
#include <stdlib.h>
void set_vol(long volume);
long get_vol();
void volup();
void voldown();
void main(){
    char a;
    while(1){
        printf("Input + or - volume\n");
        a=getchar();
        if(a=='+')volup();
        if(a=='-')voldown();}
}
void set_vol(long volume)
{
    long min, max;
    snd_mixer_t *handle;
    snd_mixer_selem_id_t *sid;
    const char *card = "default";
    const char *selem_name = "Speaker";
    snd_mixer_open(&handle, 0);
    snd_mixer_attach(handle, card);
    snd_mixer_selem_register(handle, NULL, NULL);
    snd_mixer_load(handle);
    snd_mixer_selem_id_alloca(&sid);
    snd_mixer_selem_id_set_index(sid, 0);
    snd_mixer_selem_id_set_name(sid, selem_name);
    snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
    snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
    snd_mixer_selem_set_playback_volume_all(elem, volume*(max-min)/100+min);
    snd_mixer_close(handle);
}
long get_vol() {
    snd_mixer_t *handle = NULL;
    snd_mixer_elem_t *elem = NULL;
    snd_mixer_selem_id_t *s_elem = NULL;
    long int vol, max, min;

    if (0 < (snd_mixer_open(&handle, 0))) {
        goto error;
    }

    if (0 < (snd_mixer_attach(handle, "default"))) {
        goto error;
    }

    if (0 < (snd_mixer_selem_register(handle, NULL, NULL))) {
        goto error;
    }

    if (0 < (snd_mixer_load(handle))) {
        goto error;
    }

    snd_mixer_selem_id_malloc(&s_elem);
    if (NULL == s_elem) {
        goto error;
    }

    snd_mixer_selem_id_set_name(s_elem, "Speaker");
    if (NULL == (elem = snd_mixer_find_selem(handle, s_elem))) {
        goto error;
    }

    /* Use `set' to change the current volume value */
    if (0 < (snd_mixer_selem_get_playback_volume(elem, 0, &vol))) {
        goto error;
    }
    snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
    snd_mixer_selem_id_free(s_elem);
    snd_mixer_close(handle);
    // printf("%s %ld%%\n", "Volume set to", (vol-min) * 100 / max-min);
    return (vol-min)*100/(max-min);
error:
    if (NULL != s_elem) {
        snd_mixer_selem_id_free(s_elem);
        s_elem = NULL;
    }
    if (NULL != handle) {
        snd_mixer_close(handle);
        handle = NULL;
    }
}
void volup(){
    long volume=get_vol();
    volume+=10;
    if(volume>100)volume=100;
    set_vol(volume);
    printf("system volume set to %d%\n",volume);
    fflush(stdout);
}
void voldown(){
    long volume=get_vol();
    volume-=10;
    if(volume<0)volume=0;
    set_vol(volume);
    printf("system volume set to %d%\n",volume);
    fflush(stdout);
}


保存为volume.c,然后编译gcc -o volume ./volume.c -lasound,运行看看效果吧。
可能会有人不理解我为什么费这么大劲用C语言来控制音量,其实我心中一直有一个把树莓派建设为宿舍物联网中心的想法,等到后面几篇博文我会叫大家用红外遥控来控制你的树莓派,今天这篇文章就当时伏笔了。


本文版权:霜之哀伤 转载请注明C语言控制树莓派系统音量
喜欢 (3)or分享 (0)
杨博, 卫
关于作者:
喜欢折腾路由器,懂一点Linux,最近正在学习树莓派...
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址