开发者

Get master sound volume in C in Linux

开发者 https://www.devze.com 2023-04-10 13:51 出处:网络
I\'m trying to retrieve (and probably later set) the master sound volume in Linux. I\'m using PulseAudio, but ideally it should work for ALSA too.

I'm trying to retrieve (and probably later set) the master sound volume in Linux. I'm using PulseAudio, but ideally it should work for ALSA too.

I found this very helpful post on how to set the volume, and from that I was able to deduce the existence of snd_mixer_selem_get_playback_volume() to retrieve the current setting. However on my system, this seems to be giving me wrong readings - where mixer programs show 100%, this tops out at about 66%.

If I open pavucontrol I can see the volume for this output device matches the readings I get from here, so I assume it's giving me the h开发者_开发问答ardware volume setting, not the global master volume that I wanted.


Compile the below code with gcc audio_volume.c -o audio_volume -lasound or altenatively with gcc audio_volume.c -o audio_volume_oss -DOSSCONTROL. OSS version is written very crudely and does not give precise results. Setting volume to 100 sets 97% volume in my system. ALSA version works for me and PulseAudio in openSUSE. Needs some cleanup and love but I hope it is what you need, I grant the permission to use it on the WTFPL license.

#include <unistd.h>
#include <fcntl.h>

#ifdef OSSCONTROL
#define MIXER_DEV "/dev/dsp"

#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <stdio.h>
#else
#include <alsa/asoundlib.h>
#endif


typedef enum {
    AUDIO_VOLUME_SET,
    AUDIO_VOLUME_GET,
} audio_volume_action;

/*
  Drawbacks. Sets volume on both channels but gets volume on one. Can be easily adapted.
 */
int audio_volume(audio_volume_action action, long* outvol)
{
#ifdef OSSCONTROL
    int ret = 0;
    int fd, devs;

    if ((fd = open(MIXER_DEV, O_WRONLY)) > 0)
    {
        if(action == AUDIO_VOLUME_SET) {
            if(*outvol < 0 || *outvol > 100)
                return -2;
            *outvol = (*outvol << 8) | *outvol;
            ioctl(fd, SOUND_MIXER_WRITE_VOLUME, outvol);
        }
        else if(action == AUDIO_VOLUME_GET) {
            ioctl(fd, SOUND_MIXER_READ_VOLUME, outvol);
            *outvol = *outvol & 0xff;
        }
        close(fd);
        return 0;
    }
    return -1;;
#else
    snd_mixer_t* handle;
    snd_mixer_elem_t* elem;
    snd_mixer_selem_id_t* sid;

    static const char* mix_name = "Master";
    static const char* card = "default";
    static int mix_index = 0;

    long pmin, pmax;
    long get_vol, set_vol;
    float f_multi;

    snd_mixer_selem_id_alloca(&sid);

    //sets simple-mixer index and name
    snd_mixer_selem_id_set_index(sid, mix_index);
    snd_mixer_selem_id_set_name(sid, mix_name);

        if ((snd_mixer_open(&handle, 0)) < 0)
        return -1;
    if ((snd_mixer_attach(handle, card)) < 0) {
        snd_mixer_close(handle);
        return -2;
    }
    if ((snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
        snd_mixer_close(handle);
        return -3;
    }
    ret = snd_mixer_load(handle);
    if (ret < 0) {
        snd_mixer_close(handle);
        return -4;
    }
    elem = snd_mixer_find_selem(handle, sid);
    if (!elem) {
        snd_mixer_close(handle);
        return -5;
    }

    long minv, maxv;

    snd_mixer_selem_get_playback_volume_range (elem, &minv, &maxv);
    fprintf(stderr, "Volume range <%i,%i>\n", minv, maxv);

    if(action == AUDIO_VOLUME_GET) {
        if(snd_mixer_selem_get_playback_volume(elem, 0, outvol) < 0) {
            snd_mixer_close(handle);
            return -6;
        }

        fprintf(stderr, "Get volume %i with status %i\n", *outvol, ret);
        /* make the value bound to 100 */
        *outvol -= minv;
        maxv -= minv;
        minv = 0;
        *outvol = 100 * (*outvol) / maxv; // make the value bound from 0 to 100
    }
    else if(action == AUDIO_VOLUME_SET) {
        if(*outvol < 0 || *outvol > VOLUME_BOUND) // out of bounds
            return -7;
        *outvol = (*outvol * (maxv - minv) / (100-1)) + minv;

        if(snd_mixer_selem_set_playback_volume(elem, 0, *outvol) < 0) {
            snd_mixer_close(handle);
            return -8;
        }
        if(snd_mixer_selem_set_playback_volume(elem, 1, *outvol) < 0) {
            snd_mixer_close(handle);
            return -9;
        }
        fprintf(stderr, "Set volume %i with status %i\n", *outvol, ret);
    }

    snd_mixer_close(handle);
    return 0;
#endif
    }

int main(void)
{
    long vol = -1;
    printf("Ret %i\n", audio_volume(AUDIO_VOLUME_GET, &vol));
    printf("Master volume is %i\n", vol);

    vol = 100;
    printf("Ret %i\n", audio_volume(AUDIO_VOLUME_SET, &vol));

    return 0;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号