Получить основную громкость звука в C в Linux

Я пытаюсь получить (и, возможно, позже установить) основную громкость звука в Linux. Я использую PulseAudio, но в идеале он должен работать и с ALSA.

Я нашел этот очень полезный пост о том, как установить громкость, и из этого я смог сделать вывод о существовании snd_mixer_selem_get_playback_volume() для получения текущей настройки. Однако в моей системе это, кажется, дает мне неправильные показания - там, где программы микшера показывают 100%, это достигает максимума около 66%.

Если я открою pavucontrol, я увижу, что громкость для этого устройства вывода соответствует показаниям, которые я получаю отсюда, поэтому я предполагаю, что это дает мне аппаратную настройку громкости, а не глобальную основную громкость, которую я хотел.


person Rena    schedule 05.10.2011    source источник


Ответы (1)


Скомпилируйте приведенный ниже код с gcc audio_volume.c -o audio_volume -lasound или альтернативно с gcc audio_volume.c -o audio_volume_oss -DOSSCONTROL. Версия OSS написана очень грубо и не дает точных результатов. Установка громкости на 100 устанавливает громкость 97% в моей системе. У меня работает версия ALSA и PulseAudio в openSUSE. Требуется некоторая очистка и любовь, но я надеюсь, что это то, что вам нужно, я даю разрешение на его использование по лицензии WTFPL. .

#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;
}
person RushPL    schedule 05.10.2011
comment
Помимо дополнительной проверки ошибок, похоже, что она делает то же самое, что и код, на который я ссылался. - person Rena; 06.10.2011
comment
Ну, это работает для меня, извините, что это не сработало для вас. Я не использую более продвинутые функции PulseAudio. Я думаю, что PulseAudio позволяет внутренне увеличивать громкость более чем на 100% (умножая сэмплы на >1), так что, может быть, вам стоит взглянуть на PulseAudio API для регулировки громкости? - person RushPL; 06.10.2011
comment
RushPL Как изменить громкость правого и левого каналов отдельно. Я попытался вызвать snd_mixer_selem_set_playback_volume для 0 и 1 отдельно, но изменил всю основную громкость. - person Ibrar Ahmed; 07.05.2014
comment
Любой пример PulseAudio для изменения громкости левого и правого каналов? - person Ibrar Ahmed; 07.05.2014
comment
см. место, где snd_mixer_selem_set_playback_volume называется. Второй параметр — это канал. В коде звонки осуществляются с 0 и 1 канала. - person RushPL; 08.05.2014
comment
Итак, вы знаете, что любой и весь код, опубликованный на любом сайте Stack Exchange, автоматически лицензируется в соответствии с лицензией по их выбору, а не по вашей. Разумеется, вы можете перелицензировать его, но конкретный источник на Stack Exchange поставляется с этой лицензией. - person Fund Monica's Lawsuit; 11.06.2016
comment
1) Закон не работает в обратном направлении. 2) Этот код находится под лицензией WTFPL, как указано в моем ответе. - person RushPL; 12.06.2016
comment
@RushPL Можно ли как-то распечатать объем в секунду без повторного вызова snd_mixer_open и snd_mixer_close? Вызов этих функций в цикле медленно съедает мою память. - person JiaHao Xu; 14.02.2021
comment
Не беспокойтесь, я понял — мне просто нужно вызвать snd_mixer_handle_events(handle) в цикле. - person JiaHao Xu; 14.02.2021