From 76b1e1fc842b386c7131df15c59643187b2e1f23 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Sun, 12 Jan 2025 19:49:08 +0100 Subject: [PATCH] Revert "[Backport release-24.11] nixos/alsa: rebirth from the ashes" --- .../manual/release-notes/rl-2411.section.md | 9 +- nixos/modules/services/audio/alsa.nix | 433 +----------------- nixos/tests/firefox.nix | 34 +- 3 files changed, 48 insertions(+), 428 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index 8f96c53a64f77..b6e30e975b25f 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -934,16 +934,15 @@ The `sound` options have been largely removed, as they are unnecessary for most If you set `sound.enable` in your configuration: - If you are using Pulseaudio or PipeWire, simply remove that option - - If you are using ALSA as your only sound system (no sound server), set `hardware.alsa.enable = true` instead + - If you are not using an external sound server, and want volumes to be persisted across shutdowns, set `hardware.alsa.enablePersistence = true` instead If you set `sound.enableOSSEmulation` in your configuration: - Make sure it is still necessary, as very few applications actually use OSS - - If necessary, set `hardware.alsa.enableOSSEmulation = true` + - If necessary, set `boot.kernelModules = [ "snd_pcm_oss" ]` If you set `sound.extraConfig` in your configuration: - - If you are using a sound server, like Pulseaudio, JACK or PipeWire, migrate your configuration to that - - 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. - - Otherwise, move your configuration directly into `hardware.alsa.config` + - If you are using another sound server, like Pulseaudio, JACK or PipeWire, migrate your configuration to that + - If you are not using an external sound server, set `environment.etc."asound.conf".text = yourExtraConfig` instead If you set `sound.mediaKeys` in your configuration: - Preferably switch to handling media keys in your desktop environment/compositor diff --git a/nixos/modules/services/audio/alsa.nix b/nixos/modules/services/audio/alsa.nix index a26dc493cdb0e..fac0012b666d4 100644 --- a/nixos/modules/services/audio/alsa.nix +++ b/nixos/modules/services/audio/alsa.nix @@ -1,420 +1,27 @@ -{ - config, - lib, - pkgs, - ... -}: - -let - cfg = config.hardware.alsa; - - quote = x: ''"${lib.escape [ "\"" ] x}"''; - - alsactl = lib.getExe' pkgs.alsa-utils "alsactl"; - - # Creates a volume control - mkControl = name: opts: '' - pcm.${name} { - type softvol - slave.pcm ${quote opts.device} - control.name ${quote (if opts.name != null then opts.name else name)} - control.card ${quote opts.card} - max_dB ${toString opts.maxVolume} - } - ''; - - # modprobe.conf for naming sound cards - cardsConfig = - let - # Reverse the mapping from card name→driver to card driver→name - drivers = lib.unique (lib.mapAttrsToList (n: v: v.driver) cfg.cardAliases); - options = lib.forEach drivers ( - drv: - let - byDriver = lib.filterAttrs (n: v: v.driver == drv); - ids = lib.mapAttrs (n: v: v.id) (byDriver cfg.cardAliases); - in - { - driver = drv; - names = lib.attrNames ids; - ids = lib.attrValues ids; - } - ); - toList = x: lib.concatStringsSep "," (map toString x); - in - lib.forEach options (i: "options ${i.driver} index=${toList i.ids} id=${toList i.names}"); - - defaultDeviceVars = { - "ALSA_AUDIO_OUT" = cfg.defaultDevice.playback; - "ALSA_AUDIO_IN" = cfg.defaultDevice.capture; - }; - -in +# ALSA sound support. +{ config, lib, pkgs, ... }: { imports = [ - (lib.mkRemovedOptionModule [ "sound" "enable" ] '' - The option was heavily overloaded and can be removed from most configurations. - To specifically configure the user space part of ALSA, see `hardware.alsa`. - '') - (lib.mkRemovedOptionModule [ "sound" "mediaKeys" ] '' - The media keys can be configured with any hotkey daemon (that better - integrates with your desktop setup). To continue using the actkbd daemon - (which was used up to NixOS 24.05), add these lines to your configuration: - - services.actkbd.enable = true; - services.actkbd.bindings = [ - # Mute - { keys = [ 113 ]; events = [ "key" ]; - command = "''${pkgs.alsa-utils}/bin/amixer -q set Master toggle"; - } - # Volume down - { keys = [ 114 ]; events = [ "key" "rep" ]; - command = "''${pkgs.alsa-utils}/bin/amixer -q set Master 1- unmute"; - } - # Volume up - { keys = [ 115 ]; events = [ "key" "rep" ]; - command = "''${pkgs.alsa-utils}/bin/amixer -q set Master 1+ unmute"; - } - # Mic Mute - { keys = [ 190 ]; events = [ "key" ]; - command = "''${pkgs.alsa-utils}/bin/amixer -q set Capture toggle"; - } - ]; - '') - (lib.mkRenamedOptionModule - [ "sound" "enableOSSEmulation" ] - [ "hardware" "alsa" "enableOSSEmulation" ] - ) - (lib.mkRenamedOptionModule - [ "sound" "extraConfig" ] - [ "hardware" "alsa" "config" ]) + (lib.mkRemovedOptionModule [ "sound" ] "The option was heavily overloaded and can be removed from most configurations.") ]; - options.hardware.alsa = { - - enable = lib.mkOption { - type = lib.types.bool; - default = false; - description = '' - Whether to set up the user space part of the Advanced Linux Sound Architecture (ALSA) - - ::: {.warning} - Enable this option only if you want to use ALSA as your main sound system, - not if you're using a sound server (e.g. PulseAudio or Pipewire). - ::: - ''; - }; - - enableOSSEmulation = lib.mkEnableOption "the OSS emulation"; - - enableRecorder = lib.mkOption { - type = lib.types.bool; - default = false; - description = '' - Whether to set up a loopback device that continuously records and - allows to play back audio from the computer. - - The loopback device is named `pcm.recorder`, audio can be saved - by capturing from this device as with any microphone. - - ::: {.note} - By default the output is duplicated to the recorder assuming stereo - audio, for a more complex layout you have to override the pcm.splitter - device using `hardware.alsa.config`. - See the generated /etc/asound.conf for its definition. - ::: - ''; - }; - - defaultDevice.playback = lib.mkOption { - type = lib.types.str; - default = ""; - example = "dmix:CARD=1,DEV=0"; - description = '' - The default playback device. - Leave empty to let ALSA pick the default automatically. - - ::: {.note} - The device can be changed at runtime by setting the ALSA_AUDIO_OUT - environment variables (but only before starting a program). - ::: - ''; - }; - - defaultDevice.capture = lib.mkOption { - type = lib.types.str; - default = ""; - example = "dsnoop:CARD=0,DEV=2"; - description = '' - The default capture device (i.e. microphone). - Leave empty to let ALSA pick the default automatically. - - ::: {.note} - The device can be changed at runtime by setting the ALSA_AUDIO_IN - environment variables (but only before starting a program). - ::: - ''; - }; - - controls = lib.mkOption { - type = lib.types.attrsOf ( - lib.types.submodule ({ - options.name = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = '' - Name of the control, as it appears in `alsamixer`. - If null it will be the same as the softvol device name. - ''; - }; - options.device = lib.mkOption { - type = lib.types.str; - default = "default"; - description = '' - Name of the PCM device to control (slave). - ''; - }; - options.card = lib.mkOption { - type = lib.types.str; - default = "default"; - description = '' - Name of the PCM card to control (slave). - ''; - }; - options.maxVolume = lib.mkOption { - type = lib.types.float; - default = 0.0; - description = '' - The maximum volume in dB. - ''; - }; - }) - ); - default = {}; - example = lib.literalExpression '' - { - firefox = { device = "front"; maxVolume = -25.0; }; - mpv = { device = "front"; maxVolume = -25.0; }; - # and run programs with `env ALSA_AUDIO_OUT=` - } - ''; - description = '' - Virtual volume controls (softvols) to add to a sound card. - These can be used to control the volume of specific applications - or a digital output device (HDMI video card). - ''; - }; - - cardAliases = lib.mkOption { - type = lib.types.attrsOf ( - lib.types.submodule ({ - options.driver = lib.mkOption { - type = lib.types.str; - description = '' - Name of the kernel module that provides the card. - ''; - }; - options.id = lib.mkOption { - type = lib.types.int; - default = "default"; - description = '' - The ID of the sound card - ''; - }; - }) - ); - default = { }; - example = lib.literalExpression '' - { - soundchip = { driver = "snd_intel_hda"; id = 0; }; - videocard = { driver = "snd_intel_hda"; id = 1; }; - usb = { driver = "snd_usb_audio"; id = 2; }; - } - ''; - description = '' - Assign custom names and reorder the sound cards. - - ::: {.note} - You can find the card ids by looking at `/proc/asound/cards`. - ::: - ''; - }; - - deviceAliases = lib.mkOption { - type = lib.types.attrsOf lib.types.str; - default = { }; - example = lib.literalExpression '' - { - hdmi1 = "hw:CARD=videocard,DEV=5"; - hdmi2 = "hw:CARD=videocard,DEV=6"; - } - ''; - description = '' - Assign custom names to sound cards. - ''; - }; - - config = lib.mkOption { - type = lib.types.lines; - default = ""; - example = lib.literalExpression '' - # Send audio to a remote host via SSH - pcm.remote { - @args [ HOSTNAME ] - @args.HOSTNAME { type string } - type file - format raw - slave.pcm pcm.null - file { - @func concat - strings [ - "| ''${lib.getExec pkgs.openssh} -C " - $HOSTNAME - " aplay -f %f -c %c -r %r -" - ] - } - } - ''; - description = '' - The content of the system-wide ALSA configuration (/etc/asound.conf). - - Documentation of the configuration language and examples can be found - in the unofficial ALSA wiki: https://alsa.opensrc.org/Asoundrc - ''; - }; - + options.hardware.alsa.enablePersistence = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Whether to enable ALSA sound card state saving on shutdown. + This is generally not necessary if you're using an external sound server. + ''; }; - config = lib.mkIf cfg.enable { - - # Disable sound servers enabled by default and, - # if the user enabled one manually, cause a conflict. - services.pipewire.enable = false; - hardware.pulseaudio.enable = false; - - hardware.alsa.config = - let - conf = [ - '' - pcm.!default fromenv - - # Read the capture and playback device from - # the ALSA_AUDIO_IN, ALSA_AUDIO_OUT variables - pcm.fromenv { - type asym - playback.pcm { - type plug - slave.pcm { - @func getenv - vars [ ALSA_AUDIO_OUT ] - default pcm.sysdefault - } - } - capture.pcm { - type plug - slave.pcm { - @func getenv - vars [ ALSA_AUDIO_IN ] - default pcm.sysdefault - } - } - } - '' - (lib.optional cfg.enableRecorder '' - pcm.!default "splitter:fromenv,recorder" - - # Send audio to two stereo devices - pcm.splitter { - @args [ A B ] - @args.A.type string - @args.B.type string - type asym - playback.pcm { - type plug - route_policy "duplicate" - slave.pcm { - type multi - slaves.a.pcm $A - slaves.b.pcm $B - slaves.a.channels 2 - slaves.b.channels 2 - bindings [ - { slave a channel 0 } - { slave a channel 1 } - { slave b channel 0 } - { slave b channel 1 } - ] - } - } - capture.pcm $A - } + config = lib.mkIf config.hardware.alsa.enablePersistence { + # ALSA provides a udev rule for restoring volume settings. + services.udev.packages = [ pkgs.alsa-utils ]; - # Device which records and plays back audio - pcm.recorder { - type asym - capture.pcm { - type dsnoop - ipc_key 9165218 - ipc_perm 0666 - slave.pcm "hw:loopback,1,0" - slave.period_size 1024 - slave.buffer_size 8192 - } - playback.pcm { - type dmix - ipc_key 6181923 - ipc_perm 0666 - slave.pcm "hw:loopback,0,0" - slave.period_size 1024 - slave.buffer_size 8192 - } - } - '') - (lib.mapAttrsToList mkControl cfg.controls) - (lib.mapAttrsToList (n: v: "pcm.${n} ${quote v}") cfg.deviceAliases) - ]; - in - lib.mkBefore (lib.concatStringsSep "\n" (lib.flatten conf)); - - hardware.alsa.cardAliases = lib.mkIf cfg.enableRecorder { - loopback.driver = "snd_aloop"; - loopback.id = 2; - }; - - # Set default PCM devices - environment.sessionVariables = defaultDeviceVars; - systemd.globalEnvironment = defaultDeviceVars; - - environment.etc."asound.conf".text = cfg.config; - - boot.kernelModules = - [ ] - ++ lib.optionals cfg.enableOSSEmulation [ "snd_pcm_oss" "snd_mixer_oss" ] - ++ lib.optionals cfg.enableRecorder [ "snd_aloop" ]; - - # Assign names to the sound cards - boot.extraModprobeConfig = lib.concatStringsSep "\n" cardsConfig; - - # Provide alsamixer, aplay, arecord, etc. - environment.systemPackages = [ pkgs.alsa-utils ]; - - # Install udev rules for restoring card settings on boot - services.udev.extraRules = '' - ACTION=="add", SUBSYSTEM=="sound", KERNEL=="controlC*", KERNELS!="card*", GOTO="alsa_restore_go" - GOTO="alsa_restore_end" - - LABEL="alsa_restore_go" - TEST!="/etc/alsa/state-daemon.conf", RUN+="${alsactl} restore -gU $attr{device/number}" - TEST=="/etc/alsa/state-daemon.conf", RUN+="${alsactl} nrestore -gU $attr{device/number}" - LABEL="alsa_restore_end" - ''; - - # Service to store/restore the sound card settings systemd.services.alsa-store = { description = "Store Sound Card State"; wantedBy = [ "multi-user.target" ]; - restartIfChanged = false; unitConfig = { RequiresMountsFor = "/var/lib/alsa"; ConditionVirtualization = "!systemd-nspawn"; @@ -422,18 +29,10 @@ in serviceConfig = { Type = "oneshot"; RemainAfterExit = true; - StateDirectory = "alsa"; - # Note: the service should never be restated, otherwise any - # setting changed between the last `store` and now will be lost. - # To prevent NixOS from starting it in case it has failed we - # expand the exit codes considered successful - SuccessExitStatus = [ 0 99 ]; - ExecStart = "${alsactl} restore -gU"; - ExecStop = "${alsactl} store -gU"; + ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p /var/lib/alsa"; + ExecStart = "${pkgs.alsa-utils}/sbin/alsactl restore --ignore"; + ExecStop = "${pkgs.alsa-utils}/sbin/alsactl store --ignore"; }; }; }; - - meta.maintainers = with lib.maintainers; [ rnhmjoj ]; - } diff --git a/nixos/tests/firefox.nix b/nixos/tests/firefox.nix index 9388af953a355..8243defbb9f2e 100644 --- a/nixos/tests/firefox.nix +++ b/nixos/tests/firefox.nix @@ -18,15 +18,37 @@ import ./make-test-python.nix ({ lib, pkgs, firefoxPackage, ... }: package = firefoxPackage; }; - hardware.alsa = { - enable = true; - enableRecorder = true; - defaultDevice.playback = "pcm.recorder"; - }; + # Create a virtual sound device, with mixing + # and all, for recording audio. + boot.kernelModules = [ "snd-aloop" ]; + environment.etc."asound.conf".text = '' + pcm.!default { + type plug + slave.pcm pcm.dmixer + } + pcm.dmixer { + type dmix + ipc_key 1 + slave { + pcm "hw:Loopback,0,0" + rate 48000 + periods 128 + period_time 0 + period_size 1024 + buffer_size 8192 + } + } + pcm.recorder { + type hw + card "Loopback" + device 1 + subdevice 0 + } + ''; systemd.services.audio-recorder = { description = "Record NixOS test audio to /tmp/record.wav"; - script = "${pkgs.alsa-utils}/bin/arecord -Drecorder -fS16_LE -r48000 -c2 /tmp/record.wav"; + script = "${pkgs.alsa-utils}/bin/arecord -D recorder -f S16_LE -r48000 /tmp/record.wav"; }; };