From 674d8933169e018efa3471e4eac52e5ea1e6afee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20Court=C3=A8s?= Date: Wed, 1 Mar 2023 23:08:28 +0100 Subject: [PATCH] home: services: Add 'pulseaudio-rtp-sink' and 'pulseaudio-rtp-source'. * gnu/home/services/sound.scm: New file. * gnu/local.mk (GNU_SYSTEM_MODULES): Add it. * doc/guix.texi (Sound Home Services): New section. --- doc/guix.texi | 114 ++++++++++++++++++++++++--- gnu/home/services/sound.scm | 151 ++++++++++++++++++++++++++++++++++++ gnu/local.mk | 3 +- 3 files changed, 258 insertions(+), 10 deletions(-) create mode 100644 gnu/home/services/sound.scm diff --git a/doc/guix.texi b/doc/guix.texi index 74658dbc86..6671ba9305 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -41679,15 +41679,16 @@ service with the @code{simple-service} procedure from @code{(gnu services)}. @menu -* Essential Home Services:: Environment variables, packages, on-* scripts. -* Shells: Shells Home Services. POSIX shells, Bash, Zsh. -* Mcron: Mcron Home Service. Scheduled User's Job Execution. -* Power Management: Power Management Home Services. Services for battery power. -* Shepherd: Shepherd Home Service. Managing User's Daemons. -* SSH: Secure Shell. Setting up the secure shell client. -* Desktop: Desktop Home Services. Services for graphical environments. -* Guix: Guix Home Services. Services for Guix. -* Fonts: Fonts Home Services. Services for managing User's fonts. +* Essential Home Services:: Environment variables, packages, on-* scripts. +* Shells: Shells Home Services. POSIX shells, Bash, Zsh. +* Mcron: Mcron Home Service. Scheduled User's Job Execution. +* Power Management: Power Management Home Services. Services for battery power. +* Shepherd: Shepherd Home Service. Managing User's Daemons. +* SSH: Secure Shell. Setting up the secure shell client. +* Desktop: Desktop Home Services. Services for graphical environments. +* Guix: Guix Home Services. Services for Guix. +* Fonts: Fonts Home Services. Services for managing User's fonts. +* Sound: Sound Home Services. Dealing with audio. @end menu @c In addition to that Home Services can provide @@ -42601,6 +42602,101 @@ like this: @end lisp @end defvar +@node Sound Home Services +@subsection Sound Home Services + +The @code{(gnu home services sound)} module provides services related to +sound support. + +@cindex PulseAudio, home service +@cindex RTP, for PulseAudio + +The following services dynamically reconfigure the +@uref{https://pulseaudio.org,PulseAudio sound server}: they let you +toggle broadcast of audio output over the network using the +@acronym{RTP, real-time transport protocol} and, correspondingly, +playback of sound received over RTP. Once +@code{home-pulseaudio-rtp-sink-service-type} is among your home +services, you can start broadcasting audio output by running this +command: + +@example +herd start pulseaudio-rtp-sink +@end example + +You can then run a PulseAudio-capable mixer, such as @code{pavucontrol} +or @code{pulsemixer} (both from the same-named package) to control which +audio stream(s) should be sent to the RTP ``sink''. + +By default, audio is broadcasted to a multicast address: any device on +the @acronym{LAN, local area network} receives it and may play it. +Using multicast in this way puts a lot of pressure on the network and +degrades its performance, so you may instead prefer sending to +specifically one device. The first way to do that is by specifying the +IP address of the target device when starting the service: + +@example +herd start pulseaudio-rtp-sink 192.168.1.42 +@end example + +The other option is to specify this IP address as the one to use by +default in your home environment configuration: + +@lisp +(service home-pulseaudio-rtp-sink-service-type + "192.168.1.42") +@end lisp + +On the device where you intend to receive and play the RTP stream, you +can use @code{home-pulseaudio-rtp-source-service-type}, like so: + +@lisp +(service home-pulseaudio-rtp-source-service-type) +@end lisp + +This will then let you start the receiving module for PulseAudio: + +@example +herd start pulseaudio-rtp-source +@end example + +Again, by default it will listen on the multicast address. If, instead, +you'd like it to listen for direct incoming connections, you can do that +by running: + +@lisp +(service home-pulseaudio-rtp-source-service-type + "0.0.0.0") +@end lisp + +The reference of these services is given below. + +@defvar home-pulseaudio-rtp-sink-service-type +@defvarx home-pulseaudio-rtp-source-service-type +This is the type of the service to send, respectively receive, audio +streams over @acronym{RTP, real-time transport protocol}. + +The value associated with this service is the IP address (a string) +where to send, respectively receive, the audio stream. By default, +audio is sent/received on multicast address +@code{%pulseaudio-rtp-multicast-address}. + +This service defines one Shepherd service: @code{pulseaudio-rtp-sink}, +respectively @code{pulseaudio-rtp-source}. The service is not started +by default, so you have to explicitly start it when you want to turn it +on, as in this example: + +@example +herd start pulseaudio-rtp-sink +@end example + +Stopping the Shepherd service turns off broadcasting. +@end defvar + +@defvar %pulseaudio-rtp-multicast-address +This is the multicast address used by default by the two services above. +@end defvar + @node Invoking guix home @section Invoking @command{guix home} diff --git a/gnu/home/services/sound.scm b/gnu/home/services/sound.scm new file mode 100644 index 0000000000..22c1a99250 --- /dev/null +++ b/gnu/home/services/sound.scm @@ -0,0 +1,151 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2023 Ludovic Courtès +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see . + +(define-module (gnu home services sound) + #:use-module (gnu home services) + #:use-module (gnu home services shepherd) + #:use-module (guix records) + #:use-module (guix gexp) + #:use-module (srfi srfi-1) + #:use-module (ice-9 match) + #:export (home-pulseaudio-rtp-sink-service-type + home-pulseaudio-rtp-source-service-type + %pulseaudio-rtp-multicast-address)) + + +;;; +;;; PulseAudio support. +;;; + +(define (with-pulseaudio-connection sock exp) + ;; Wrap EXP in an expression where SOCK is bound to a socket connected to + ;; the user's PulseAudio command-line interface socket. + #~(let* ((#$sock (socket AF_UNIX SOCK_STREAM 0)) + (pulse-user-file + (lambda (name) + (string-append "/run/user/" (number->string (getuid)) + "/pulse/" name))) + (file (pulse-user-file "cli"))) + (let loop ((tries 0)) + (catch #t + (lambda () + (connect #$sock AF_UNIX file) + (let ((result #$exp)) + (close-port #$sock) + result)) + (lambda (key . args) + (if (and (eq? key 'system-error) + (= ENOENT (system-error-errno (cons key args))) + (< tries 3)) + ;; The CLI socket doesn't exist yet, so send pulseaudio + ;; SIGUSR2 so that it creates it and listens to it. + (let ((pid (call-with-input-file (pulse-user-file "pid") + read))) + (when (and (integer? pid) (> pid 1)) + (kill pid SIGUSR2)) + ((@ (fibers) sleep) 1) + (loop (+ tries 1))) + (begin + (close-port #$sock) + (apply throw key args)))))))) + +(define %pulseaudio-rtp-multicast-address + ;; Default address used by 'module-rtp-sink' and 'module-rtp-recv'. This is + ;; a multicast address, for the Session Announcement Protocol (SAP) and the + ;; Session Description Protocol (SDP). + "224.0.0.56") + +(define (pulseaudio-rtp-sink-shepherd-services destination-ip) + (list (shepherd-service + (provision '(pulseaudio-rtp-sink)) + (start + #~(lambda* (#:optional (destination-ip #$destination-ip)) + #$(with-pulseaudio-connection + #~sock + #~(begin + (display "\ +load-module module-null-sink \ +sink_name=rtp sink_properties=\"device.description='RTP network output'\"\n" + sock) + (display (string-append "\ +load-module module-rtp-send source=rtp.monitor" + (if destination-ip + (string-append + " destination_ip=" + destination-ip) + "") + "\n") + sock) + #t)))) + (stop + #~(lambda (_) + #$(with-pulseaudio-connection + #~sock + #~(begin + (display "unload-module module-rtp-send\n" + sock) + (display "unload-module module-null-sink\n" + sock) + #f)))) + (auto-start? #f)))) + +(define home-pulseaudio-rtp-sink-service-type + (service-type + (name 'pulseaudio-rtp-sink) + (extensions + (list (service-extension home-shepherd-service-type + pulseaudio-rtp-sink-shepherd-services))) + (description + "Define a PulseAudio sink to broadcast audio output over RTP, which can +then by played by another PulseAudio instance.") + + ;; By default, send to the SAP multicast address, 224.0.0.56, which can be + ;; network-intensive. + (default-value %pulseaudio-rtp-multicast-address))) + +(define (pulseaudio-rtp-source-shepherd-services source-ip) + (list (shepherd-service + (provision '(pulseaudio-rtp-source)) + (start + #~(lambda* (#:optional (source-ip #$source-ip)) + #$(with-pulseaudio-connection + #~sock + #~(begin + (format sock "\ +load-module module-rtp-recv sap_address=~a\n" source-ip) + #t)))) + (stop + #~(lambda (_) + #$(with-pulseaudio-connection + #~sock + #~(begin + (display "unload-module module-rtp-recv\n" + sock) + #f)))) + (auto-start? #f)))) + +(define home-pulseaudio-rtp-source-service-type + (service-type + (name 'pulseaudio-rtp-source) + (extensions + (list (service-extension home-shepherd-service-type + pulseaudio-rtp-source-shepherd-services))) + (description + "Define a PulseAudio source to receive audio broadcasted over RTP by +another PulseAudio instance.") + (default-value %pulseaudio-rtp-multicast-address))) diff --git a/gnu/local.mk b/gnu/local.mk index e286e0b076..70dab40087 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -1,5 +1,5 @@ # GNU Guix --- Functional package management for GNU -# Copyright © 2012-2021, 2021-2022 Ludovic Courtès +# Copyright © 2012-2023 Ludovic Courtès # Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2022 Andreas Enge # Copyright © 2016 Mathieu Lirzin # Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Mark H Weaver @@ -94,6 +94,7 @@ GNU_SYSTEM_MODULES = \ %D%/home/services/pm.scm \ %D%/home/services/shells.scm \ %D%/home/services/shepherd.scm \ + %D%/home/services/sound.scm \ %D%/home/services/ssh.scm \ %D%/home/services/mcron.scm \ %D%/home/services/utils.scm \