Asound.conf: What's the best way to implement this?

I’m trying to have NixOS implement this asound.conf for a Traktor Audio 6: xwax - Tested Soundcards

I’m seeking instructions to use services.pipewire.extraConfig to add the configuration in the aforementioned link: Traktor Audio 6 http://www.pogo.org.uk/~mark/linuxdj/t6.asoundrc

#
# asoundrc entries for Traktor Audio 6
# Carl Brothers <decibelkaos at gmail.com>
#

# These options will Enable/Disable the passthrough on your Traktor
# Audio 6.  Personally, I just created shortcut buttons with the
# commands, and it works flawlessly.
#
# amixer -c T6 cset numid=1 on   # switch PASSTHRU on channel A
# amixer -c T6 cset numid=2 on   # switch PASSTHRU on channel B
# amixer -c T6 cset numid=3 on   # switch PHONO/LINE on channel A
# amixer -c T6 cset numid=4 on   # switch PHONO/LINE on channel B

pcm.T6_capture {
	type dsnoop
	ipc_key 1646
	slave {
		pcm "hw:T6"
		period_size 0
		buffer_size 65536
		rate 44100
		channels 6
	}
}

pcm.T6_playback {
	type dmix
	ipc_key 1646
	slave {
		pcm "hw:T6"
		period_size 0
		buffer_size 65536
		rate 44100
		channels 6
	}
}

pcm.T6_duplex {
	type asym
	playback.pcm T6_playback
	capture.pcm T6_capture
}

pcm.T6_pair1 {
	type plug
	ttable.0.0 1.0
	ttable.1.1 1.0
	slave.pcm T6_duplex
}

pcm.T6_pair2 {
	type plug
	ttable.0.2 1.0
	ttable.1.3 1.0
	slave.pcm T6_duplex
}

pcm.T6_pair3 {
	type plug
	ttable.0.4 1.0
	ttable.1.5 1.0
	slave.pcm T6_duplex
}

