nixos/alsa: reintroduce hardware.alsa.enablePersistence (#373529)

authored by

Michele Guerini Rocco and committed by
GitHub
b4622e7a e5ad45c4

+157 -135
+4 -5
nixos/doc/manual/release-notes/rl-2411.section.md
··· 936 937 If you set `sound.enable` in your configuration: 938 - If you are using Pulseaudio or PipeWire, simply remove that option 939 - - If you are using ALSA as your only sound system (no sound server), set `hardware.alsa.enable = true` instead 940 941 If you set `sound.enableOSSEmulation` in your configuration: 942 - Make sure it is still necessary, as very few applications actually use OSS 943 - - If necessary, set `hardware.alsa.enableOSSEmulation = true` 944 945 If you set `sound.extraConfig` in your configuration: 946 - - If you are using a sound server, like Pulseaudio, JACK or PipeWire, migrate your configuration to that 947 - - If you are using ALSA as your only sound system, check if you can use the new structured ALSA options `hardware.alsa.defaultDevice`, `hardware.alsa.cardAliases`, `hardware.alsa.controls`, etc. 948 - - Otherwise, move your configuration directly into `hardware.alsa.config` 949 950 If you set `sound.mediaKeys` in your configuration: 951 - Preferably switch to handling media keys in your desktop environment/compositor
··· 936 937 If you set `sound.enable` in your configuration: 938 - If you are using Pulseaudio or PipeWire, simply remove that option 939 + - If you are not using an external sound server, and want volumes to be persisted across shutdowns, set `hardware.alsa.enablePersistence = true` instead 940 941 If you set `sound.enableOSSEmulation` in your configuration: 942 - Make sure it is still necessary, as very few applications actually use OSS 943 + - If necessary, set `boot.kernelModules = [ "snd_pcm_oss" ]` 944 945 If you set `sound.extraConfig` in your configuration: 946 + - If you are using another sound server, like Pulseaudio, JACK or PipeWire, migrate your configuration to that 947 + - If you are not using an external sound server, set `environment.etc."asound.conf".text = yourExtraConfig` instead 948 949 If you set `sound.mediaKeys` in your configuration: 950 - Preferably switch to handling media keys in your desktop environment/compositor
+3
nixos/doc/manual/release-notes/rl-2505.section.md
··· 375 - The paperless module now has an option for regular automatic export of 376 documents data using the integrated document exporter. 377 378 - Caddy can now be built with plugins by using `caddy.withPlugins`, a `passthru` function that accepts an attribute set as a parameter. The `plugins` argument represents a list of Caddy plugins, with each Caddy plugin being a versioned module. The `hash` argument represents the `vendorHash` of the resulting Caddy source code with the plugins added. 379 380 Example:
··· 375 - The paperless module now has an option for regular automatic export of 376 documents data using the integrated document exporter. 377 378 + - New options for the declarative configuration of the user space part of ALSA have been introduced under [hardware.alsa](options.html#opt-hardware.alsa.enable), including setting the default capture and playback device, defining sound card aliases and volume controls. 379 + Note: these are intended for users not running a sound server like PulseAudio or PipeWire, but having ALSA as their only sound system. 380 + 381 - Caddy can now be built with plugins by using `caddy.withPlugins`, a `passthru` function that accepts an attribute set as a parameter. The `plugins` argument represents a list of Caddy plugins, with each Caddy plugin being a versioned module. The `hash` argument represents the `vendorHash` of the resulting Caddy source code with the plugins added. 382 383 Example:
+150 -130
nixos/modules/services/audio/alsa.nix
··· 86 [ "sound" "enableOSSEmulation" ] 87 [ "hardware" "alsa" "enableOSSEmulation" ] 88 ) 89 - (lib.mkRenamedOptionModule 90 - [ "sound" "extraConfig" ] 91 - [ "hardware" "alsa" "config" ]) 92 ]; 93 94 options.hardware.alsa = { ··· 191 }; 192 }) 193 ); 194 - default = {}; 195 example = lib.literalExpression '' 196 { 197 firefox = { device = "front"; maxVolume = -25.0; }; ··· 286 287 }; 288 289 - config = lib.mkIf cfg.enable { 290 291 - # Disable sound servers enabled by default and, 292 - # if the user enabled one manually, cause a conflict. 293 - services.pipewire.enable = false; 294 - hardware.pulseaudio.enable = false; 295 296 - hardware.alsa.config = 297 - let 298 - conf = [ 299 - '' 300 - pcm.!default fromenv 301 302 - # Read the capture and playback device from 303 - # the ALSA_AUDIO_IN, ALSA_AUDIO_OUT variables 304 - pcm.fromenv { 305 - type asym 306 - playback.pcm { 307 - type plug 308 - slave.pcm { 309 - @func getenv 310 - vars [ ALSA_AUDIO_OUT ] 311 - default pcm.sysdefault 312 } 313 - } 314 - capture.pcm { 315 - type plug 316 - slave.pcm { 317 - @func getenv 318 - vars [ ALSA_AUDIO_IN ] 319 - default pcm.sysdefault 320 } 321 } 322 - } 323 - '' 324 - (lib.optional cfg.enableRecorder '' 325 - pcm.!default "splitter:fromenv,recorder" 326 327 - # Send audio to two stereo devices 328 - pcm.splitter { 329 - @args [ A B ] 330 - @args.A.type string 331 - @args.B.type string 332 - type asym 333 - playback.pcm { 334 - type plug 335 - route_policy "duplicate" 336 - slave.pcm { 337 - type multi 338 - slaves.a.pcm $A 339 - slaves.b.pcm $B 340 - slaves.a.channels 2 341 - slaves.b.channels 2 342 - bindings [ 343 - { slave a channel 0 } 344 - { slave a channel 1 } 345 - { slave b channel 0 } 346 - { slave b channel 1 } 347 - ] 348 } 349 } 350 - capture.pcm $A 351 - } 352 353 - # Device which records and plays back audio 354 - pcm.recorder { 355 - type asym 356 - capture.pcm { 357 - type dsnoop 358 - ipc_key 9165218 359 - ipc_perm 0666 360 - slave.pcm "hw:loopback,1,0" 361 - slave.period_size 1024 362 - slave.buffer_size 8192 363 - } 364 - playback.pcm { 365 - type dmix 366 - ipc_key 6181923 367 - ipc_perm 0666 368 - slave.pcm "hw:loopback,0,0" 369 - slave.period_size 1024 370 - slave.buffer_size 8192 371 } 372 - } 373 - '') 374 - (lib.mapAttrsToList mkControl cfg.controls) 375 - (lib.mapAttrsToList (n: v: "pcm.${n} ${quote v}") cfg.deviceAliases) 376 - ]; 377 - in 378 - lib.mkBefore (lib.concatStringsSep "\n" (lib.flatten conf)); 379 380 - hardware.alsa.cardAliases = lib.mkIf cfg.enableRecorder { 381 - loopback.driver = "snd_aloop"; 382 - loopback.id = 2; 383 - }; 384 385 - # Set default PCM devices 386 - environment.sessionVariables = defaultDeviceVars; 387 - systemd.globalEnvironment = defaultDeviceVars; 388 389 - environment.etc."asound.conf".text = cfg.config; 390 391 - boot.kernelModules = 392 - [ ] 393 - ++ lib.optionals cfg.enableOSSEmulation [ "snd_pcm_oss" "snd_mixer_oss" ] 394 - ++ lib.optionals cfg.enableRecorder [ "snd_aloop" ]; 395 396 - # Assign names to the sound cards 397 - boot.extraModprobeConfig = lib.concatStringsSep "\n" cardsConfig; 398 399 - # Provide alsamixer, aplay, arecord, etc. 400 - environment.systemPackages = [ pkgs.alsa-utils ]; 401 402 - # Install udev rules for restoring card settings on boot 403 - services.udev.extraRules = '' 404 - ACTION=="add", SUBSYSTEM=="sound", KERNEL=="controlC*", KERNELS!="card*", GOTO="alsa_restore_go" 405 - GOTO="alsa_restore_end" 406 407 - LABEL="alsa_restore_go" 408 - TEST!="/etc/alsa/state-daemon.conf", RUN+="${alsactl} restore -gU $attr{device/number}" 409 - TEST=="/etc/alsa/state-daemon.conf", RUN+="${alsactl} nrestore -gU $attr{device/number}" 410 - LABEL="alsa_restore_end" 411 - ''; 412 413 - # Service to store/restore the sound card settings 414 - systemd.services.alsa-store = { 415 - description = "Store Sound Card State"; 416 - wantedBy = [ "multi-user.target" ]; 417 - restartIfChanged = false; 418 - unitConfig = { 419 - RequiresMountsFor = "/var/lib/alsa"; 420 - ConditionVirtualization = "!systemd-nspawn"; 421 - }; 422 - serviceConfig = { 423 - Type = "oneshot"; 424 - RemainAfterExit = true; 425 - StateDirectory = "alsa"; 426 - # Note: the service should never be restated, otherwise any 427 - # setting changed between the last `store` and now will be lost. 428 - # To prevent NixOS from starting it in case it has failed we 429 - # expand the exit codes considered successful 430 - SuccessExitStatus = [ 0 99 ]; 431 - ExecStart = "${alsactl} restore -gU"; 432 - ExecStop = "${alsactl} store -gU"; 433 }; 434 - }; 435 - }; 436 437 meta.maintainers = with lib.maintainers; [ rnhmjoj ]; 438
··· 86 [ "sound" "enableOSSEmulation" ] 87 [ "hardware" "alsa" "enableOSSEmulation" ] 88 ) 89 + (lib.mkRenamedOptionModule [ "sound" "extraConfig" ] [ "hardware" "alsa" "config" ]) 90 ]; 91 92 options.hardware.alsa = { ··· 189 }; 190 }) 191 ); 192 + default = { }; 193 example = lib.literalExpression '' 194 { 195 firefox = { device = "front"; maxVolume = -25.0; }; ··· 284 285 }; 286 287 + options.hardware.alsa.enablePersistence = lib.mkOption { 288 + type = lib.types.bool; 289 + defaultText = lib.literalExpression "config.hardware.alsa.enable"; 290 + default = config.hardware.alsa.enable; 291 + description = '' 292 + Whether to enable ALSA sound card state saving on shutdown. 293 + This is generally not necessary if you're using an external sound server. 294 + ''; 295 + }; 296 297 + config = lib.mkMerge [ 298 299 + (lib.mkIf cfg.enable { 300 + # Disable sound servers enabled by default and, 301 + # if the user enabled one manually, cause a conflict. 302 + services.pipewire.enable = false; 303 + services.pulseaudio.enable = false; 304 305 + hardware.alsa.config = 306 + let 307 + conf = [ 308 + '' 309 + pcm.!default fromenv 310 + 311 + # Read the capture and playback device from 312 + # the ALSA_AUDIO_IN, ALSA_AUDIO_OUT variables 313 + pcm.fromenv { 314 + type asym 315 + playback.pcm { 316 + type plug 317 + slave.pcm { 318 + @func getenv 319 + vars [ ALSA_AUDIO_OUT ] 320 + default pcm.sysdefault 321 + } 322 } 323 + capture.pcm { 324 + type plug 325 + slave.pcm { 326 + @func getenv 327 + vars [ ALSA_AUDIO_IN ] 328 + default pcm.sysdefault 329 + } 330 } 331 } 332 + '' 333 + (lib.optional cfg.enableRecorder '' 334 + pcm.!default "splitter:fromenv,recorder" 335 336 + # Send audio to two stereo devices 337 + pcm.splitter { 338 + @args [ A B ] 339 + @args.A.type string 340 + @args.B.type string 341 + type asym 342 + playback.pcm { 343 + type plug 344 + route_policy "duplicate" 345 + slave.pcm { 346 + type multi 347 + slaves.a.pcm $A 348 + slaves.b.pcm $B 349 + slaves.a.channels 2 350 + slaves.b.channels 2 351 + bindings [ 352 + { slave a channel 0 } 353 + { slave a channel 1 } 354 + { slave b channel 0 } 355 + { slave b channel 1 } 356 + ] 357 + } 358 } 359 + capture.pcm $A 360 } 361 362 + # Device which records and plays back audio 363 + pcm.recorder { 364 + type asym 365 + capture.pcm { 366 + type dsnoop 367 + ipc_key 9165218 368 + ipc_perm 0666 369 + slave.pcm "hw:loopback,1,0" 370 + slave.period_size 1024 371 + slave.buffer_size 8192 372 + } 373 + playback.pcm { 374 + type dmix 375 + ipc_key 6181923 376 + ipc_perm 0666 377 + slave.pcm "hw:loopback,0,0" 378 + slave.period_size 1024 379 + slave.buffer_size 8192 380 + } 381 } 382 + '') 383 + (lib.mapAttrsToList mkControl cfg.controls) 384 + (lib.mapAttrsToList (n: v: "pcm.${n} ${quote v}") cfg.deviceAliases) 385 + ]; 386 + in 387 + lib.mkBefore (lib.concatStringsSep "\n" (lib.flatten conf)); 388 389 + hardware.alsa.cardAliases = lib.mkIf cfg.enableRecorder { 390 + loopback.driver = "snd_aloop"; 391 + loopback.id = 2; 392 + }; 393 394 + # Set default PCM devices 395 + environment.sessionVariables = defaultDeviceVars; 396 + systemd.globalEnvironment = defaultDeviceVars; 397 398 + environment.etc."asound.conf".text = cfg.config; 399 400 + boot.kernelModules = 401 + [ ] 402 + ++ lib.optionals cfg.enableOSSEmulation [ 403 + "snd_pcm_oss" 404 + "snd_mixer_oss" 405 + ] 406 + ++ lib.optionals cfg.enableRecorder [ "snd_aloop" ]; 407 408 + # Assign names to the sound cards 409 + boot.extraModprobeConfig = lib.concatStringsSep "\n" cardsConfig; 410 411 + # Provide alsamixer, aplay, arecord, etc. 412 + environment.systemPackages = [ pkgs.alsa-utils ]; 413 + }) 414 415 + (lib.mkIf config.hardware.alsa.enablePersistence { 416 417 + # Install udev rules for restoring card settings on boot 418 + services.udev.extraRules = '' 419 + ACTION=="add", SUBSYSTEM=="sound", KERNEL=="controlC*", KERNELS!="card*", GOTO="alsa_restore_go" 420 + GOTO="alsa_restore_end" 421 422 + LABEL="alsa_restore_go" 423 + TEST!="/etc/alsa/state-daemon.conf", RUN+="${alsactl} restore -gU $attr{device/number}" 424 + TEST=="/etc/alsa/state-daemon.conf", RUN+="${alsactl} nrestore -gU $attr{device/number}" 425 + LABEL="alsa_restore_end" 426 + ''; 427 + 428 + # Service to store/restore the sound card settings 429 + systemd.services.alsa-store = { 430 + description = "Store Sound Card State"; 431 + wantedBy = [ "multi-user.target" ]; 432 + restartIfChanged = false; 433 + unitConfig = { 434 + RequiresMountsFor = "/var/lib/alsa"; 435 + ConditionVirtualization = "!systemd-nspawn"; 436 + }; 437 + serviceConfig = { 438 + Type = "oneshot"; 439 + RemainAfterExit = true; 440 + StateDirectory = "alsa"; 441 + # Note: the service should never be restated, otherwise any 442 + # setting changed between the last `store` and now will be lost. 443 + # To prevent NixOS from starting it in case it has failed we 444 + # expand the exit codes considered successful 445 + SuccessExitStatus = [ 446 + 0 447 + 99 448 + ]; 449 + ExecStart = "${alsactl} restore -gU"; 450 + ExecStop = "${alsactl} store -gU"; 451 + }; 452 }; 453 + }) 454 + 455 + ]; 456 457 meta.maintainers = with lib.maintainers; [ rnhmjoj ]; 458