How to set Alsa sound volume using sound.extraConfig?

My sound volume is too low when logging in. So, I added these commands in my .profile file:

amixer -c "PRMA" set PCM,0 100%
amixer -c "PRMA" set PCM,1 100%

This solution is resolving my issue. But, I wonder if I could instead use the sound.extraConfig NixOS option in my NixOS configuration file.

Would someone know how?

1 Like

That appears to be a shell command. extraConfig generally depends on a given program or service. There is a way with extraConfig precisely when there is a way with whatever configuration options the program or service provides.

Here’s a possible solution. It hasn’t been tested.

  systemd.services.alsaCommands = {
    enable = true;
    script = ''
      amixer -c "PRMA" set PCM,0 100%
      amixer -c "PRMA" set PCM,1 100%
    '';
    wantedBy = [ "multi-user.target" ];
  };

This creates a systemd service called alsaCommands that runs the given script for all users.

systemd.services.<name>.wantedBy
Units that want (i.e. depend on) this unit. The default method for starting a unit by default at boot time is to set this option to ["multi-user.target"] for system services. Likewise for user units (systemd.user.<name>.* ) set it to ["default.target"] to make a unit start by default when the user <name> logs on.

My sound volume is too low when logging in.

The sound levels should persists across reboots: the state is stored in /var/lib/alsa/asound.state . Do you have impermanence or something like that and forgot to keep this file?

if I could instead use the sound.extraConfig NixOS option in my NixOS configuration file.

No, you can’t use the ALSA configuration to change the sound levels. At best you can raise the minimum level, but then you wouldn’t be able to lower the volume if you ever need to.
How exactly depends on your card, but generally the default device looks something like this:

pcm.default {
	@args [ CARD ]
	@args.CARD {
		type string
	}
	type asym
	playback.pcm {
		type plug
		slave.pcm {
			type softvol
			slave.pcm {
				@func concat
				strings [ "dmix:" $CARD ]
			}
			control {
				name "PCM Playback Volume"
				card $CARD
			}
		}
	}
	capture.pcm {
		type plug
		slave.pcm {
			type softvol
			slave.pcm {
				@func concat
				strings [ "dsnoop:" $CARD ]
			}
			control {
				name "Digital Capture Volume"
				card $CARD
			}
			min_dB -30.0  # CHANGE THIS
			max_dB 30.0
			resolution 121
		}
		route_policy copy
	}
	hint.device 0
}

I don’t think so. I haven’t done anything special.

When I log in, after a startup, my sound is too low. So, I use alsamixer to change this volume. Thereafter, the volume stays at the same level for the entire session, keeping its level even if the computer has slept.

Adding the two commands that I have shown previously in my .profile file solve the issue when I log in: I no longer have to go into alsamixer to change the volume manually. But the sound volume goes back to low after the computer has slept with this method.

I don’t understand what is written in /var/lib/alsa/asound.state and I don’t want to change it because this solution would not be portable.

Making a service was a great idea! Here is what I came with:

  systemd.services.RestoreAlsaVolume = {
      enable = true;
      description = "Setting SPDIF ALSA volume at 100% after sleep.";
      unitConfig = {
          Type = "simple";
      };
      serviceConfig.User = "pierre";
      script = lib.getExe(pkgs.writeShellApplication {
          name = "set-spdif-volume";
          runtimeInputs = with pkgs; [alsa-utils];
          text = ''
	    amixer -c "PRMA" set PCM,0 100%
            amixer -c "PRMA" set PCM,1 100%
          '';
      });
      wantedBy = [ "suspend.target" ];
  };

I still have the two same commands in .profile. I was unable to create a service setting the volume at startup. Because, I think this is conflicting with Alsa trying to do the same thing.

I had difficulties in my tests because setting the volume in alsamixer makes Alsa remember the volume when the machine is back from sleep. The command lines I used to not have this memory effect.

But the sound volume goes back to low after the computer has slept with this method.

This should not happen: the sound levels are saved before the computer goes to sleep or is powered off and restored on boot.

Can you share the output of these commands?

systemctl status alsa-store.service 
journalctl -u systemd-udevd -b -o cat --grep alsa

Can you share the output of these commands?

systemctl status alsa-store.service 
journalctl -u systemd-udevd -b -o cat --grep alsa
systemctl status alsa-store.service

â—Ź alsa-store.service - Store Sound Card State
     Loaded: loaded (/etc/systemd/system/alsa-store.service; enabled; preset: enabled)
     Active: active (exited) since Sun 2024-06-30 02:01:20 EDT; 3h 3min ago
    Process: 1592 ExecStart=/nix/store/pxf98n8dsxb6kmm0pdvc3dr3gzcqhizi-coreutils-9.3/bin/mkdir -p /var/lib/alsa (code=exited, status=0/SUCCESS)
   Main PID: 1592 (code=exited, status=0/SUCCESS)
         IP: 0B in, 0B out
        CPU: 1ms

jun 30 02:01:20 pierre-nixos systemd[1]: Starting Store Sound Card State...
jun 30 02:01:20 pierre-nixos systemd[1]: Finished Store Sound Card State.

journalctl -u systemd-udevd -b -o cat --grep alsa produces no output.

It looks like the udev rule is not firing… strange. What if you manually run sudo alsactl restore, is the volume restored? If so, you could put this in you workaround service.

What do you mean by that? What is the test you are proposing?

  1. Change the volume with alsamixer to whatever level you like
  2. reboot the system
  3. log in and run sudo alsactl restore

Is the volume properly restored now?

Yes, the volume is properly restored. :smiley:

So, what to do next?

Next step would be figuring out if the udev rules are really not firing and, if so, why.
To debug udev add udev.log-priority=debug to the kernel command line arguments from the bootloader. Then after you log in check the logs again: journalctl -u systemd-udevd -b -o cat --grep alsa.

This time you should see much more information, including which commands udev has run and what the result was.