This is my current config.nix for pipewire is:

 # Enable sound with pipewire.
  sound.enable = true;
  hardware.pulseaudio.enable = false;
  security.rtkit.enable = true;
  services.pipewire = {
    enable = true;
    alsa.enable = true;
    alsa.support32Bit = true;
    pulse.enable = true;
    # If you want to use JACK applications, uncomment this
    jack.enable = true;

What are the instructions to add this conf? Or, am I going about it the wrong way?

asound.conf is the ALSA main configuration file. You can add lines to it via sound.extraConfig, alterantively you can copy that file into your home directory and rename it to .asoundrc.

Does this require the use of home-manager since it’s a dotfile?

I would just use the system-wide /etc/asound.conf (sound.extraConfig), since the sound card is necessarily shared across all users. I was suggesting to copying the file fo your home as a way to quickly test if that ALSA configuration works.

I would look at the various lib.generators.* functions to implement a custom attrSet -> string function. It shouldn’t difficult, you just check each value like myfunc a1: a2: k: v: if !(lib.isAttrs v) then "${k} ${toString v} else "${k} ${myfunc {} a1}". If the type is a string then output the value surrounded with quotes, etc.

Since a lib.generators function would allow invalid configs, ideally the module would check the config’s validity as well if it doesn’t already.

Are you suggesting to implement an RFC42-like option for ALSA configuration?

I would say it is difficult: the configuration language is pretty complex, it includes stuff like include statements, arithmetics, string interpolation and functions, take a look at this:

# pre-load the configuration files

@hooks [
	{
		func load
		files [
			"/var/lib/alsa/conf.d"
			"/usr/etc/alsa/conf.d"
			"/etc/alsa/conf.d"
			"/etc/asound.conf|||/usr/etc/asound.conf"
			"~/.asoundrc"
			{
				@func concat
				strings [
					{
						@func getenv
						vars [
							XDG_CONFIG_HOME
						]
						default "~/.config"
					}
					"/alsa/asoundrc"
				]
			}
		]
		errors false
	}
]

I doubt that recreating this in Nix would make it less error-prone.
Having a syntactic validity check would be useful, but I don’t think there’s any existing tool that does that in alsaLib, so it would probably have to be implemented ad-hoc.

@rnhmjoj

I did the following:

sound.extraConfig = {
#
# asoundrc entries for Traktor Audio 6
# Carl Brothers <decibelkaos at gmail.com>
#

# These options will Enable/Disable the passthrough on your Traktor
# Audio 6.  Personally, I just created shortcut buttons with the
# commands, and it works flawlessly.
#
# amixer -c T6 cset numid=1 on   # switch PASSTHRU on channel A
# amixer -c T6 cset numid=2 on   # switch PASSTHRU on channel B
# amixer -c T6 cset numid=3 on   # switch PHONO/LINE on channel A
# amixer -c T6 cset numid=4 on   # switch PHONO/LINE on channel B

pcm.T6_capture {
	type dsnoop
	ipc_key 1646
	slave {
		pcm "hw:T6"
		period_size 0
		buffer_size 65536
		rate 44100
		channels 6
	}
}

pcm.T6_playback {
	type dmix
	ipc_key 1646
	slave {
		pcm "hw:T6"
		period_size 0
		buffer_size 65536
		rate 44100
		channels 6
	}
}

pcm.T6_duplex {
	type asym
	playback.pcm T6_playback
	capture.pcm T6_capture
}

pcm.T6_pair1 {
	type plug
	ttable.0.0 1.0
	ttable.1.1 1.0
	slave.pcm T6_duplex
}

pcm.T6_pair2 {
	type plug
	ttable.0.2 1.0
	ttable.1.3 1.0
	slave.pcm T6_duplex
}

pcm.T6_pair3 {
	type plug
	ttable.0.4 1.0
	ttable.1.5 1.0
	slave.pcm T6_duplex
}
};

And it’s giving me the following output error:

error:
       … while evaluating the attribute 'config'

         at /nix/var/nix/profiles/per-user/root/channels/nixos/lib/modules.nix:322:9:

          321|         options = checked options;
          322|         config = checked (removeAttrs config [ "_module" ]);
             |         ^
          323|         _module = checked (config._module);

       … while calling the 'seq' builtin

         at /nix/var/nix/profiles/per-user/root/channels/nixos/lib/modules.nix:322:18:

          321|         options = checked options;
          322|         config = checked (removeAttrs config [ "_module" ]);
             |                  ^
          323|         _module = checked (config._module);

       (stack trace truncated; use '--show-trace' to show the full trace)

       error: syntax error, unexpected '{', expecting '.' or '='

       at /etc/nixos/configuration.nix:181:16:

          180|
          181| pcm.T6_capture {
             |                ^
          182|     type dsnoop
building Nix...
error:
       … while evaluating the attribute 'config'

         at /nix/var/nix/profiles/per-user/root/channels/nixos/lib/modules.nix:322:9:

          321|         options = checked options;
          322|         config = checked (removeAttrs config [ "_module" ]);
             |         ^
          323|         _module = checked (config._module);

       … while calling the 'seq' builtin

         at /nix/var/nix/profiles/per-user/root/channels/nixos/lib/modules.nix:322:18:

          321|         options = checked options;
          322|         config = checked (removeAttrs config [ "_module" ]);
             |                  ^
          323|         _module = checked (config._module);

       (stack trace truncated; use '--show-trace' to show the full trace)

       error: syntax error, unexpected '{', expecting '.' or '='

       at /etc/nixos/configuration.nix:181:16:

          180|
          181| pcm.T6_capture {
             |                ^
          182|     type dsnoop
building the system configuration...
error:
       … while evaluating the attribute 'config.system.build.toplevel'

         at /nix/var/nix/profiles/per-user/root/channels/nixos/lib/modules.nix:322:9:

          321|         options = checked options;
          322|         config = checked (removeAttrs config [ "_module" ]);
             |         ^
          323|         _module = checked (config._module);

       … while calling the 'seq' builtin

         at /nix/var/nix/profiles/per-user/root/channels/nixos/lib/modules.nix:322:18:

          321|         options = checked options;
          322|         config = checked (removeAttrs config [ "_module" ]);
             |                  ^
          323|         _module = checked (config._module);

       (stack trace truncated; use '--show-trace' to show the full trace)

       error: syntax error, unexpected '{', expecting '.' or '='

       at /etc/nixos/configuration.nix:181:16:

          180|
          181| pcm.T6_capture {
             |                ^
          182|       type dsnoop

I can’t seem to find a manual or any instructions on how to use sound.extraConfig, but I do see it here: NixOS Search

You have to put all that sound.extraConfig stuff in a string literal, not curly braces! Use double-apostrophes, like in the example:

sound.extraConfig = ''
  defaults.pcm.!card 3
'';
1 Like

@rhendric Thank you for pointing that out! I was able to build it. I’ll try out the interface when I get a chance.

If I were to put multiple configurations, for different soundcards, would it look something like:

sound.extraConfig = ''
  defaults.pcm.!card 3
'';

''
defaults.pcm!card 4
'';

''
defaults.pcm!card 5
'';

And so on?

No no, all in one string.

Nix files are very consistently structured—you’re building an expression out of building blocks:

  • atomic literals like strings, numbers, booleans, and paths
  • list and attrset literals
  • function expressions, also called lambdas
  • operators and function calls
  • fancier expressions like let, with, and assert

In particular, a Nix configuration is one big attrset. An attrset literal can only contain two types of things:

  • some.attribute.path = expression;
  • an inherit clause

That’s it! Nix the language will complain if you try to stick bare string literals inside an attrset, like { "example"; }, because the language has no rules for what that would mean.

(Nix language basics is a more complete introduction to these concepts.)

So if you’re setting an option (like sound.extraConfig, but this goes for any option), you have to set it to a single expression. Now some options will accept a complex expression, like a list. It’s not unimaginable that sound.extraConfig, in an alternate universe, might accept the following:

sound.extraConfig = [
  "defaults.pcm.!card 3"
  "defaults.pcm.!card 4"
  "defaults.pcm.!card 5"
];

(Note the square brackets: they mean that this is a list, not an attrset, so it’s okay to have bare strings here! Also note that the only difference between "this kind of string" and

''
this kind of string
''

is how whitespace is handled; the latter is typically used when a string has line breaks because it takes care of stripping out common indentation.)

That’s a valid expression, as far as Nix the language is concerned, but it won’t work because the type of sound.extraConfig, as you can see in the Type entry when looking at sound.extraConfig on NixOS Search, is strings concatenated with "\n" (a multi-line string, in other words) and not list of string. So that example would be a problem for the NixOS module system, and you’d get an error if you tried it.

No, what the NixOS module system wants is a single string. One could build that string in complex ways, but if you have a big block of non-Nix configuration that you just want to stuff in there, all you need to do is make a single multi-line string literal out of it.

1 Like