5

I can't seem to be able to set the volume in dB on my Machine.

I am running a Ubuntu 13.04 System in a VirtualBox (for development).

I followed these instructions to set the volume as scalar and to control the mute state and everything works just fine.

But when I try to set it in dB with this function

snd_mixer_selem_set_playback_dB_all(elem, volume, 0)

or with that function

snd_mixer_selem_set_playback_dB(elem, chn, volume, 0)

it always fails with the error code -22 (Invalid argument)

Before you ask: I already tried out a wide range of volume levels to set (from -20000 to 20000) and I also changed the last parameter to the three defined values [-1, 0, 1]

Here is my complete code for testing right now:

#include <alsa/asoundlib.h>
#include <stdbool.h>
#include <stdlib.h>

void SetAlsaMasterVolume(long volume) {
    long min, max;
    snd_mixer_t *handle;
    snd_mixer_selem_id_t *sid;
    const char *card = "default";
    const char *selem_name = "Master";
    int x, mute_state;
    long i;
    
    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 / 100);

    snd_mixer_selem_get_playback_dB_range(elem, &min, &max);
    printf("min: %ld, max: %ld\n", min, max);

    snd_mixer_selem_channel_id_t chn;
    for (chn = 0; chn < 32; chn++) {
        for (i = -1000; i <= 1000; i++)
            if (x = snd_mixer_selem_set_playback_dB(elem, chn, i, 0)) {
                ;//printf("%d: %d %s\n", chn, x, snd_strerror(x));
            } else {
                printf("Volume successfully set in dB!\n");
            }
    }

    if (snd_mixer_selem_has_playback_switch(elem)) {
        snd_mixer_selem_set_playback_switch_all(elem, true);
        snd_mixer_selem_get_playback_switch(elem, 0, &mute_state);
        if (!mute_state) {
            printf("System Muted.\n");
        } else {
            printf("System unmuted.\n");
        }
    }

    snd_mixer_close(handle);
}

int main() {
    SetAlsaMasterVolume(100);
    return 0;
}

As you see I in this example I try to set every channel by myself, it's the same thing "snd_mixer_selem_set_playback_dB_all" does, but I wanted to try it to see if I get different results.

The output of this example looks like this:

min: 15774463, max: 191

System unmuted.

Befor I widened the range of dB values I tested the output was:

min: 1, max: 191

Maybe I am missing something here... I hope you can help me out!

edit:

To compile this script I use the following command:

gcc test.c -lasound -o test

Community
  • 1
  • 1
  • 15774463 is obviously wrong. Check the return values of all functions. – CL. Oct 21 '13 at 11:38
  • I checked them already. But **snd_mixer_selem_get_playback_volume_range** for example behaves normal (min: 0, max: 65536) As I mentioned, the value for min was 1 before until I tried to set extremely high values like 200000 as volume level. I don't know how to reset this. A reboot doesn't fix it. –  Oct 21 '13 at 12:40
  • I also checked the value of **snd_mixer_selem_get_playback_dB** and it also always returns the value 15774463, no matter what volume I set. –  Oct 21 '13 at 13:00
  • What is the *return value* (not the value of an output parameter) of the call to `snd_mixer_selem_get_playback_dB_range`? – CL. Oct 21 '13 at 13:08
  • Ou. That would be -22 (EINVAL) :P So there is already something wrong... But what could be wrong there?? –  Oct 21 '13 at 13:54
  • Please show the output of `amixer scontents` for the Master control. – CL. Oct 21 '13 at 14:53
  • amixer scontent `Simple mixer control 'Master',0 Capabilities: pvolume pswitch pswitch-joined penum Playback channels: Front Left - Front Right Limits: Playback 0 - 31 Mono: Front Left: Playback 0 [0%] [-46.50dB] [on] Front Right: Playback 0 [0%] [-46.50dB] [on]` –  Oct 22 '13 at 06:33
  • The program works for me. Check the return values of *all* functions. – CL. Oct 22 '13 at 19:45
  • Ok. I checked all return values now. All functions that return an integer error code return 0 except for `snd_mixer_selem_get_playback_dB_range` and `snd_mixer_selem_set_playback_dB[_all]`. The three dB functions all return **-22** –  Oct 23 '13 at 07:57
  • This error code would indicate that the control is not a playback control, or that it has no dB information. According to `amixer`, neither of these is the case. Can you set the dB value with `amixer`? – CL. Oct 23 '13 at 08:01
  • Yes I can set the volume with this command: `amixer set Master -- -22.5dB` –  Oct 23 '13 at 08:55
  • Strange. Try updating alsa-lib. – CL. Oct 23 '13 at 09:02
  • How do I do that exactly. I just reintslled the whole VM and nothing has changed, except that the new range values are now `min: -1217479904, max: -1075727553` I am also running Ubuntu 13.10 now in 32bit not 64bit as before. –  Oct 29 '13 at 11:25
  • When the function fails, min/max are never written and have random values. To update alsa-lib, [download](http://www.alsa-project.org/main/index.php/Download) and compile it. – CL. Oct 29 '13 at 12:10
  • I know they are just random numbers, because they never get initialized but, shouldn't their value change then over time / after a reboot? FYI: As expected, Xubuntu behaves the same way (min: -1217492192, max: -1073769631). I will try to compile the alsa-lib soon! –  Oct 29 '13 at 13:15

1 Answers1

2

I was experiencing a similar problem. The cause seems to have been interference from pulseaudio. It was a bit difficult to kill, though. Once I ran

mkdir -p ~/.pulse
echo autospawn=no > ~/.pulse/client.conf
killall pulseaudio

pulseaudio died and stayed dead, and the ALSA program I was writing was able to obtain dB information from the mixer.

Alternatively, you can do an end-run around pulseaudio by talking directly to the card. Change

const char *card = "default";

to

const char *card = "hw:0";

and see what that does for you.

wyrm
  • 318
  • 1
  • 12