projects/guix·blob python-team · refs

264 lines · 9.9 KiB

1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2023, 2026 Ludovic Courtès <[email protected]>
3;;; Copyright © 2023 Brian Cully <[email protected]>
4;;; Copyright © 2024 Janneke Nieuwenhuizen <[email protected]>
5;;;
6;;; This file is part of GNU Guix.
7;;;
8;;; GNU Guix is free software; you can redistribute it and/or modify it
9;;; under the terms of the GNU General Public License as published by
10;;; the Free Software Foundation; either version 3 of the License, or (at
11;;; your option) any later version.
12;;;
13;;; GNU Guix is distributed in the hope that it will be useful, but
14;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;;; GNU General Public License for more details.
17;;;
18;;; You should have received a copy of the GNU General Public License
19;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20
21(define-module (gnu home services sound)
22 #:use-module (gnu home services)
23 #:use-module (gnu home services shepherd)
24 #:use-module (gnu home services xdg)
25 #:use-module (gnu packages linux)
26 #:use-module (gnu services configuration)
27 #:use-module (guix records)
28 #:use-module (guix gexp)
29 #:use-module (srfi srfi-1)
30 #:use-module (ice-9 match)
31 #:export (home-pulseaudio-rtp-sink-service-type
32 home-pulseaudio-rtp-source-service-type
33 %pulseaudio-rtp-multicast-address
34
35 home-pipewire-configuration
36 home-pipewire-service-type))
37
38
39;;;
40;;; PipeWire support.
41;;;
42
43(define-configuration/no-serialization home-pipewire-configuration
44 (pipewire
45 (file-like pipewire)
46 "The PipeWire package to use.")
47 (wireplumber
48 (file-like wireplumber)
49 "The WirePlumber package to use.")
50 (enable-pulseaudio?
51 (boolean #t)
52 "When true, enable PipeWire's PulseAudio emulation support, allowing
53PulseAudio clients to use PipeWire transparently.")
54 (extra-content
55 (string "")
56 "Extra content to add to the end of @file{~/.config/alsa/asoundrc}."))
57
58(define (home-pipewire-shepherd-service config)
59 (shepherd-service
60 (documentation "PipeWire media processing.")
61 (provision '(pipewire))
62 (requirement '(dbus))
63 (start #~(make-forkexec-constructor
64 (list #$(file-append
65 (home-pipewire-configuration-pipewire config)
66 "/bin/pipewire"))))
67 (stop #~(make-kill-destructor))))
68
69(define (home-pipewire-pulseaudio-shepherd-service config)
70 (shepherd-service
71 (documentation "Drop-in PulseAudio replacement service for PipeWire.")
72 (provision '(pipewire-pulseaudio))
73 (requirement '(pipewire))
74 (start #~(make-forkexec-constructor
75 (list #$(file-append
76 (home-pipewire-configuration-pipewire config)
77 "/bin/pipewire-pulse"))))
78 (stop #~(make-kill-destructor))))
79
80(define (home-wireplumber-shepherd-service config)
81 (shepherd-service
82 (documentation "WirePlumber session management for PipeWire.")
83 (provision '(wireplumber))
84 (requirement '(pipewire))
85 (start #~(make-forkexec-constructor
86 (list #$(file-append
87 (home-pipewire-configuration-wireplumber config)
88 "/bin/wireplumber"))))
89 (stop #~(make-kill-destructor))))
90
91(define (home-pipewire-shepherd-services config)
92 (cons* (home-pipewire-shepherd-service config)
93 (home-wireplumber-shepherd-service config)
94 (if (home-pipewire-configuration-enable-pulseaudio? config)
95 (list (home-pipewire-pulseaudio-shepherd-service config))
96 '())))
97
98(define (home-pipewire-profile-service config)
99 (list (home-pipewire-configuration-pipewire config)
100 (home-pipewire-configuration-wireplumber config)))
101
102(define (home-pipewire-asoundrc config)
103 (match-record config <home-pipewire-configuration>
104 (pipewire extra-content)
105 (mixed-text-file
106 "asoundrc"
107 "<" pipewire "/share/alsa/alsa.conf.d/50-pipewire.conf>\n"
108 "<" pipewire "/share/alsa/alsa.conf.d/99-pipewire-default.conf>\n"
109 "pcm_type.pipewire {\n"
110 " lib \"" pipewire "/lib/alsa-lib/libasound_module_pcm_pipewire.so\"\n"
111 "}\n"
112 "ctl_type.pipewire {\n"
113 " lib \"" pipewire "/lib/alsa-lib/libasound_module_ctl_pipewire.so\"\n"
114 "}\n"
115 extra-content)))
116
117(define home-pipewire-disable-pulseaudio-auto-start
118 (plain-file "client.conf" "autospawn = no"))
119
120(define (home-pipewire-xdg-configuration config)
121 (cons* `("alsa/asoundrc" ,(home-pipewire-asoundrc config))
122 (if (home-pipewire-configuration-enable-pulseaudio? config)
123 `(("pulse/client.conf"
124 ,home-pipewire-disable-pulseaudio-auto-start))
125 '())))
126
127(define home-pipewire-service-type
128 (service-type
129 (name 'pipewire)
130 (extensions
131 (list (service-extension home-shepherd-service-type
132 home-pipewire-shepherd-services)
133 (service-extension home-profile-service-type
134 home-pipewire-profile-service)
135 (service-extension home-xdg-configuration-files-service-type
136 home-pipewire-xdg-configuration)))
137 (description
138 "Start essential PipeWire services.")
139 (default-value (home-pipewire-configuration))))
140
141
142;;;
143;;; PulseAudio support.
144;;;
145
146(define (with-pulseaudio-connection sock exp)
147 ;; Wrap EXP in an expression where SOCK is bound to a socket connected to
148 ;; the user's PulseAudio command-line interface socket.
149 #~(let* ((#$sock (socket AF_UNIX SOCK_STREAM 0))
150 (pulse-user-file
151 (lambda (name)
152 (string-append "/run/user/" (number->string (getuid))
153 "/pulse/" name)))
154 (file (pulse-user-file "cli")))
155 (let loop ((tries 0))
156 (catch #t
157 (lambda ()
158 (connect #$sock AF_UNIX file)
159 (let ((result #$exp))
160 (close-port #$sock)
161 result))
162 (lambda (key . args)
163 (if (and (eq? key 'system-error)
164 (= ENOENT (system-error-errno (cons key args)))
165 (< tries 3))
166 ;; The CLI socket doesn't exist yet, so send pulseaudio
167 ;; SIGUSR2 so that it creates it and listens to it.
168 (let ((pid (call-with-input-file (pulse-user-file "pid")
169 read)))
170 (when (and (integer? pid) (> pid 1))
171 (kill pid SIGUSR2))
172 ((@ (fibers) sleep) 1)
173 (loop (+ tries 1)))
174 (begin
175 (close-port #$sock)
176 (apply throw key args))))))))
177
178(define %pulseaudio-rtp-multicast-address
179 ;; Default address used by 'module-rtp-sink' and 'module-rtp-recv'. This is
180 ;; a multicast address, for the Session Announcement Protocol (SAP) and the
181 ;; Session Description Protocol (SDP).
182 "224.0.0.56")
183
184(define (pulseaudio-rtp-sink-shepherd-services destination-ip)
185 (list (shepherd-service
186 (provision '(pulseaudio-rtp-sink))
187 (start
188 #~(lambda* (#:optional (destination-ip #$destination-ip))
189 #$(with-pulseaudio-connection
190 #~sock
191 #~(begin
192 (display "\
193load-module module-null-sink \
194sink_name=rtp sink_properties=\"device.description='RTP network output'\"\n"
195 sock)
196 ;; Use a fixed sample rate to match what the receiving
197 ;; end is likely to expect.
198 (display (string-append "\
199load-module module-rtp-send source=rtp.monitor rate=44100"
200 (if destination-ip
201 (string-append
202 " destination_ip="
203 destination-ip)
204 "")
205 "\n")
206 sock)
207 #t))))
208 (stop
209 #~(lambda (_)
210 #$(with-pulseaudio-connection
211 #~sock
212 #~(begin
213 (display "unload-module module-rtp-send\n"
214 sock)
215 (display "unload-module module-null-sink\n"
216 sock)
217 #f))))
218 (auto-start? #f))))
219
220(define home-pulseaudio-rtp-sink-service-type
221 (service-type
222 (name 'pulseaudio-rtp-sink)
223 (extensions
224 (list (service-extension home-shepherd-service-type
225 pulseaudio-rtp-sink-shepherd-services)))
226 (description
227 "Define a PulseAudio sink to broadcast audio output over RTP, which can
228then by played by another PulseAudio instance.")
229
230 ;; By default, send to the SAP multicast address, 224.0.0.56, which can be
231 ;; network-intensive.
232 (default-value %pulseaudio-rtp-multicast-address)))
233
234(define (pulseaudio-rtp-source-shepherd-services source-ip)
235 (list (shepherd-service
236 (provision '(pulseaudio-rtp-source))
237 (start
238 #~(lambda* (#:optional (source-ip #$source-ip))
239 #$(with-pulseaudio-connection
240 #~sock
241 #~(begin
242 (format sock "\
243load-module module-rtp-recv sap_address=~a\n" source-ip)
244 #t))))
245 (stop
246 #~(lambda (_)
247 #$(with-pulseaudio-connection
248 #~sock
249 #~(begin
250 (display "unload-module module-rtp-recv\n"
251 sock)
252 #f))))
253 (auto-start? #f))))
254
255(define home-pulseaudio-rtp-source-service-type
256 (service-type
257 (name 'pulseaudio-rtp-source)
258 (extensions
259 (list (service-extension home-shepherd-service-type
260 pulseaudio-rtp-source-shepherd-services)))
261 (description
262 "Define a PulseAudio source to receive audio broadcasted over RTP by
263another PulseAudio instance.")
264 (default-value %pulseaudio-rtp-multicast-address)))