1{ lib, pkgs, config, user, ... }:
2
3with lib;
4let
5 inherit (generators) toLua;
6 cfg = config.modules.desktop;
7in {
8 options.modules.desktop.audio = {
9 enable = mkOption {
10 default = true;
11 example = true;
12 description = "Whether to enable Pipewire audio.";
13 type = types.bool;
14 };
15
16 lowLatency = mkOption {
17 default = true;
18 example = true;
19 description = "Whether to enable Pipewire low latency.";
20 type = types.bool;
21 };
22
23 quantum = mkOption {
24 description = "Minimum quantum to set";
25 type = types.int;
26 default = 64;
27 example = 32;
28 };
29
30 rate = mkOption {
31 description = "Rate to set";
32 type = types.int;
33 default = 48000;
34 example = 96000;
35 };
36 };
37
38 config = mkIf cfg.audio.enable {
39 security.rtkit.enable = true;
40
41 services = {
42 pipewire = let
43 quantum = cfg.audio.quantum;
44 rate = cfg.audio.rate;
45 qr = "${toString quantum}/${toString rate}";
46 in {
47 enable = cfg.audio.enable;
48 pulse.enable = true;
49 jack.enable = true;
50 alsa = {
51 enable = true;
52 support32Bit = mkDefault true;
53 };
54
55 # write extra config
56 extraConfig.pipewire = mkIf cfg.audio.lowLatency {
57 "99-lowlatency" = {
58 context = {
59 properties.default.clock.min-quantum = quantum;
60 modules = [
61 {
62 name = "libpipewire-module-rtkit";
63 flags = ["ifexists" "nofail"];
64 args = {
65 nice.level = -15;
66 rt = {
67 prio = 88;
68 time.soft = 200000;
69 time.hard = 200000;
70 };
71 };
72 }
73 {
74 name = "libpipewire-module-protocol-pulse";
75 args = {
76 server.address = ["unix:native"];
77 pulse.min = {
78 req = qr;
79 quantum = qr;
80 frag = qr;
81 };
82 };
83 }
84 ];
85
86 stream.properties = {
87 node.latency = qr;
88 resample.quality = 1;
89 };
90 };
91 };
92 };
93
94 wireplumber = {
95 enable = true;
96 configPackages = let
97 # generate "matches" section of the rules
98 matches = toLua {
99 multiline = false; # looks better while inline
100 indent = false;
101 } [[["node.name" "matches" "alsa_output.*"]]]; # nested lists are to produce `{{{ }}}` in the output
102
103 # generate "apply_properties" section of the rules
104 apply_properties = toLua {} {
105 "audio.format" = "S32LE";
106 "audio.rate" = rate * 2;
107 "api.alsa.period-size" = 2;
108 };
109 in [
110 (pkgs.writeTextDir "share/lowlatency.lua.d/99-alsa-lowlatency.lua" ''
111 -- Generated by nix-gaming
112 alsa_monitor.rules = {
113 {
114 matches = ${matches};
115 apply_properties = ${apply_properties};
116 }
117 }
118 '')
119 ];
120 };
121 };
122 };
123 };
124}