[PATCH 0/2] services: mpd: Refactor MPD service.

  • Done
  • quality assurance status badge
Details
3 participants
  • Liliana Marie Prikler
  • mirai
  • (
Owner
unassigned
Submitted by
mirai
Severity
normal
M
M
mirai wrote on 7 Dec 2022 00:22
[PATCH 0/2] services: mpd: Refactor MPD service
(address . guix-patches@gnu.org)(address . mirai@makinata.eu)
cover.1670368385.git.mirai@makinata.eu
Modernizes and expands 'mpd-service-type'.
Performs "pretty-formatting" to the generated configuration file
at the cost of a few extra procedures.
It also deprecates some of the fields (abbreviated forms).

Bruno Victal (2):
services: mpd: use 'define-configuration'.
services: mpd: Refactor MPD service.

doc/guix.texi | 172 +++++++++++--
gnu/services/audio.scm | 549 ++++++++++++++++++++++++++++++-----------
2 files changed, 556 insertions(+), 165 deletions(-)


base-commit: b94724e8b2102be0fe9d19e9dfe44d6f7101bd4b
--
2.38.1
M
M
mirai wrote on 7 Dec 2022 00:25
[PATCH 1/2] services: mpd: use 'define-configuration'.
(address . 59866@debbugs.gnu.org)(name . Bruno Victal)(address . mirai@makinata.eu)
bd3ba0e684224d42c4a56b427ec3022a8c50fe38.1670368385.git.mirai@makinata.eu
From: Bruno Victal <mirai@makinata.eu>

---
gnu/services/audio.scm | 217 ++++++++++++++++++++++++-----------------
1 file changed, 129 insertions(+), 88 deletions(-)

Toggle diff (251 lines)
diff --git a/gnu/services/audio.scm b/gnu/services/audio.scm
index c60053f33c..2351db8a4a 100644
--- a/gnu/services/audio.scm
+++ b/gnu/services/audio.scm
@@ -21,6 +21,7 @@
(define-module (gnu services audio)
#:use-module (guix gexp)
#:use-module (gnu services)
+ #:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
#:use-module (gnu system shadow)
#:use-module (gnu packages admin)
@@ -28,6 +29,8 @@ (define-module (gnu services audio)
#:use-module (guix records)
#:use-module (ice-9 match)
#:use-module (ice-9 format)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-26)
#:export (mpd-output
mpd-output?
mpd-configuration
@@ -40,93 +43,131 @@ (define-module (gnu services audio)
;;;
;;; Code:
-(define-record-type* <mpd-output>
- mpd-output make-mpd-output
- mpd-output?
- (type mpd-output-type
- (default "pulse"))
- (name mpd-output-name
- (default "MPD"))
- (enabled? mpd-output-enabled?
- (default #t))
- (tags? mpd-output-tags?
- (default #t))
- (always-on? mpd-output-always-on?
- (default #f))
- (mixer-type mpd-output-mixer-type
- ;; valid: hardware, software, null, none
- (default #f))
- (extra-options mpd-output-extra-options
- (default '())))
-
-(define-record-type* <mpd-configuration>
- mpd-configuration make-mpd-configuration
- mpd-configuration?
- (user mpd-configuration-user
- (default "mpd"))
- (music-dir mpd-configuration-music-dir
- (default "~/Music"))
- (playlist-dir mpd-configuration-playlist-dir
- (default "~/.mpd/playlists"))
- (db-file mpd-configuration-db-file
- (default "~/.mpd/tag_cache"))
- (state-file mpd-configuration-state-file
- (default "~/.mpd/state"))
- (sticker-file mpd-configuration-sticker-file
- (default "~/.mpd/sticker.sql"))
- (port mpd-configuration-port
- (default "6600"))
- (address mpd-configuration-address
- (default "any"))
- (outputs mpd-configuration-outputs
- (default (list (mpd-output)))))
-
-(define (mpd-output->string output)
- "Convert the OUTPUT of type <mpd-output> to a configuration file snippet."
- (let ((extra (string-join
- (map (match-lambda
- ((key . value)
- (format #f " ~a \"~a\""
- (string-map
- (lambda (c) (if (char=? c #\-) #\_ c))
- (symbol->string key))
- value)))
- (mpd-output-extra-options output))
- "\n")))
- (format #f "\
-audio_output {
- type \"~a\"
- name \"~a\"
-~:[ enabled \"no\"~%~;~]\
-~:[ tags \"no\"~%~;~]\
-~:[~; always_on \"yes\"~%~]\
-~@[ mixer_type \"~a\"~%~]\
-~a~%}~%"
- (mpd-output-type output)
- (mpd-output-name output)
- (mpd-output-enabled? output)
- (mpd-output-tags? output)
- (mpd-output-always-on? output)
- (mpd-output-mixer-type output)
- extra)))
-
-(define (mpd-config->file config)
- (apply
- mixed-text-file "mpd.conf"
- "pid_file \"" (mpd-file-name config "pid") "\"\n"
- (append (map mpd-output->string
- (mpd-configuration-outputs config))
- (map (match-lambda
- ((config-name config-val)
- (string-append config-name " \"" (config-val config) "\"\n")))
- `(("user" ,mpd-configuration-user)
- ("music_directory" ,mpd-configuration-music-dir)
- ("playlist_directory" ,mpd-configuration-playlist-dir)
- ("db_file" ,mpd-configuration-db-file)
- ("state_file" ,mpd-configuration-state-file)
- ("sticker_file" ,mpd-configuration-sticker-file)
- ("port" ,mpd-configuration-port)
- ("bind_to_address" ,mpd-configuration-address))))))
+(define (uglify-field-name field-name)
+ (let ((str (symbol->string field-name)))
+ (string-join (string-split (if (string-suffix? "?" str)
+ (string-drop-right str 1)
+ str)
+ #\-)
+ "_")))
+
+(define (free-form-args? val)
+ (match val
+ (() #t)
+ ((((? symbol?) . (? string?)) . val) (free-form-args? val))
+ (_ #f)))
+
+(define* (mpd-serialize-field field-name value #:optional (indent-level 0))
+ #~(begin
+ (use-modules ((ice-9 format)))
+ (format #f "~v/~a \"~a\"~%" #$indent-level #$(if (string? field-name)
+ field-name
+ (uglify-field-name field-name)) #$value)))
+
+(define* (mpd-serialize-free-form-args field-name value #:optional (indent-level 0))
+ (generic-serialize-alist string-append (cut mpd-serialize-field <> <> indent-level) value))
+
+(define mpd-serialize-number mpd-serialize-field)
+
+(define mpd-serialize-string mpd-serialize-field)
+
+(define* (mpd-serialize-boolean field-name value #:optional (indent-level 0))
+ (mpd-serialize-field field-name (if value "yes" "no") indent-level))
+
+(define (mpd-serialize-list-of-mpd-output field-name value)
+ #~(string-append "\naudio_output {\n"
+ #$@(map (cut serialize-configuration <>
+ mpd-output-fields)
+ value)
+ "}\n"))
+
+(define (mpd-serialize-configuration configuration)
+ (mixed-text-file
+ "mpd.conf"
+ (serialize-configuration configuration mpd-configuration-fields)))
+
+(define mpd-subsystem-serialize-field (cut mpd-serialize-field <> <> 1))
+(define mpd-subsystem-serialize-string (cut mpd-serialize-string <> <> 1))
+(define mpd-subsystem-serialize-number (cut mpd-serialize-number <> <> 1))
+(define mpd-subsystem-serialize-boolean (cut mpd-serialize-boolean <> <> 1))
+(define mpd-subsystem-serialize-free-form-args (cut mpd-serialize-free-form-args <> <> 1))
+
+(define-configuration mpd-output
+ (name
+ (string "MPD")
+ "The name of the audio output.")
+ (type
+ (string "pulse")
+ "The type of audio output.")
+ (enabled?
+ (boolean #t)
+ "Specifies whether this audio output is enabled when MPD is started. By
+default, all audio outputs are enabled. This is just the default
+setting when there is no state file; with a state file, the previous
+state is restored.")
+ (tags?
+ (boolean #t)
+ "If set to @code{#f}, then MPD will not send tags to this output. This
+is only useful for output plugins that can receive tags, for example the
+@code{httpd} output plugin.")
+ (always-on?
+ (boolean #f)
+ "If set to @code{#t}, then MPD attempts to keep this audio output always
+open. This may be useful for streaming servers, when you don’t want to
+disconnect all listeners even when playback is accidentally stopped.")
+ (mixer-type
+ (string "none")
+ "This field accepts a symbol that specifies which mixer should be used
+for this audio output: the @code{hardware} mixer, the @code{software}
+mixer, the @code{null} mixer (allows setting the volume, but with no
+effect; this can be used as a trick to implement an external mixer
+External Mixer) or no mixer (@code{none}).")
+ (extra-options
+ (free-form-args '())
+ "An association list of option symbols to string values to be appended to
+the audio output configuration.")
+ (prefix mpd-subsystem-))
+
+(define list-of-mpd-output?
+ (list-of mpd-output?))
+
+(define-configuration mpd-configuration
+ (user
+ (string "mpd")
+ "The user to run mpd as.")
+ (music-dir
+ (string "~/Music")
+ "The directory to scan for music files."
+ (lambda (_ x)
+ (mpd-serialize-field "music_directory" x)))
+ (playlist-dir
+ (string "~/.mpd/playlists")
+ "The directory to store playlists."
+ (lambda (_ x)
+ (mpd-serialize-field "playlist_directory" x)))
+ (db-file
+ (string "~/.mpd/tag_cache")
+ "The location of the music database.")
+ (state-file
+ (string "~/.mpd/state")
+ "The location of the file that stores current MPD's state.")
+ (sticker-file
+ (string "~/.mpd/sticker.sql")
+ "The location of the sticker database.")
+ (port
+ (string "6600")
+ "The port to run mpd on.")
+ (address
+ (string "any")
+ "The address that mpd will bind to.
+To use a Unix domain socket, an absolute path can be specified here."
+ (lambda (_ x)
+ (mpd-serialize-field "bind_to_address" x)))
+ (outputs
+ (list-of-mpd-output (list (mpd-output)))
+ "The audio outputs that MPD can use.
+By default this is a single output using pulseaudio.")
+ (prefix mpd-))
(define (mpd-file-name config file)
"Return a path in /var/run/mpd/ that is writable
@@ -143,7 +184,7 @@ (define (mpd-shepherd-service config)
(start #~(make-forkexec-constructor
(list #$(file-append mpd "/bin/mpd")
"--no-daemon"
- #$(mpd-config->file config))
+ #$(mpd-serialize-configuration config))
#:environment-variables
;; Required to detect PulseAudio when run under a user account.
(list (string-append
--
2.38.1
M
M
mirai wrote on 7 Dec 2022 00:25
[PATCH 2/2] services: mpd: Refactor MPD service.
(address . 59866@debbugs.gnu.org)(name . Bruno Victal)(address . mirai@makinata.eu)
fd87503ea39dc6ad730e5dbf7156887924a64624.1670368385.git.mirai@makinata.eu
From: Bruno Victal <mirai@makinata.eu>

Introduces 'mpd-plugin' and 'mpd-partition' records.
Expands 'mpd-output' and 'mpd-configuration' records.
Deprecates redundant abbreviated fields in 'mpd-configuration' and
avoids serializing unused fields that may introduce undesired behavior.
Implement log-rotation via rottlog.
Implements Shepherd actions: 'reload' and 'configuration'.
---
doc/guix.texi | 172 ++++++++++++++---
gnu/services/audio.scm | 422 +++++++++++++++++++++++++++++++----------
2 files changed, 472 insertions(+), 122 deletions(-)

Toggle diff (527 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index a79b777826..0408cab1cb 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -109,6 +109,7 @@ Copyright @copyright{} 2022 Reily Siegel@*
Copyright @copyright{} 2022 Simon Streit@*
Copyright @copyright{} 2022 (@*
Copyright @copyright{} 2022 John Kehayias@*
+Copyright @copyright{} 2022 Bruno Victal@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -32823,79 +32824,184 @@ The service type for @command{mpd}
Data type representing the configuration of @command{mpd}.
@table @asis
-@item @code{user} (default: @code{"mpd"})
+@item @code{package} (default: @code{mpd}) (type: file-like)
+The MPD package.
+
+@item @code{user} (default: @code{"mpd"}) (type: string)
The user to run mpd as.
-@item @code{music-dir} (default: @code{"~/Music"})
+@item @code{group} (type: maybe-string)
+The group to run mpd as.
+
+@item @code{shepherd-requirement} (default: @code{()}) (type: list-of-symbol)
+This is a list of symbols naming Shepherd services that this service
+will depend on.
+
+@item @code{log-file} (default: @code{"/var/log/mpd/log"}) (type: maybe-string)
+The location of the log file. Set to @code{syslog} to use the local
+syslog daemon or @code{%unset-value} to omit this directive from the
+configuration file.
+
+@item @code{log-level} (type: maybe-string)
+Supress any messages below this threshold. Available values:
+@code{notice}, @code{info}, @code{verbose}, @code{warning} and
+@code{error}.
+
+@item @code{music-directory} (type: maybe-string)
The directory to scan for music files.
-@item @code{playlist-dir} (default: @code{"~/.mpd/playlists"})
+@item @code{playlist-directory} (type: maybe-string)
The directory to store playlists.
-@item @code{db-file} (default: @code{"~/.mpd/tag_cache"})
+@item @code{db-file} (type: maybe-string)
The location of the music database.
-@item @code{state-file} (default: @code{"~/.mpd/state"})
+@item @code{state-file} (type: maybe-string)
The location of the file that stores current MPD's state.
-@item @code{sticker-file} (default: @code{"~/.mpd/sticker.sql"})
+@item @code{sticker-file} (type: maybe-string)
The location of the sticker database.
-@item @code{port} (default: @code{"6600"})
+@item @code{port} (default: @code{6600}) (type: maybe-integer)
The port to run mpd on.
-@item @code{address} (default: @code{"any"})
-The address that mpd will bind to. To use a Unix domain socket,
-an absolute path can be specified here.
+@item @code{addresses} (type: maybe-list-of-string)
+The addresses that mpd will bind to. To use a Unix domain socket, an
+absolute path can be specified here.
+
+@item @code{database} (type: maybe-mpd-plugin)
+MPD database plugin configuration.
+
+@item @code{partitions} (default: @code{()}) (type: list-of-mpd-partition)
+List of MPD "partitions".
+
+@item @code{neighbors} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD neighbor plugin configurations.
-@item @code{outputs} (default: @code{"(list (mpd-output))"})
-The audio outputs that MPD can use. By default this is a single output using pulseaudio.
+@item @code{inputs} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD input plugin configurations.
+
+@item @code{archive-plugins} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD archive plugin configurations.
+
+@item @code{input-cache-size} (type: maybe-string)
+MPD input cache size.
+
+@item @code{decoders} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD decoder plugin configurations.
+
+@item @code{resampler} (type: maybe-mpd-plugin)
+MPD resampler plugin configuration.
+
+@item @code{filters} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD filter plugin configurations.
+
+@item @code{outputs} (type: list-of-mpd-output)
+The audio outputs that MPD can use. By default this is a single output
+using pulseaudio.
+
+@item @code{playlist-plugins} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD playlist plugin configurations.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the configuration.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-plugin
+Data type representing a @command{mpd} plugin.
+
+@table @asis
+@item @code{plugin} (type: maybe-string)
+Plugin name.
+
+@item @code{name} (type: maybe-string)
+Name.
+
+@item @code{enabled?} (type: maybe-boolean)
+Whether the plugin is enabled/disabled.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the plugin configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin
+reference} for available options.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-partition
+Data type representing a @command{mpd} partition.
+
+@table @asis
+@item @code{name} (type: string)
+Partition name.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the partition configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions,Configuring
+Partitions} for available options.
@end table
@end deftp
@deftp {Data Type} mpd-output
-Data type representing an @command{mpd} audio output.
+Data type representing a @command{mpd} audio output.
@table @asis
-@item @code{name} (default: @code{"MPD"})
+@item @code{name} (default: @code{"MPD"}) (type: string)
The name of the audio output.
-@item @code{type} (default: @code{"pulse"})
+@item @code{type} (default: @code{"pulse"}) (type: string)
The type of audio output.
-@item @code{enabled?} (default: @code{#t})
+@item @code{enabled?} (default: @code{#t}) (type: boolean)
Specifies whether this audio output is enabled when MPD is started. By
default, all audio outputs are enabled. This is just the default
setting when there is no state file; with a state file, the previous
state is restored.
-@item @code{tags?} (default: @code{#t})
+@item @code{format} (type: maybe-string)
+Force a specific audio format on output. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#audio-output-format,Global
+Audio Format} for a more detailed description.
+
+@item @code{tags?} (default: @code{#t}) (type: boolean)
If set to @code{#f}, then MPD will not send tags to this output. This
is only useful for output plugins that can receive tags, for example the
@code{httpd} output plugin.
-@item @code{always-on?} (default: @code{#f})
+@item @code{always-on?} (default: @code{#f}) (type: boolean)
If set to @code{#t}, then MPD attempts to keep this audio output always
-open. This may be useful for streaming servers, when you don’t want to
+open. This may be useful for streaming servers, when you don?t want to
disconnect all listeners even when playback is accidentally stopped.
-@item @code{mixer-type}
-This field accepts a symbol that specifies which mixer should be used
+@item @code{mixer-type} (default: @code{"none"}) (type: string)
+This field accepts a string that specifies which mixer should be used
for this audio output: the @code{hardware} mixer, the @code{software}
mixer, the @code{null} mixer (allows setting the volume, but with no
effect; this can be used as a trick to implement an external mixer
External Mixer) or no mixer (@code{none}).
-@item @code{extra-options} (default: @code{'()})
-An association list of option symbols to string values to be appended to
-the audio output configuration.
+@item @code{replay-gain-handler} (type: maybe-string)
+This field accepts a string that specifies how
+@uref{https://mpd.readthedocs.io/en/latest/user.html#replay-gain,Replay
+Gain} is to be applied. @code{software} uses an internal software
+volume control, @code{mixer} uses the configured (hardware) mixer
+control and @code{none} disables replay gain on this audio output.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the audio output configuration.
@end table
@end deftp
-The following example shows a configuration of @code{mpd} that provides
-an HTTP audio streaming output.
+The following example shows a configuration of @command{mpd} that
+configures some of its plugins and provides a HTTP audio streaming output.
@lisp
(service mpd-service-type
@@ -32907,7 +33013,19 @@ an HTTP audio streaming output.
(mixer-type 'null)
(extra-options
`((encoder . "vorbis")
- (port . "8080"))))))))
+ (port . "8080"))))))
+ (decoders
+ (list (mpd-plugin
+ (plugin "mikmod")
+ (enabled? #f))
+ (mpd-plugin
+ (plugin "openmpt")
+ (enabled? #t)
+ (extra-options `((repeat-count . -1)
+ (interpolation-filter . 1))))))
+ (resampler (mpd-plugin
+ (plugin "libsamplerate")
+ (extra-options `((type . 0)))))))
@end lisp
diff --git a/gnu/services/audio.scm b/gnu/services/audio.scm
index 2351db8a4a..b0587e0d3c 100644
--- a/gnu/services/audio.scm
+++ b/gnu/services/audio.scm
@@ -2,6 +2,7 @@
;;; Copyright © 2017 Peter Mikkelsen <petermikkelsen10@gmail.com>
;;; Copyright © 2019 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2022 Bruno Victal <mirai@makinata.eu>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -20,15 +21,18 @@
(define-module (gnu services audio)
#:use-module (guix gexp)
+ #:use-module (guix deprecation)
+ #:use-module (guix diagnostics)
+ #:use-module (guix i18n)
#:use-module (gnu services)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu services admin)
#:use-module (gnu system shadow)
#:use-module (gnu packages admin)
#:use-module (gnu packages mpd)
#:use-module (guix records)
#:use-module (ice-9 match)
- #:use-module (ice-9 format)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-26)
#:export (mpd-output
@@ -51,180 +55,406 @@ (define (uglify-field-name field-name)
#\-)
"_")))
-(define (free-form-args? val)
- (match val
- (() #t)
- ((((? symbol?) . (? string?)) . val) (free-form-args? val))
- (_ #f)))
+(define list-of-string?
+ (list-of string?))
-(define* (mpd-serialize-field field-name value #:optional (indent-level 0))
- #~(begin
- (use-modules ((ice-9 format)))
- (format #f "~v/~a \"~a\"~%" #$indent-level #$(if (string? field-name)
- field-name
- (uglify-field-name field-name)) #$value)))
+(define list-of-symbol?
+ (list-of symbol?))
-(define* (mpd-serialize-free-form-args field-name value #:optional (indent-level 0))
- (generic-serialize-alist string-append (cut mpd-serialize-field <> <> indent-level) value))
+(define* (mpd-serialize-field field-name value #:optional (indent-level 0))
+ (let ((field (if (string? field-name) field-name
+ (uglify-field-name field-name)))
+ (value (if (boolean? value) (if value "yes" "no") value)))
+ #~(begin
+ (use-modules (ice-9 format))
+ (format #f "~v/~a \"~a\"~%" #$indent-level #$field #$value))))
(define mpd-serialize-number mpd-serialize-field)
(define mpd-serialize-string mpd-serialize-field)
-(define* (mpd-serialize-boolean field-name value #:optional (indent-level 0))
- (mpd-serialize-field field-name (if value "yes" "no") indent-level))
+(define mpd-serialize-boolean mpd-serialize-field)
-(define (mpd-serialize-list-of-mpd-output field-name value)
- #~(string-append "\naudio_output {\n"
- #$@(map (cut serialize-configuration <>
- mpd-output-fields)
- value)
- "}\n"))
+(define* (mpd-serialize-alist field-name value #:optional (indent-level 0))
+ #~(string-append #$@(generic-serialize-alist list (cut mpd-serialize-field <> <> indent-level) value)))
-(define (mpd-serialize-configuration configuration)
- (mixed-text-file
- "mpd.conf"
- (serialize-configuration configuration mpd-configuration-fields)))
+(define-maybe string (prefix mpd-))
+(define-maybe list-of-string (prefix mpd-))
(define mpd-subsystem-serialize-field (cut mpd-serialize-field <> <> 1))
(define mpd-subsystem-serialize-string (cut mpd-serialize-string <> <> 1))
(define mpd-subsystem-serialize-number (cut mpd-serialize-number <> <> 1))
(define mpd-subsystem-serialize-boolean (cut mpd-serialize-boolean <> <> 1))
-(define mpd-subsystem-serialize-free-form-args (cut mpd-serialize-free-form-args <> <> 1))
+(define mpd-subsystem-serialize-alist (cut mpd-serialize-alist <> <> 1))
+
+(define-maybe string (prefix mpd-subsystem-))
+(define-maybe boolean (prefix mpd-subsystem-))
+
+;;; TODO: Procedures for deprecated fields, to be removed.
+
+(define mpd-deprecated-fields '((music-dir . music-directory)
+ (playlist-dir . playlist-directory)
+ (address . addresses)))
+
+(define (port? value) (or (string? value) (integer? value)))
+
+(define (mpd-serialize-deprecated-field field-name value)
+ (if (maybe-value-set? value)
+ (begin (warn-about-deprecation field-name #f
+ #:replacement (assoc-ref mpd-deprecated-fields field-name))
+ (match field-name
+ ('playlist-dir (mpd-serialize-string "playlist_directory" value))
+ ('music-dir (mpd-serialize-string "music_directory" value))
+ ('address (mpd-serialize-string "bind_to_address" value))))
+ ""))
+
+(define (mpd-serialize-port field-name value)
+ (when (string? value)
+ (warning (G_ "string value for '~a' is deprecated, use integer instead~%") field-name))
+ (mpd-serialize-field field-name value))
+
+(define-maybe port (prefix mpd-))
+
+;;;
+
+;; Generic MPD plugin record, lists only the most prevalent fields.
+(define-configuration mpd-plugin
+ (plugin
+ maybe-string
+ "Plugin name.")
+
+ (name
+ maybe-string
+ "Name.")
+
+ (enabled?
+ maybe-boolean
+ "Whether the plugin is enabled/disabled.")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols/strings to string values to be appended to
+the plugin configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin reference}
+for available options.")
+
+ (prefix mpd-subsystem-))
+
+;; Translate <mpd-configuration> field name into block name for mpd.conf"
+(define mpd-subsystem-name
+ (match-lambda
+ ('archive-plugins "archive_plugin")
+ ('playlist-plugins "playlist_plugin")
+ ('inputs "input")
+ ('decoders "decoder")
+ ('filters "filter")
+ (x (symbol->string x))))
+
+(define (mpd-serialize-mpd-plugin field-name value)
+ #~(string-append "\n" #$(mpd-subsystem-name field-name) " {\n"
+ #$(serialize-configuration value mpd-plugin-fields)
+ "}\n"))
+
+(define (mpd-serialize-list-of-mpd-plugin field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-plugin field-name <>) value)))
+
+(define list-of-mpd-plugin? (list-of mpd-plugin?))
+
+(define-maybe mpd-plugin (prefix mpd-))
+
+(define-configuration mpd-partition
+ (name
+ string
+ "Partition name.")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols/strings to string values to be appended to
+the partition configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions,Configuring Partitions}
+for available options.")
+
+ (prefix mpd-subsystem-))
+
+(define (mpd-serialize-mpd-partition field-name value)
+ #~(string-append "\npartition {\n"
+ #$(serialize-configuration value mpd-partition-fields)
+ "}\n"))
+
+(define (mpd-serialize-list-of-mpd-partition field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-partition #f <>) value)))
+
+(define list-of-mpd-partition?
+ (list-of mpd-partition?))
(define-configuration mpd-output
(name
(string "MPD")
"The name of the audio output.")
+
(type
(string "pulse")
"The type of audio output.")
+
(enabled?
(boolean #t)
"Specifies whether this audio output is enabled when MPD is started. By
default, all audio outputs are enabled. This is just the default
setting when there is no state file; with a state file, the previous
state is restored.")
+
+ (format
+ maybe-string
+ "Force a specific audio format on output. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#audio-output-format,Global Audio Format}
+for a more detailed description.")
+
(tags?
(boolean #t)
"If set to @code{#f}, then MPD will not send tags to this output. This
is only useful for output plugins that can receive tags, for example the
@code{httpd} output plugin.")
+
(always-on?
(boolean #f)
"If set to @code{#t}, then MPD attempts to keep this audio output always
open. This may be useful for streaming servers, when you don’t want to
disconnect all listeners even when playback is accidentally stopped.")
+
(mixer-type
(string "none")
- "This field accepts a symbol that specifies which mixer should be used
+ "This field accepts a string that specifies which mixer should be used
for this audio output: the @code{hardware} mixer, the @code{software}
mixer, the @code{null} mixer (allows setting the volume, but with no
effect; this can be used as a trick to implement an external mixer
External Mixer) or no mixer (@code{none}).")
+
+ (replay-gain-handler
+ maybe-string
+ "This field accepts a string that specifies how
+@uref{https://mpd.readthedocs.io/en/latest/user.html#replay-gain,Replay Gain} is
+to be applied. @code{software} uses an internal software volume control,
+@code{mixer} uses the configured (hardware) mixer control and @code{none}
+disables replay gain on this audio output.")
+
(extra-options
- (free-form-args '())
- "An association list of option symbols to string values to be appended to
+ (alist '())
+ "An association list of option symbols/strings to string values to be appended to
the audio output configuration.")
+
(prefix mpd-subsystem-))
+(define (mpd-serialize-mpd-output field-name value)
+ #~(string-append "\naudio_output {\n"
+ #$(serialize-configuration value mpd-output-fields)
+ "}\n"))
+
+(define (mpd-serialize-list-of-mpd-output field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-output #f <>) value)))
+
(define list-of-mpd-output?
(list-of mpd-output?))
(define-configuration mpd-configuration
+ (package
+ (file-like mpd)
+ "The MPD package."
+ empty-serializer)
+
(user
(string "mpd")
"The user to run mpd as.")
- (music-dir
- (string "~/Music")
+
+ (group
+ maybe-string
+ "The group to run mpd as.")
+
+ (shepherd-requirement
+ (list-o
This message was truncated. Download the full message here.
(
Re: [bug#59866] [PATCH 0/2] services: mpd: Refactor MPD service
COVGN7KIK265.2AP7SD2GJ0Y4H@guix-framework
On Tue Dec 6, 2022 at 11:22 PM GMT, wrote:
Toggle quote (18 lines)
> Modernizes and expands 'mpd-service-type'.
> Performs "pretty-formatting" to the generated configuration file
> at the cost of a few extra procedures.
> It also deprecates some of the fields (abbreviated forms).
>
> Bruno Victal (2):
> services: mpd: use 'define-configuration'.
> services: mpd: Refactor MPD service.
>
> doc/guix.texi | 172 +++++++++++--
> gnu/services/audio.scm | 549 ++++++++++++++++++++++++++++++-----------
> 2 files changed, 556 insertions(+), 165 deletions(-)
>
>
> base-commit: b94724e8b2102be0fe9d19e9dfe44d6f7101bd4b
> --
> 2.38.1

Could you also add support for ``herd configuration'' (added in #59197)?
-- (
-----BEGIN PGP SIGNATURE-----

iQGzBAABCgAdFiEE6Vh10NblKE5doNlW7ImHg/nqI20FAmOQVewACgkQ7ImHg/nq
I23FbQv/e6N7SbmFcjqJ7eAs3eNcwtP3FfMLxIqkFAKqi3IVJwQpyHY+J+OmQA4q
TEE1FBmM+nPJ1J/XG/ahHh2ACOULT9YZPaEwsndZQEPyoUr/cbdFQ9k0J0GNG62+
hcwEmlGpHRCAR4T1gjFj7hqvlI6Gt/S/kjG8Mygnj7Q82qv4V4LJcjbRf4Q8F4Q5
15EYjvPQRfFdgT0rm9QYQU1YX/qOCX4aIiZGPkETV3i9J6bPSghtjcXQgbX4WzzS
6T0HEIIOOeJPNf4lJ+DH+N5m3iLxKe+PTZJ2owpihymyE5YeUhGRoGk664MzNSmG
wuXaFMQdSJFQ8zDqywkTGMy834OA7ZXxNvScVtYBVJmuCh1Vj6k2KRJbsdpoDYws
w3dQW1NF29ijChDsu7lO9RViMIf6gatb/xz/UitrR0EFe9i0UFAGM9hMgwG5s1yE
yCFnogLZkxCStbEi599Jx2kwe8wnphqn3yZvm5L1jqmrOZgpwmLAASBbC68hZFug
8j/MIRxB
=2UDc
-----END PGP SIGNATURE-----


M
M
mirai wrote on 7 Dec 2022 14:42
f0eabd03-9523-efca-0399-b7cb3e852ed1@makinata.eu
It already does! The second commit introduces the bulk of the changes :)

On 2022-12-07 08:59, ( wrote:
Toggle quote (21 lines)
> On Tue Dec 6, 2022 at 11:22 PM GMT, wrote:
>> Modernizes and expands 'mpd-service-type'.
>> Performs "pretty-formatting" to the generated configuration file
>> at the cost of a few extra procedures.
>> It also deprecates some of the fields (abbreviated forms).
>>
>> Bruno Victal (2):
>> services: mpd: use 'define-configuration'.
>> services: mpd: Refactor MPD service.
>>
>> doc/guix.texi | 172 +++++++++++--
>> gnu/services/audio.scm | 549 ++++++++++++++++++++++++++++++-----------
>> 2 files changed, 556 insertions(+), 165 deletions(-)
>>
>>
>> base-commit: b94724e8b2102be0fe9d19e9dfe44d6f7101bd4b
>> --
>> 2.38.1
>
> Could you also add support for ``herd configuration'' (added in #59197)?
> -- (
(
COVMOR39ORBF.1UPCUSAICLT6L@guix-framework
Heya,

On Wed Dec 7, 2022 at 1:42 PM GMT, mirai wrote:
Toggle quote (2 lines)
> It already does! The second commit introduces the bulk of the changes :)

Oops :) Reviewing now...

-- (
-----BEGIN PGP SIGNATURE-----

iQGzBAABCgAdFiEE6Vh10NblKE5doNlW7ImHg/nqI20FAmOQmIQACgkQ7ImHg/nq
I23rXQv+JkbnxdWrWh17sL1WfjnKBfw1dbwDR5EOcwt3gyH95DYX0L8EtxWv1kwH
fnV+ZYIu1W1/gqrrkNWAWcXqIFliZjL7wmPIqxE4QdJ0eB9/QVLvp6Cx6nLsuYAK
TDVZUBLbKHgKIQlKR9dIyPILEgKxHW7E1x0lnaOda86KeiPerUyG3BEx/KbIqm92
Txk9agDrNVc3+vS+cyhgtnf0LvIw4CgMg19UjhZwOGm/n+/iPjtTysmwEbU3ZobP
Mv3LQR97LqOlc0UBjWy4wmKRSXigI8mR53D1nz0A8pcSElTIFCD/2nfcl3dRg7GH
fDbPpUHW3qGwHiLKX7supcu8ge/LMcNJpdHbtxUFrroegI6ODGHyuKfo5wodc7mn
wRuvTVWsXpunRl9V9/NyZO40ao43a54VYOXeKTSBXunGlqIh4uRhPjSTD+EWT4hv
7FBLcU8OXDj2j9mAAHzUEnWZMJgsDhcjcpixxRSb2Hvorpi5/nmXuAR6XM8dZ8+Y
YcDJ81Qn
=3VPs
-----END PGP SIGNATURE-----


L
L
Liliana Marie Prikler wrote on 7 Dec 2022 19:27
Re: [PATCH 0/2] services: mpd: Refactor MPD service
6e66967984d1bc22d8abf5dd4b07c1a20b4b06ee.camel@gmail.com
Am Dienstag, dem 06.12.2022 um 23:22 +0000 schrieb mirai@makinata.eu:
Toggle quote (8 lines)
> Modernizes and expands 'mpd-service-type'.
> Performs "pretty-formatting" to the generated configuration file
> at the cost of a few extra procedures.
> It also deprecates some of the fields (abbreviated forms).
>
> Bruno Victal (2):
>   services: mpd: use 'define-configuration'.
>   services: mpd: Refactor MPD service.
Note that there is [1], which attempts to make it so that shepherd
endpoints can be specified in lieu of MPD endpoints. You don't need to
implement this logic – you are free to do so if you want to – but could
you make it so that there is an explicit endpoint abstraction that
would allow for extension later on?

Cheers

M
M
mirai wrote on 8 Dec 2022 01:59
[PATCH v2] services: mpd: Refactor MPD service.
(address . 59866@debbugs.gnu.org)(name . Bruno Victal)(address . mirai@makinata.eu)
358a67f1493b55505ec5907750722fd43656e4d4.1670461192.git.mirai@makinata.eu
From: Bruno Victal <mirai@makinata.eu>

Introduces 'mpd-plugin' and 'mpd-partition' records.
Expands 'mpd-output' and 'mpd-configuration' records.
Deprecates redundant abbreviated fields in 'mpd-configuration' and
avoids serializing unused fields that may introduce undesired behavior.
Implement log-rotation via rottlog.
Implements Shepherd actions: 'reload' and 'configuration'.
---

Slight oversight, mpd-plugin and mpd-partition record types weren't
being exported.

doc/guix.texi | 172 ++++++++++++++---
gnu/services/audio.scm | 426 ++++++++++++++++++++++++++++++++---------
2 files changed, 476 insertions(+), 122 deletions(-)

Toggle diff (518 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 50487a5172..5a009df240 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -109,6 +109,7 @@ Copyright @copyright{} 2022 Reily Siegel@*
Copyright @copyright{} 2022 Simon Streit@*
Copyright @copyright{} 2022 (@*
Copyright @copyright{} 2022 John Kehayias@*
+Copyright @copyright{} 2022 Bruno Victal@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -32823,79 +32824,184 @@ The service type for @command{mpd}
Data type representing the configuration of @command{mpd}.
@table @asis
-@item @code{user} (default: @code{"mpd"})
+@item @code{package} (default: @code{mpd}) (type: file-like)
+The MPD package.
+
+@item @code{user} (default: @code{"mpd"}) (type: string)
The user to run mpd as.
-@item @code{music-dir} (default: @code{"~/Music"})
+@item @code{group} (type: maybe-string)
+The group to run mpd as.
+
+@item @code{shepherd-requirement} (default: @code{()}) (type: list-of-symbol)
+This is a list of symbols naming Shepherd services that this service
+will depend on.
+
+@item @code{log-file} (default: @code{"/var/log/mpd/log"}) (type: maybe-string)
+The location of the log file. Set to @code{syslog} to use the local
+syslog daemon or @code{%unset-value} to omit this directive from the
+configuration file.
+
+@item @code{log-level} (type: maybe-string)
+Supress any messages below this threshold. Available values:
+@code{notice}, @code{info}, @code{verbose}, @code{warning} and
+@code{error}.
+
+@item @code{music-directory} (type: maybe-string)
The directory to scan for music files.
-@item @code{playlist-dir} (default: @code{"~/.mpd/playlists"})
+@item @code{playlist-directory} (type: maybe-string)
The directory to store playlists.
-@item @code{db-file} (default: @code{"~/.mpd/tag_cache"})
+@item @code{db-file} (type: maybe-string)
The location of the music database.
-@item @code{state-file} (default: @code{"~/.mpd/state"})
+@item @code{state-file} (type: maybe-string)
The location of the file that stores current MPD's state.
-@item @code{sticker-file} (default: @code{"~/.mpd/sticker.sql"})
+@item @code{sticker-file} (type: maybe-string)
The location of the sticker database.
-@item @code{port} (default: @code{"6600"})
+@item @code{port} (default: @code{6600}) (type: maybe-integer)
The port to run mpd on.
-@item @code{address} (default: @code{"any"})
-The address that mpd will bind to. To use a Unix domain socket,
-an absolute path can be specified here.
+@item @code{addresses} (type: maybe-list-of-string)
+The addresses that mpd will bind to. To use a Unix domain socket, an
+absolute path can be specified here.
+
+@item @code{database} (type: maybe-mpd-plugin)
+MPD database plugin configuration.
+
+@item @code{partitions} (default: @code{()}) (type: list-of-mpd-partition)
+List of MPD "partitions".
+
+@item @code{neighbors} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD neighbor plugin configurations.
-@item @code{outputs} (default: @code{"(list (mpd-output))"})
-The audio outputs that MPD can use. By default this is a single output using pulseaudio.
+@item @code{inputs} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD input plugin configurations.
+
+@item @code{archive-plugins} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD archive plugin configurations.
+
+@item @code{input-cache-size} (type: maybe-string)
+MPD input cache size.
+
+@item @code{decoders} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD decoder plugin configurations.
+
+@item @code{resampler} (type: maybe-mpd-plugin)
+MPD resampler plugin configuration.
+
+@item @code{filters} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD filter plugin configurations.
+
+@item @code{outputs} (type: list-of-mpd-output)
+The audio outputs that MPD can use. By default this is a single output
+using pulseaudio.
+
+@item @code{playlist-plugins} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD playlist plugin configurations.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the configuration.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-plugin
+Data type representing a @command{mpd} plugin.
+
+@table @asis
+@item @code{plugin} (type: maybe-string)
+Plugin name.
+
+@item @code{name} (type: maybe-string)
+Name.
+
+@item @code{enabled?} (type: maybe-boolean)
+Whether the plugin is enabled/disabled.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the plugin configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin
+reference} for available options.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-partition
+Data type representing a @command{mpd} partition.
+
+@table @asis
+@item @code{name} (type: string)
+Partition name.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the partition configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions,Configuring
+Partitions} for available options.
@end table
@end deftp
@deftp {Data Type} mpd-output
-Data type representing an @command{mpd} audio output.
+Data type representing a @command{mpd} audio output.
@table @asis
-@item @code{name} (default: @code{"MPD"})
+@item @code{name} (default: @code{"MPD"}) (type: string)
The name of the audio output.
-@item @code{type} (default: @code{"pulse"})
+@item @code{type} (default: @code{"pulse"}) (type: string)
The type of audio output.
-@item @code{enabled?} (default: @code{#t})
+@item @code{enabled?} (default: @code{#t}) (type: boolean)
Specifies whether this audio output is enabled when MPD is started. By
default, all audio outputs are enabled. This is just the default
setting when there is no state file; with a state file, the previous
state is restored.
-@item @code{tags?} (default: @code{#t})
+@item @code{format} (type: maybe-string)
+Force a specific audio format on output. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#audio-output-format,Global
+Audio Format} for a more detailed description.
+
+@item @code{tags?} (default: @code{#t}) (type: boolean)
If set to @code{#f}, then MPD will not send tags to this output. This
is only useful for output plugins that can receive tags, for example the
@code{httpd} output plugin.
-@item @code{always-on?} (default: @code{#f})
+@item @code{always-on?} (default: @code{#f}) (type: boolean)
If set to @code{#t}, then MPD attempts to keep this audio output always
-open. This may be useful for streaming servers, when you don’t want to
+open. This may be useful for streaming servers, when you don?t want to
disconnect all listeners even when playback is accidentally stopped.
-@item @code{mixer-type}
-This field accepts a symbol that specifies which mixer should be used
+@item @code{mixer-type} (default: @code{"none"}) (type: string)
+This field accepts a string that specifies which mixer should be used
for this audio output: the @code{hardware} mixer, the @code{software}
mixer, the @code{null} mixer (allows setting the volume, but with no
effect; this can be used as a trick to implement an external mixer
External Mixer) or no mixer (@code{none}).
-@item @code{extra-options} (default: @code{'()})
-An association list of option symbols to string values to be appended to
-the audio output configuration.
+@item @code{replay-gain-handler} (type: maybe-string)
+This field accepts a string that specifies how
+@uref{https://mpd.readthedocs.io/en/latest/user.html#replay-gain,Replay
+Gain} is to be applied. @code{software} uses an internal software
+volume control, @code{mixer} uses the configured (hardware) mixer
+control and @code{none} disables replay gain on this audio output.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the audio output configuration.
@end table
@end deftp
-The following example shows a configuration of @code{mpd} that provides
-an HTTP audio streaming output.
+The following example shows a configuration of @command{mpd} that
+configures some of its plugins and provides a HTTP audio streaming output.
@lisp
(service mpd-service-type
@@ -32907,7 +33013,19 @@ an HTTP audio streaming output.
(mixer-type 'null)
(extra-options
`((encoder . "vorbis")
- (port . "8080"))))))))
+ (port . "8080"))))))
+ (decoders
+ (list (mpd-plugin
+ (plugin "mikmod")
+ (enabled? #f))
+ (mpd-plugin
+ (plugin "openmpt")
+ (enabled? #t)
+ (extra-options `((repeat-count . -1)
+ (interpolation-filter . 1))))))
+ (resampler (mpd-plugin
+ (plugin "libsamplerate")
+ (extra-options `((type . 0)))))))
@end lisp
diff --git a/gnu/services/audio.scm b/gnu/services/audio.scm
index 2351db8a4a..07baf8554c 100644
--- a/gnu/services/audio.scm
+++ b/gnu/services/audio.scm
@@ -2,6 +2,7 @@
;;; Copyright © 2017 Peter Mikkelsen <petermikkelsen10@gmail.com>
;;; Copyright © 2019 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2022 Bruno Victal <mirai@makinata.eu>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -20,19 +21,26 @@
(define-module (gnu services audio)
#:use-module (guix gexp)
+ #:use-module (guix deprecation)
+ #:use-module (guix diagnostics)
+ #:use-module (guix i18n)
#:use-module (gnu services)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu services admin)
#:use-module (gnu system shadow)
#:use-module (gnu packages admin)
#:use-module (gnu packages mpd)
#:use-module (guix records)
#:use-module (ice-9 match)
- #:use-module (ice-9 format)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-26)
#:export (mpd-output
mpd-output?
+ mpd-plugin
+ mpd-plugin?
+ mpd-partition
+ mpd-partition?
mpd-configuration
mpd-configuration?
mpd-service-type))
@@ -51,180 +59,406 @@ (define (uglify-field-name field-name)
#\-)
"_")))
-(define (free-form-args? val)
- (match val
- (() #t)
- ((((? symbol?) . (? string?)) . val) (free-form-args? val))
- (_ #f)))
+(define list-of-string?
+ (list-of string?))
-(define* (mpd-serialize-field field-name value #:optional (indent-level 0))
- #~(begin
- (use-modules ((ice-9 format)))
- (format #f "~v/~a \"~a\"~%" #$indent-level #$(if (string? field-name)
- field-name
- (uglify-field-name field-name)) #$value)))
+(define list-of-symbol?
+ (list-of symbol?))
-(define* (mpd-serialize-free-form-args field-name value #:optional (indent-level 0))
- (generic-serialize-alist string-append (cut mpd-serialize-field <> <> indent-level) value))
+(define* (mpd-serialize-field field-name value #:optional (indent-level 0))
+ (let ((field (if (string? field-name) field-name
+ (uglify-field-name field-name)))
+ (value (if (boolean? value) (if value "yes" "no") value)))
+ #~(begin
+ (use-modules (ice-9 format))
+ (format #f "~v/~a \"~a\"~%" #$indent-level #$field #$value))))
(define mpd-serialize-number mpd-serialize-field)
(define mpd-serialize-string mpd-serialize-field)
-(define* (mpd-serialize-boolean field-name value #:optional (indent-level 0))
- (mpd-serialize-field field-name (if value "yes" "no") indent-level))
+(define mpd-serialize-boolean mpd-serialize-field)
-(define (mpd-serialize-list-of-mpd-output field-name value)
- #~(string-append "\naudio_output {\n"
- #$@(map (cut serialize-configuration <>
- mpd-output-fields)
- value)
- "}\n"))
+(define* (mpd-serialize-alist field-name value #:optional (indent-level 0))
+ #~(string-append #$@(generic-serialize-alist list (cut mpd-serialize-field <> <> indent-level) value)))
-(define (mpd-serialize-configuration configuration)
- (mixed-text-file
- "mpd.conf"
- (serialize-configuration configuration mpd-configuration-fields)))
+(define-maybe string (prefix mpd-))
+(define-maybe list-of-string (prefix mpd-))
(define mpd-subsystem-serialize-field (cut mpd-serialize-field <> <> 1))
(define mpd-subsystem-serialize-string (cut mpd-serialize-string <> <> 1))
(define mpd-subsystem-serialize-number (cut mpd-serialize-number <> <> 1))
(define mpd-subsystem-serialize-boolean (cut mpd-serialize-boolean <> <> 1))
-(define mpd-subsystem-serialize-free-form-args (cut mpd-serialize-free-form-args <> <> 1))
+(define mpd-subsystem-serialize-alist (cut mpd-serialize-alist <> <> 1))
+
+(define-maybe string (prefix mpd-subsystem-))
+(define-maybe boolean (prefix mpd-subsystem-))
+
+;;; TODO: Procedures for deprecated fields, to be removed.
+
+(define mpd-deprecated-fields '((music-dir . music-directory)
+ (playlist-dir . playlist-directory)
+ (address . addresses)))
+
+(define (port? value) (or (string? value) (integer? value)))
+
+(define (mpd-serialize-deprecated-field field-name value)
+ (if (maybe-value-set? value)
+ (begin (warn-about-deprecation field-name #f
+ #:replacement (assoc-ref mpd-deprecated-fields field-name))
+ (match field-name
+ ('playlist-dir (mpd-serialize-string "playlist_directory" value))
+ ('music-dir (mpd-serialize-string "music_directory" value))
+ ('address (mpd-serialize-string "bind_to_address" value))))
+ ""))
+
+(define (mpd-serialize-port field-name value)
+ (when (string? value)
+ (warning (G_ "string value for '~a' is deprecated, use integer instead~%") field-name))
+ (mpd-serialize-field field-name value))
+
+(define-maybe port (prefix mpd-))
+
+;;;
+
+;; Generic MPD plugin record, lists only the most prevalent fields.
+(define-configuration mpd-plugin
+ (plugin
+ maybe-string
+ "Plugin name.")
+
+ (name
+ maybe-string
+ "Name.")
+
+ (enabled?
+ maybe-boolean
+ "Whether the plugin is enabled/disabled.")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols/strings to string values to be appended to
+the plugin configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin reference}
+for available options.")
+
+ (prefix mpd-subsystem-))
+
+;; Translate <mpd-configuration> field name into block name for mpd.conf"
+(define mpd-subsystem-name
+ (match-lambda
+ ('archive-plugins "archive_plugin")
+ ('playlist-plugins "playlist_plugin")
+ ('inputs "input")
+ ('decoders "decoder")
+ ('filters "filter")
+ (x (symbol->string x))))
+
+(define (mpd-serialize-mpd-plugin field-name value)
+ #~(string-append "\n" #$(mpd-subsystem-name field-name) " {\n"
+ #$(serialize-configuration value mpd-plugin-fields)
+ "}\n"))
+
+(define (mpd-serialize-list-of-mpd-plugin field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-plugin field-name <>) value)))
+
+(define list-of-mpd-plugin? (list-of mpd-plugin?))
+
+(define-maybe mpd-plugin (prefix mpd-))
+
+(define-configuration mpd-partition
+ (name
+ string
+ "Partition name.")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols/strings to string values to be appended to
+the partition configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions,Configuring Partitions}
+for available options.")
+
+ (prefix mpd-subsystem-))
+
+(define (mpd-serialize-mpd-partition field-name value)
+ #~(string-append "\npartition {\n"
+ #$(serialize-configuration value mpd-partition-fields)
+ "}\n"))
+
+(define (mpd-serialize-list-of-mpd-partition field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-partition #f <>) value)))
+
+(define list-of-mpd-partition?
+ (list-of mpd-partition?))
(define-configuration mpd-output
(name
(string "MPD")
"The name of the audio output.")
+
(type
(string "pulse")
"The type of audio output.")
+
(enabled?
(boolean #t)
"Specifies whether this audio output is enabled when MPD is started. By
default, all audio outputs are enabled. This is just the default
setting when there is no state file; with a state file, the previous
state is restored.")
+
+ (format
+ maybe-string
+ "Force a specific audio format on output. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#audio-output-format,Global Audio Format}
+for a more detailed description.")
+
(tags?
(boolean #t)
"If set to @code{#f}, then MPD will not send tags to this output. This
is only useful for output plugins that can receive tags, for example the
@code{httpd} output plugin.")
+
(always-on?
(boolean #f)
"If set to @code{#t}, then MPD attempts to keep this audio output always
open. This may be useful for streaming servers, when you don’t want to
disconnect all listeners even when playback is accidentally stopped.")
+
(mixer-type
(string "none")
- "This field accepts a symbol that specifies which mixer should be used
+ "This field accepts a string that specifies which mixer should be used
for this audio output: the @code{hardware} mixer, the @code{software}
mixer, the @code{null} mixer (allows setting the volume, but with no
effect; this can be used as a trick to implement an external mixer
External Mixer) or no mixer (@code{none}).")
+
+ (replay-gain-handler
+ maybe-string
+ "This field accepts a string that specifies how
+@uref{https://mpd.readthedocs.io/en/latest/user.html#replay-gain,Replay Gain} is
+to be applied. @code{software} uses an internal software volume control,
+@code{mixer} uses the configured (hardware) mixer control and @code{none}
+disables replay gain on this audio output.")
+
(extra-options
- (free-form-args '())
- "An association list of option symbols to string values to be appended to
+ (alist '())
+ "An association list of option symbols/strings to string values to be appended to
the audio output configuration.")
+
(prefix mpd-subsystem-))
+(define (mpd-serialize-mpd-output field-name value)
+ #~(string-append "\naudio_output {\n"
+ #$(serialize-configuration value mpd-output-fields)
+ "}\n"))
+
+(define (mpd-serialize-list-of-mpd-output field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-output #f <>) value)))
+
(define list-of-mpd-output?
(list-of mpd-output?))
(def
This message was truncated. Download the full message here.
M
M
mirai wrote on 8 Dec 2022 14:11
Re: [PATCH 0/2] services: mpd: Refactor MPD service
(address . 54986@debbugs.gnu.org)
71f31a0d-cc58-a0e6-4aa4-b5c46513c835@makinata.eu
On 2022-12-07 18:27, Liliana Marie Prikler wrote:
Toggle quote (10 lines)
> Note that there is [1], which attempts to make it so that shepherd
> endpoints can be specified in lieu of MPD endpoints. You don't need to
> implement this logic – you are free to do so if you want to – but could
> you make it so that there is an explicit endpoint abstraction that
> would allow for extension later on?
>
> Cheers
>
> [1] https://issues.guix.gnu.org/54986

Hi,

After reading issue #54986, regarding mpd escaping shepherd management,
my guess is that there could be two issues at play here:

* mpd.conf was serialized with pid_file [1]. The safest is
to actually not serialize any unused directives and let MPD decide
based on what's actually present. pid_file should only be set if
MPD is to be launched as a "daemon process". [2]

* But the service definition for MPD is launched with '--no-daemon' option
as seen in [3], which could be triggering a bug in MPD. It's probably worth
testing the combinations of having pid_file set and invoking --no-daemon.


Toggle quote (4 lines)
> but could
> you make it so that there is an explicit endpoint abstraction that
> would allow for extension later on?

If I'm understanding correctly what [1] is about, I think this can be
implemented through the 'extend' interface. See how
certbot adds a nginx-server-configuration to a nginx-service-type
through the 'extend' interface. For MPD, this would amount to
appending the 'adresses' field with "adequately formatted" values.


L
L
Liliana Marie Prikler wrote on 8 Dec 2022 14:35
(address . 54986@debbugs.gnu.org)
c2c097ea2491638d0553e2a3bea65e571e5e595a.camel@gmail.com
Am Donnerstag, dem 08.12.2022 um 13:11 +0000 schrieb mirai:
Toggle quote (25 lines)
> On 2022-12-07 18:27, Liliana Marie Prikler wrote:
> > Note that there is [1], which attempts to make it so that shepherd
> > endpoints can be specified in lieu of MPD endpoints.  You don't
> > need to implement this logic – you are free to do so if you want to
> > – but could you make it so that there is an explicit endpoint
> > abstraction that would allow for extension later on?
> >
> > Cheers
> >
> > [1] https://issues.guix.gnu.org/54986
>
> Hi,
>
> After reading issue #54986, regarding mpd escaping shepherd
> management, my guess is that there could be two issues at play here:
>
> * mpd.conf was serialized with pid_file [1]. The safest is
> to actually not serialize any unused directives and let MPD decide
> based on what's actually present. pid_file should only be set if
> MPD is to be launched as a "daemon process". [2]
>
> * But the service definition for MPD is launched with '--no-daemon'
> option as seen in [3], which could be triggering a bug in MPD. It's
> probably worth testing the combinations of having pid_file set and
> invoking --no-daemon.
Nice catch, but completely unrelated to the issue. In neither case do
we ever spawn MPD as a regular daemon, yet only in the systemd case
does shepherd have a problem with it. That statement was concerning
shepherd's (mis)management of sockets, not MPD.

Toggle quote (8 lines)
> > but could you make it so that there is an explicit endpoint
> > abstraction that would allow for extension later on?
>
> If I'm understanding correctly what [1] is about, I think this can be
> implemented through the 'extend' interface. See how certbot adds a
> nginx-server-configuration to a nginx-service-type through the
> 'extend' interface. For MPD, this would amount to appending the
> 'adresses' field with "adequately formatted" values.
This doesn't work for #54986, which makes it so that in-file addresses
are ignored in favour of handing over the sockets directly through
shepherd. Looking at [4], it appears the meaning of "port" is closer
to that of a default port, as addresses can have ports in them. But I
would still prefer addresses to be "endpoints", which if they happen to
be a list of strings are taken as MPD addresses and if they happen to
be shepherd endpoints are passed on to the shepherd service.

WDYT?

M
M
mirai wrote on 9 Dec 2022 14:44
(address . 54986@debbugs.gnu.org)
d5439211-66b2-0938-2de4-1847b22ce0e2@makinata.eu
On 2022-12-08 13:35, Liliana Marie Prikler wrote:
Toggle quote (8 lines)
> This doesn't work for #54986, which makes it so that in-file addresses
> are ignored in favour of handing over the sockets directly through
> shepherd. Looking at [4], it appears the meaning of "port" is closer
> to that of a default port, as addresses can have ports in them. But I
> would still prefer addresses to be "endpoints", which if they happen to
> be a list of strings are taken as MPD addresses and if they happen to
> be shepherd endpoints are passed on to the shepherd service.

Are you proposing for the 'addresses' field to be a
"maybe-list-of-string-or-shepherd-endpoint"? (more of a xor as they can't
be used simultaneously)
Example:

Toggle snippet (7 lines)
;; should fire a error message during guix system reconfigure
(mpd-configuration
(addresses `("[::]:6645"
,(shepherd-endpoint
(address "/var/run/mpd-shepherd-socket")))))

I don't think it breaks backward compatibility to introduce this
after #59866 is merged.
The type of field 'addresses' could be changed transparently to something like:

Toggle snippet (2 lines)
(define list-of-addresses (list-of (lambda (x) (or (string? x) (shepherd-endpoint? x)))))
M
M
mirai wrote on 9 Dec 2022 14:52
(no subject)
(name . control)(address . control@debbugs.gnu.org)
d14f869f-3ae7-a539-9195-cdcbf5176c25@makinata.eu
# mpd-output represents a MPD plugin, so mpd-plugin type should also be accepted as a valid alternative to outputs field.
# "Pretty-formatting" could be removed to reduce redundant code
# Shepherd endpoints?
retitle 59866 [WIP] [PATCH 0/2] services: mpd: Refactor MPD service
quit
L
L
Liliana Marie Prikler wrote on 9 Dec 2022 20:22
Re: [PATCH 0/2] services: mpd: Refactor MPD service
(address . 54986@debbugs.gnu.org)
4e6249e0970fcd02ac4666ce1fd40203b4b09003.camel@gmail.com
Am Freitag, dem 09.12.2022 um 13:44 +0000 schrieb mirai:
Toggle quote (33 lines)
> On 2022-12-08 13:35, Liliana Marie Prikler wrote:
> > This doesn't work for #54986, which makes it so that in-file
> > addresses are ignored in favour of handing over the sockets
> > directly through shepherd.  Looking at [4], it appears the meaning
> > of "port" is closer to that of a default port, as addresses can
> > have ports in them. 
> > But I would still prefer addresses to be "endpoints", which if they
> > happen to be a list of strings are taken as MPD addresses and if
> > they happen to be shepherd endpoints are passed on to the shepherd
> > service.
>
> Are you proposing for the 'addresses' field to be a
> "maybe-list-of-string-or-shepherd-endpoint"? (more of a xor as they
> can't be used simultaneously)
> Example:
>
> --8<---------------cut here---------------start------------->8---
> ;; should fire a error message during guix system reconfigure
> (mpd-configuration
>   (addresses `("[::]:6645" 
>                ,(shepherd-endpoint
>                   (address "/var/run/mpd-shepherd-socket")))))
> --8<---------------cut here---------------end--------------->8---
>
> I don't think it breaks backward compatibility to introduce this
> after #59866 is merged.
> The type of field 'addresses' could be changed transparently to
> something like:
>
> --8<---------------cut here---------------start------------->8---
> (define list-of-addresses (list-of (lambda (x) (or (string? x)
> (shepherd-endpoint? x)))))
> --8<---------------cut here---------------end--------------->8---
Something like that, but I don't think the vocabulary matches 1:1. In
my opinion, an address is an endpoint – not a shepherd endpoint, but an
endpoint still – while a shepherd endpoint is not an address. Thus, I
propose changing the vocabulary now to not break backwards
compatibility later. IIUC, the change from the previous records to
define-configuration is already an API change, so it'd be good to have
both in the same series.

Cheers
M
(address . 54986@debbugs.gnu.org)
cddf3b32-97e1-026f-d94b-1c35780db3a1@makinata.eu
On 2022-12-09 19:22, Liliana Marie Prikler wrote:
Toggle quote (5 lines)
> Something like that, but I don't think the vocabulary matches 1:1. In
> my opinion, an address is an endpoint – not a shepherd endpoint, but an
> endpoint still – while a shepherd endpoint is not an address. Thus, I
> propose changing the vocabulary now to not break backwards
> compatibility later.
You prefer the 'addresses' field to be renamed to 'endpoints', is that correct?

IIUC, the change from the previous records to
Toggle quote (2 lines)
> define-configuration is already an API change, so it'd be good to have
> both in the same series.
The first patch in the series changes from define-record-type* to define-configuration
but it does not add anything, I don't think there are actually any (exported)
API changes visible to the user.

Only the second patch starts to introduce visible API changes.

Cheers
M
[PATCH v3] services: mpd: Refactor MPD service.
(address . 59866@debbugs.gnu.org)(name . Bruno Victal)(address . mirai@makinata.eu)
bd2216b94eb04866095ba43d1807463153215ee2.1671200114.git.mirai@makinata.eu
From: Bruno Victal <mirai@makinata.eu>

Introduces 'mpd-plugin' and 'mpd-partition' records.
Expands 'mpd-output' and 'mpd-configuration' records.
Deprecates redundant abbreviated fields in 'mpd-configuration' and
avoids serializing unused fields that may introduce undesired behavior.
Implement log-rotation via rottlog.
Implements Shepherd actions: 'reopen' and 'configuration'.
---

Changes from v2 to v3:
* Removed "pretty-formatting" code to reduce overall code "weight".
* Renamed shepherd action 'reload' to 'reopen' as reloading would suggest
that it re-reads the config file.
* Renamed 'addresses' field to 'endpoints'.
* Allow mpd-plugin records to be used in 'outputs' field.
Technically an audio output is a mpd-plugin but since there
are numerous common audio output specific options it's worthwhile
having a mpd-output record-type for it.
* mpd-plugin serialization was simplified and the "subsystem block name"
was integrated directly into mpd-configuration record-type to better
track which field corresponds to which subsystem rather than keep an
external dictionary that maps the fields to the subsystem names.

doc/guix.texi | 172 +++++++++++++---
gnu/services/audio.scm | 434 +++++++++++++++++++++++++++++++----------
2 files changed, 476 insertions(+), 130 deletions(-)

Toggle diff (500 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 5cb5ae1dfe..3f186aefa9 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -109,6 +109,7 @@ Copyright @copyright{} 2022 Reily Siegel@*
Copyright @copyright{} 2022 Simon Streit@*
Copyright @copyright{} 2022 (@*
Copyright @copyright{} 2022 John Kehayias@*
+Copyright @copyright{} 2022 Bruno Victal@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -33007,79 +33008,184 @@ The service type for @command{mpd}
Data type representing the configuration of @command{mpd}.
@table @asis
-@item @code{user} (default: @code{"mpd"})
+@item @code{package} (default: @code{mpd}) (type: file-like)
+The MPD package.
+
+@item @code{user} (default: @code{"mpd"}) (type: string)
The user to run mpd as.
-@item @code{music-dir} (default: @code{"~/Music"})
+@item @code{group} (type: maybe-string)
+The group to run mpd as.
+
+@item @code{shepherd-requirement} (default: @code{()}) (type: list-of-symbol)
+This is a list of symbols naming Shepherd services that this service
+will depend on.
+
+@item @code{log-file} (default: @code{"/var/log/mpd/log"}) (type: maybe-string)
+The location of the log file. Set to @code{syslog} to use the local
+syslog daemon or @code{%unset-value} to omit this directive from the
+configuration file.
+
+@item @code{log-level} (type: maybe-string)
+Supress any messages below this threshold. Available values:
+@code{notice}, @code{info}, @code{verbose}, @code{warning} and
+@code{error}.
+
+@item @code{music-directory} (type: maybe-string)
The directory to scan for music files.
-@item @code{playlist-dir} (default: @code{"~/.mpd/playlists"})
+@item @code{playlist-directory} (type: maybe-string)
The directory to store playlists.
-@item @code{db-file} (default: @code{"~/.mpd/tag_cache"})
+@item @code{db-file} (type: maybe-string)
The location of the music database.
-@item @code{state-file} (default: @code{"~/.mpd/state"})
+@item @code{state-file} (type: maybe-string)
The location of the file that stores current MPD's state.
-@item @code{sticker-file} (default: @code{"~/.mpd/sticker.sql"})
+@item @code{sticker-file} (type: maybe-string)
The location of the sticker database.
-@item @code{port} (default: @code{"6600"})
+@item @code{port} (default: @code{6600}) (type: maybe-integer)
The port to run mpd on.
-@item @code{address} (default: @code{"any"})
-The address that mpd will bind to. To use a Unix domain socket,
-an absolute path can be specified here.
+@item @code{endpoints} (type: maybe-list-of-string)
+The addresses that mpd will bind to. To use a Unix domain socket, an
+absolute path can be specified here.
+
+@item @code{database} (type: maybe-mpd-plugin)
+MPD database plugin configuration.
+
+@item @code{partitions} (default: @code{()}) (type: list-of-mpd-partition)
+List of MPD "partitions".
+
+@item @code{neighbors} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD neighbor plugin configurations.
-@item @code{outputs} (default: @code{"(list (mpd-output))"})
-The audio outputs that MPD can use. By default this is a single output using pulseaudio.
+@item @code{inputs} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD input plugin configurations.
+
+@item @code{archive-plugins} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD archive plugin configurations.
+
+@item @code{input-cache-size} (type: maybe-string)
+MPD input cache size.
+
+@item @code{decoders} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD decoder plugin configurations.
+
+@item @code{resampler} (type: maybe-mpd-plugin)
+MPD resampler plugin configuration.
+
+@item @code{filters} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD filter plugin configurations.
+
+@item @code{outputs} (type: list-of-mpd-plugin-or-output)
+The audio outputs that MPD can use. By default this is a single output
+using pulseaudio.
+
+@item @code{playlist-plugins} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD playlist plugin configurations.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the configuration.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-plugin
+Data type representing a @command{mpd} plugin.
+
+@table @asis
+@item @code{plugin} (type: maybe-string)
+Plugin name.
+
+@item @code{name} (type: maybe-string)
+Name.
+
+@item @code{enabled?} (type: maybe-boolean)
+Whether the plugin is enabled/disabled.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the plugin configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin
+reference} for available options.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-partition
+Data type representing a @command{mpd} partition.
+
+@table @asis
+@item @code{name} (type: string)
+Partition name.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the partition configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions,Configuring
+Partitions} for available options.
@end table
@end deftp
@deftp {Data Type} mpd-output
-Data type representing an @command{mpd} audio output.
+Data type representing a @command{mpd} audio output.
@table @asis
-@item @code{name} (default: @code{"MPD"})
+@item @code{name} (default: @code{"MPD"}) (type: string)
The name of the audio output.
-@item @code{type} (default: @code{"pulse"})
+@item @code{type} (default: @code{"pulse"}) (type: string)
The type of audio output.
-@item @code{enabled?} (default: @code{#t})
+@item @code{enabled?} (default: @code{#t}) (type: boolean)
Specifies whether this audio output is enabled when MPD is started. By
default, all audio outputs are enabled. This is just the default
setting when there is no state file; with a state file, the previous
state is restored.
-@item @code{tags?} (default: @code{#t})
+@item @code{format} (type: maybe-string)
+Force a specific audio format on output. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#audio-output-format,Global
+Audio Format} for a more detailed description.
+
+@item @code{tags?} (default: @code{#t}) (type: boolean)
If set to @code{#f}, then MPD will not send tags to this output. This
is only useful for output plugins that can receive tags, for example the
@code{httpd} output plugin.
-@item @code{always-on?} (default: @code{#f})
+@item @code{always-on?} (default: @code{#f}) (type: boolean)
If set to @code{#t}, then MPD attempts to keep this audio output always
-open. This may be useful for streaming servers, when you don’t want to
+open. This may be useful for streaming servers, when you don?t want to
disconnect all listeners even when playback is accidentally stopped.
-@item @code{mixer-type}
-This field accepts a symbol that specifies which mixer should be used
+@item @code{mixer-type} (default: @code{"none"}) (type: string)
+This field accepts a string that specifies which mixer should be used
for this audio output: the @code{hardware} mixer, the @code{software}
mixer, the @code{null} mixer (allows setting the volume, but with no
effect; this can be used as a trick to implement an external mixer
External Mixer) or no mixer (@code{none}).
-@item @code{extra-options} (default: @code{'()})
-An association list of option symbols to string values to be appended to
-the audio output configuration.
+@item @code{replay-gain-handler} (type: maybe-string)
+This field accepts a string that specifies how
+@uref{https://mpd.readthedocs.io/en/latest/user.html#replay-gain,Replay
+Gain} is to be applied. @code{software} uses an internal software
+volume control, @code{mixer} uses the configured (hardware) mixer
+control and @code{none} disables replay gain on this audio output.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the audio output configuration.
@end table
@end deftp
-The following example shows a configuration of @code{mpd} that provides
-an HTTP audio streaming output.
+The following example shows a configuration of @command{mpd} that
+configures some of its plugins and provides a HTTP audio streaming output.
@lisp
(service mpd-service-type
@@ -33091,7 +33197,19 @@ an HTTP audio streaming output.
(mixer-type 'null)
(extra-options
`((encoder . "vorbis")
- (port . "8080"))))))))
+ (port . "8080"))))))
+ (decoders
+ (list (mpd-plugin
+ (plugin "mikmod")
+ (enabled? #f))
+ (mpd-plugin
+ (plugin "openmpt")
+ (enabled? #t)
+ (extra-options `((repeat-count . -1)
+ (interpolation-filter . 1))))))
+ (resampler (mpd-plugin
+ (plugin "libsamplerate")
+ (extra-options `((type . 0)))))))
@end lisp
diff --git a/gnu/services/audio.scm b/gnu/services/audio.scm
index 2351db8a4a..aeb081380a 100644
--- a/gnu/services/audio.scm
+++ b/gnu/services/audio.scm
@@ -2,6 +2,7 @@
;;; Copyright © 2017 Peter Mikkelsen <petermikkelsen10@gmail.com>
;;; Copyright © 2019 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2022 Bruno Victal <mirai@makinata.eu>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -20,19 +21,27 @@
(define-module (gnu services audio)
#:use-module (guix gexp)
+ #:use-module (guix deprecation)
+ #:use-module (guix diagnostics)
+ #:use-module (guix i18n)
#:use-module (gnu services)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu services admin)
#:use-module (gnu system shadow)
#:use-module (gnu packages admin)
#:use-module (gnu packages mpd)
#:use-module (guix records)
#:use-module (ice-9 match)
- #:use-module (ice-9 format)
#:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-8)
#:use-module (srfi srfi-26)
#:export (mpd-output
mpd-output?
+ mpd-plugin
+ mpd-plugin?
+ mpd-partition
+ mpd-partition?
mpd-configuration
mpd-configuration?
mpd-service-type))
@@ -51,180 +60,397 @@ (define (uglify-field-name field-name)
#\-)
"_")))
-(define (free-form-args? val)
- (match val
- (() #t)
- ((((? symbol?) . (? string?)) . val) (free-form-args? val))
- (_ #f)))
+(define list-of-string?
+ (list-of string?))
-(define* (mpd-serialize-field field-name value #:optional (indent-level 0))
- #~(begin
- (use-modules ((ice-9 format)))
- (format #f "~v/~a \"~a\"~%" #$indent-level #$(if (string? field-name)
- field-name
- (uglify-field-name field-name)) #$value)))
+(define list-of-symbol?
+ (list-of symbol?))
-(define* (mpd-serialize-free-form-args field-name value #:optional (indent-level 0))
- (generic-serialize-alist string-append (cut mpd-serialize-field <> <> indent-level) value))
+(define (mpd-serialize-field field-name value)
+ (let ((field (if (string? field-name) field-name
+ (uglify-field-name field-name)))
+ (value (if (boolean? value) (if value "yes" "no") value)))
+ #~(format #f "~a \"~a\"~%" #$field #$value)))
(define mpd-serialize-number mpd-serialize-field)
(define mpd-serialize-string mpd-serialize-field)
-(define* (mpd-serialize-boolean field-name value #:optional (indent-level 0))
- (mpd-serialize-field field-name (if value "yes" "no") indent-level))
+(define mpd-serialize-boolean mpd-serialize-field)
-(define (mpd-serialize-list-of-mpd-output field-name value)
- #~(string-append "\naudio_output {\n"
- #$@(map (cut serialize-configuration <>
- mpd-output-fields)
- value)
- "}\n"))
+(define* (mpd-serialize-alist field-name value)
+ #~(string-append #$@(generic-serialize-alist list mpd-serialize-field value)))
-(define (mpd-serialize-configuration configuration)
- (mixed-text-file
- "mpd.conf"
- (serialize-configuration configuration mpd-configuration-fields)))
+(define-maybe string (prefix mpd-))
+(define-maybe list-of-string (prefix mpd-))
+(define-maybe boolean (prefix mpd-))
+
+;;; TODO: Procedures for deprecated fields, to be removed.
+
+(define mpd-deprecated-fields '((music-dir . music-directory)
+ (playlist-dir . playlist-directory)
+ (address . addresses)))
+
+(define (port? value) (or (string? value) (integer? value)))
+
+(define (mpd-serialize-deprecated-field field-name value)
+ (if (maybe-value-set? value)
+ (begin (warn-about-deprecation field-name #f
+ #:replacement (assoc-ref mpd-deprecated-fields field-name))
+ (match field-name
+ ('playlist-dir (mpd-serialize-string "playlist_directory" value))
+ ('music-dir (mpd-serialize-string "music_directory" value))
+ ('address (mpd-serialize-string "bind_to_address" value))))
+ ""))
+
+(define (mpd-serialize-port field-name value)
+ (when (string? value)
+ (warning (G_ "string value for '~a' is deprecated, use integer instead~%") field-name))
+ (mpd-serialize-field field-name value))
+
+(define-maybe port (prefix mpd-))
+
+;;;
+
+;; Generic MPD plugin record, lists only the most prevalent fields.
+(define-configuration mpd-plugin
+ (plugin
+ maybe-string
+ "Plugin name.")
+
+ (name
+ maybe-string
+ "Name.")
+
+ (enabled?
+ maybe-boolean
+ "Whether the plugin is enabled/disabled.")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols/strings to string values to be appended to
+the plugin configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin reference}
+for available options.")
+
+ (prefix mpd-))
+
+(define (mpd-serialize-mpd-plugin field-name value)
+ #~(format #f "~a {~%~a}~%"
+ '#$field-name #$(serialize-configuration value mpd-plugin-fields)))
+
+(define (mpd-serialize-list-of-mpd-plugin field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-plugin field-name <>) value)))
-(define mpd-subsystem-serialize-field (cut mpd-serialize-field <> <> 1))
-(define mpd-subsystem-serialize-string (cut mpd-serialize-string <> <> 1))
-(define mpd-subsystem-serialize-number (cut mpd-serialize-number <> <> 1))
-(define mpd-subsystem-serialize-boolean (cut mpd-serialize-boolean <> <> 1))
-(define mpd-subsystem-serialize-free-form-args (cut mpd-serialize-free-form-args <> <> 1))
+(define list-of-mpd-plugin? (list-of mpd-plugin?))
+
+(define-maybe mpd-plugin (prefix mpd-))
+
+(define-configuration mpd-partition
+ (name
+ string
+ "Partition name.")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols/strings to string values to be appended to
+the partition configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions,Configuring Partitions}
+for available options.")
+
+ (prefix mpd-))
+
+(define (mpd-serialize-mpd-partition field-name value)
+ #~(format #f "partition {~%~a}~%"
+ #$(serialize-configuration value mpd-partition-fields)))
+
+(define (mpd-serialize-list-of-mpd-partition field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-partition #f <>) value)))
+
+(define list-of-mpd-partition?
+ (list-of mpd-partition?))
(define-configuration mpd-output
(name
(string "MPD")
"The name of the audio output.")
+
(type
(string "pulse")
"The type of audio output.")
+
(enabled?
(boolean #t)
"Specifies whether this audio output is enabled when MPD is started. By
default, all audio outputs are enabled. This is just the default
setting when there is no state file; with a state file, the previous
state is restored.")
+
+ (format
+ maybe-string
+ "Force a specific audio format on output. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#audio-output-format,Global Audio Format}
+for a more detailed description.")
+
(tags?
(boolean #t)
"If set to @code{#f}, then MPD will not send tags to this output. This
is only useful for output plugins that can receive tags, for example the
@code{httpd} output plugin.")
+
(always-on?
(boolean #f)
"If set to @code{#t}, then MPD attempts to keep this audio output always
open. This may be useful for streaming servers, when you don’t want to
disconnect all listeners even when playback is accidentally stopped.")
+
(mixer-type
(string "none")
- "This field accepts a symbol that specifies which mixer should be used
+ "This field accepts a string that specifies which mixer should be used
for this audio output: the @code{hardware} mixer, the @code{software}
mixer, the @code{null} mixer (allows setting the volume, but with no
effect; this can be used as a trick to implement an external mixer
External Mixer) or no mixer (@code{none}).")
+
+ (replay-gain-handler
+ maybe-string
+ "This field accepts a string that specifies how
+@uref{https://mpd.readthedocs.io/en/latest/user.html#replay-gain,Replay Gain} is
+to be applied. @code{software} uses an internal software volume control,
+@code{mixer} uses the configured (hardware) mixer control and @code{none}
+disables replay gain on this audio output.")
+
(extra-options
- (free-form-args '())
- "An association list of option symbols to string values to be appended to
+ (alist '())
+ "An association list of option symbols/strings to string values to be appended to
the audio output configuration.")
- (prefix mpd-subsystem-))
-(define list-of-mpd-output?
- (list-of mpd-output?))
+ (prefix mpd-))
+
+(define (mpd-serialize-mpd-output field-name value)
+ #~(format #f "audio_output {~%~a}~%"
+ #$(serialize-configuration value mpd-output-fields)))
+
+(define (mpd-serialize-list-of-mpd-plugin-or-output field-name value)
+ (receive (plugins outputs)
+ (partition mpd-plugin? value)
+ #~(string-append #$@(map (cut
This message was truncated. Download the full message here.
M
[PATCH v4 1/2] services: mpd: use 'define-configuration'.
(address . 59866@debbugs.gnu.org)(name . Bruno Victal)(address . mirai@makinata.eu)
248f6adc7d434e3d8df791311744a38e0d965744.1671632111.git.mirai@makinata.eu
From: Bruno Victal <mirai@makinata.eu>

---
gnu/services/audio.scm | 217 ++++++++++++++++++++++++-----------------
1 file changed, 129 insertions(+), 88 deletions(-)

Toggle diff (253 lines)
diff --git a/gnu/services/audio.scm b/gnu/services/audio.scm
index c60053f33c..2351db8a4a 100644
--- a/gnu/services/audio.scm
+++ b/gnu/services/audio.scm
@@ -21,6 +21,7 @@
(define-module (gnu services audio)
#:use-module (guix gexp)
#:use-module (gnu services)
+ #:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
#:use-module (gnu system shadow)
#:use-module (gnu packages admin)
@@ -28,6 +29,8 @@ (define-module (gnu services audio)
#:use-module (guix records)
#:use-module (ice-9 match)
#:use-module (ice-9 format)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-26)
#:export (mpd-output
mpd-output?
mpd-configuration
@@ -40,93 +43,131 @@ (define-module (gnu services audio)
;;;
;;; Code:
-(define-record-type* <mpd-output>
- mpd-output make-mpd-output
- mpd-output?
- (type mpd-output-type
- (default "pulse"))
- (name mpd-output-name
- (default "MPD"))
- (enabled? mpd-output-enabled?
- (default #t))
- (tags? mpd-output-tags?
- (default #t))
- (always-on? mpd-output-always-on?
- (default #f))
- (mixer-type mpd-output-mixer-type
- ;; valid: hardware, software, null, none
- (default #f))
- (extra-options mpd-output-extra-options
- (default '())))
-
-(define-record-type* <mpd-configuration>
- mpd-configuration make-mpd-configuration
- mpd-configuration?
- (user mpd-configuration-user
- (default "mpd"))
- (music-dir mpd-configuration-music-dir
- (default "~/Music"))
- (playlist-dir mpd-configuration-playlist-dir
- (default "~/.mpd/playlists"))
- (db-file mpd-configuration-db-file
- (default "~/.mpd/tag_cache"))
- (state-file mpd-configuration-state-file
- (default "~/.mpd/state"))
- (sticker-file mpd-configuration-sticker-file
- (default "~/.mpd/sticker.sql"))
- (port mpd-configuration-port
- (default "6600"))
- (address mpd-configuration-address
- (default "any"))
- (outputs mpd-configuration-outputs
- (default (list (mpd-output)))))
-
-(define (mpd-output->string output)
- "Convert the OUTPUT of type <mpd-output> to a configuration file snippet."
- (let ((extra (string-join
- (map (match-lambda
- ((key . value)
- (format #f " ~a \"~a\""
- (string-map
- (lambda (c) (if (char=? c #\-) #\_ c))
- (symbol->string key))
- value)))
- (mpd-output-extra-options output))
- "\n")))
- (format #f "\
-audio_output {
- type \"~a\"
- name \"~a\"
-~:[ enabled \"no\"~%~;~]\
-~:[ tags \"no\"~%~;~]\
-~:[~; always_on \"yes\"~%~]\
-~@[ mixer_type \"~a\"~%~]\
-~a~%}~%"
- (mpd-output-type output)
- (mpd-output-name output)
- (mpd-output-enabled? output)
- (mpd-output-tags? output)
- (mpd-output-always-on? output)
- (mpd-output-mixer-type output)
- extra)))
-
-(define (mpd-config->file config)
- (apply
- mixed-text-file "mpd.conf"
- "pid_file \"" (mpd-file-name config "pid") "\"\n"
- (append (map mpd-output->string
- (mpd-configuration-outputs config))
- (map (match-lambda
- ((config-name config-val)
- (string-append config-name " \"" (config-val config) "\"\n")))
- `(("user" ,mpd-configuration-user)
- ("music_directory" ,mpd-configuration-music-dir)
- ("playlist_directory" ,mpd-configuration-playlist-dir)
- ("db_file" ,mpd-configuration-db-file)
- ("state_file" ,mpd-configuration-state-file)
- ("sticker_file" ,mpd-configuration-sticker-file)
- ("port" ,mpd-configuration-port)
- ("bind_to_address" ,mpd-configuration-address))))))
+(define (uglify-field-name field-name)
+ (let ((str (symbol->string field-name)))
+ (string-join (string-split (if (string-suffix? "?" str)
+ (string-drop-right str 1)
+ str)
+ #\-)
+ "_")))
+
+(define (free-form-args? val)
+ (match val
+ (() #t)
+ ((((? symbol?) . (? string?)) . val) (free-form-args? val))
+ (_ #f)))
+
+(define* (mpd-serialize-field field-name value #:optional (indent-level 0))
+ #~(begin
+ (use-modules ((ice-9 format)))
+ (format #f "~v/~a \"~a\"~%" #$indent-level #$(if (string? field-name)
+ field-name
+ (uglify-field-name field-name)) #$value)))
+
+(define* (mpd-serialize-free-form-args field-name value #:optional (indent-level 0))
+ (generic-serialize-alist string-append (cut mpd-serialize-field <> <> indent-level) value))
+
+(define mpd-serialize-number mpd-serialize-field)
+
+(define mpd-serialize-string mpd-serialize-field)
+
+(define* (mpd-serialize-boolean field-name value #:optional (indent-level 0))
+ (mpd-serialize-field field-name (if value "yes" "no") indent-level))
+
+(define (mpd-serialize-list-of-mpd-output field-name value)
+ #~(string-append "\naudio_output {\n"
+ #$@(map (cut serialize-configuration <>
+ mpd-output-fields)
+ value)
+ "}\n"))
+
+(define (mpd-serialize-configuration configuration)
+ (mixed-text-file
+ "mpd.conf"
+ (serialize-configuration configuration mpd-configuration-fields)))
+
+(define mpd-subsystem-serialize-field (cut mpd-serialize-field <> <> 1))
+(define mpd-subsystem-serialize-string (cut mpd-serialize-string <> <> 1))
+(define mpd-subsystem-serialize-number (cut mpd-serialize-number <> <> 1))
+(define mpd-subsystem-serialize-boolean (cut mpd-serialize-boolean <> <> 1))
+(define mpd-subsystem-serialize-free-form-args (cut mpd-serialize-free-form-args <> <> 1))
+
+(define-configuration mpd-output
+ (name
+ (string "MPD")
+ "The name of the audio output.")
+ (type
+ (string "pulse")
+ "The type of audio output.")
+ (enabled?
+ (boolean #t)
+ "Specifies whether this audio output is enabled when MPD is started. By
+default, all audio outputs are enabled. This is just the default
+setting when there is no state file; with a state file, the previous
+state is restored.")
+ (tags?
+ (boolean #t)
+ "If set to @code{#f}, then MPD will not send tags to this output. This
+is only useful for output plugins that can receive tags, for example the
+@code{httpd} output plugin.")
+ (always-on?
+ (boolean #f)
+ "If set to @code{#t}, then MPD attempts to keep this audio output always
+open. This may be useful for streaming servers, when you don’t want to
+disconnect all listeners even when playback is accidentally stopped.")
+ (mixer-type
+ (string "none")
+ "This field accepts a symbol that specifies which mixer should be used
+for this audio output: the @code{hardware} mixer, the @code{software}
+mixer, the @code{null} mixer (allows setting the volume, but with no
+effect; this can be used as a trick to implement an external mixer
+External Mixer) or no mixer (@code{none}).")
+ (extra-options
+ (free-form-args '())
+ "An association list of option symbols to string values to be appended to
+the audio output configuration.")
+ (prefix mpd-subsystem-))
+
+(define list-of-mpd-output?
+ (list-of mpd-output?))
+
+(define-configuration mpd-configuration
+ (user
+ (string "mpd")
+ "The user to run mpd as.")
+ (music-dir
+ (string "~/Music")
+ "The directory to scan for music files."
+ (lambda (_ x)
+ (mpd-serialize-field "music_directory" x)))
+ (playlist-dir
+ (string "~/.mpd/playlists")
+ "The directory to store playlists."
+ (lambda (_ x)
+ (mpd-serialize-field "playlist_directory" x)))
+ (db-file
+ (string "~/.mpd/tag_cache")
+ "The location of the music database.")
+ (state-file
+ (string "~/.mpd/state")
+ "The location of the file that stores current MPD's state.")
+ (sticker-file
+ (string "~/.mpd/sticker.sql")
+ "The location of the sticker database.")
+ (port
+ (string "6600")
+ "The port to run mpd on.")
+ (address
+ (string "any")
+ "The address that mpd will bind to.
+To use a Unix domain socket, an absolute path can be specified here."
+ (lambda (_ x)
+ (mpd-serialize-field "bind_to_address" x)))
+ (outputs
+ (list-of-mpd-output (list (mpd-output)))
+ "The audio outputs that MPD can use.
+By default this is a single output using pulseaudio.")
+ (prefix mpd-))
(define (mpd-file-name config file)
"Return a path in /var/run/mpd/ that is writable
@@ -143,7 +184,7 @@ (define (mpd-shepherd-service config)
(start #~(make-forkexec-constructor
(list #$(file-append mpd "/bin/mpd")
"--no-daemon"
- #$(mpd-config->file config))
+ #$(mpd-serialize-configuration config))
#:environment-variables
;; Required to detect PulseAudio when run under a user account.
(list (string-append

base-commit: 7833acab0da02335941974608510c02e2d1d8069
--
2.38.1
M
[PATCH v4 2/2] services: mpd: Refactor MPD service.
(address . 59866@debbugs.gnu.org)(name . Bruno Victal)(address . mirai@makinata.eu)
22745f1abf008ad389638c984e51b12630a18307.1671632111.git.mirai@makinata.eu
From: Bruno Victal <mirai@makinata.eu>

Introduces 'mpd-plugin' and 'mpd-partition' records.
Expands 'mpd-output' and 'mpd-configuration' records.
Deprecates redundant abbreviated fields in 'mpd-configuration' and
avoids serializing unused fields that may introduce undesired behavior.
Implement log-rotation via rottlog.
Implements Shepherd actions: 'reopen' and 'configuration'.
---
doc/guix.texi | 172 +++++++++++++---
gnu/services/audio.scm | 434 +++++++++++++++++++++++++++++++----------
2 files changed, 476 insertions(+), 130 deletions(-)

Toggle diff (530 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index fd03da8c97..3daec7dfaa 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -109,6 +109,7 @@ Copyright @copyright{} 2022 Reily Siegel@*
Copyright @copyright{} 2022 Simon Streit@*
Copyright @copyright{} 2022 (@*
Copyright @copyright{} 2022 John Kehayias@*
+Copyright @copyright{} 2022 Bruno Victal@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -33012,79 +33013,184 @@ The service type for @command{mpd}
Data type representing the configuration of @command{mpd}.
@table @asis
-@item @code{user} (default: @code{"mpd"})
+@item @code{package} (default: @code{mpd}) (type: file-like)
+The MPD package.
+
+@item @code{user} (default: @code{"mpd"}) (type: string)
The user to run mpd as.
-@item @code{music-dir} (default: @code{"~/Music"})
+@item @code{group} (type: maybe-string)
+The group to run mpd as.
+
+@item @code{shepherd-requirement} (default: @code{()}) (type: list-of-symbol)
+This is a list of symbols naming Shepherd services that this service
+will depend on.
+
+@item @code{log-file} (default: @code{"/var/log/mpd/log"}) (type: maybe-string)
+The location of the log file. Set to @code{syslog} to use the local
+syslog daemon or @code{%unset-value} to omit this directive from the
+configuration file.
+
+@item @code{log-level} (type: maybe-string)
+Supress any messages below this threshold. Available values:
+@code{notice}, @code{info}, @code{verbose}, @code{warning} and
+@code{error}.
+
+@item @code{music-directory} (type: maybe-string)
The directory to scan for music files.
-@item @code{playlist-dir} (default: @code{"~/.mpd/playlists"})
+@item @code{playlist-directory} (type: maybe-string)
The directory to store playlists.
-@item @code{db-file} (default: @code{"~/.mpd/tag_cache"})
+@item @code{db-file} (type: maybe-string)
The location of the music database.
-@item @code{state-file} (default: @code{"~/.mpd/state"})
+@item @code{state-file} (type: maybe-string)
The location of the file that stores current MPD's state.
-@item @code{sticker-file} (default: @code{"~/.mpd/sticker.sql"})
+@item @code{sticker-file} (type: maybe-string)
The location of the sticker database.
-@item @code{port} (default: @code{"6600"})
+@item @code{port} (default: @code{6600}) (type: maybe-integer)
The port to run mpd on.
-@item @code{address} (default: @code{"any"})
-The address that mpd will bind to. To use a Unix domain socket,
-an absolute path can be specified here.
+@item @code{endpoints} (type: maybe-list-of-string)
+The addresses that mpd will bind to. To use a Unix domain socket, an
+absolute path can be specified here.
+
+@item @code{database} (type: maybe-mpd-plugin)
+MPD database plugin configuration.
+
+@item @code{partitions} (default: @code{()}) (type: list-of-mpd-partition)
+List of MPD "partitions".
+
+@item @code{neighbors} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD neighbor plugin configurations.
-@item @code{outputs} (default: @code{"(list (mpd-output))"})
-The audio outputs that MPD can use. By default this is a single output using pulseaudio.
+@item @code{inputs} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD input plugin configurations.
+
+@item @code{archive-plugins} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD archive plugin configurations.
+
+@item @code{input-cache-size} (type: maybe-string)
+MPD input cache size.
+
+@item @code{decoders} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD decoder plugin configurations.
+
+@item @code{resampler} (type: maybe-mpd-plugin)
+MPD resampler plugin configuration.
+
+@item @code{filters} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD filter plugin configurations.
+
+@item @code{outputs} (type: list-of-mpd-plugin-or-output)
+The audio outputs that MPD can use. By default this is a single output
+using pulseaudio.
+
+@item @code{playlist-plugins} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD playlist plugin configurations.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the configuration.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-plugin
+Data type representing a @command{mpd} plugin.
+
+@table @asis
+@item @code{plugin} (type: maybe-string)
+Plugin name.
+
+@item @code{name} (type: maybe-string)
+Name.
+
+@item @code{enabled?} (type: maybe-boolean)
+Whether the plugin is enabled/disabled.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the plugin configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin
+reference} for available options.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-partition
+Data type representing a @command{mpd} partition.
+
+@table @asis
+@item @code{name} (type: string)
+Partition name.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the partition configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions,Configuring
+Partitions} for available options.
@end table
@end deftp
@deftp {Data Type} mpd-output
-Data type representing an @command{mpd} audio output.
+Data type representing a @command{mpd} audio output.
@table @asis
-@item @code{name} (default: @code{"MPD"})
+@item @code{name} (default: @code{"MPD"}) (type: string)
The name of the audio output.
-@item @code{type} (default: @code{"pulse"})
+@item @code{type} (default: @code{"pulse"}) (type: string)
The type of audio output.
-@item @code{enabled?} (default: @code{#t})
+@item @code{enabled?} (default: @code{#t}) (type: boolean)
Specifies whether this audio output is enabled when MPD is started. By
default, all audio outputs are enabled. This is just the default
setting when there is no state file; with a state file, the previous
state is restored.
-@item @code{tags?} (default: @code{#t})
+@item @code{format} (type: maybe-string)
+Force a specific audio format on output. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#audio-output-format,Global
+Audio Format} for a more detailed description.
+
+@item @code{tags?} (default: @code{#t}) (type: boolean)
If set to @code{#f}, then MPD will not send tags to this output. This
is only useful for output plugins that can receive tags, for example the
@code{httpd} output plugin.
-@item @code{always-on?} (default: @code{#f})
+@item @code{always-on?} (default: @code{#f}) (type: boolean)
If set to @code{#t}, then MPD attempts to keep this audio output always
-open. This may be useful for streaming servers, when you don’t want to
+open. This may be useful for streaming servers, when you don?t want to
disconnect all listeners even when playback is accidentally stopped.
-@item @code{mixer-type}
-This field accepts a symbol that specifies which mixer should be used
+@item @code{mixer-type} (default: @code{"none"}) (type: string)
+This field accepts a string that specifies which mixer should be used
for this audio output: the @code{hardware} mixer, the @code{software}
mixer, the @code{null} mixer (allows setting the volume, but with no
effect; this can be used as a trick to implement an external mixer
External Mixer) or no mixer (@code{none}).
-@item @code{extra-options} (default: @code{'()})
-An association list of option symbols to string values to be appended to
-the audio output configuration.
+@item @code{replay-gain-handler} (type: maybe-string)
+This field accepts a string that specifies how
+@uref{https://mpd.readthedocs.io/en/latest/user.html#replay-gain,Replay
+Gain} is to be applied. @code{software} uses an internal software
+volume control, @code{mixer} uses the configured (hardware) mixer
+control and @code{none} disables replay gain on this audio output.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the audio output configuration.
@end table
@end deftp
-The following example shows a configuration of @code{mpd} that provides
-an HTTP audio streaming output.
+The following example shows a configuration of @command{mpd} that
+configures some of its plugins and provides a HTTP audio streaming output.
@lisp
(service mpd-service-type
@@ -33096,7 +33202,19 @@ an HTTP audio streaming output.
(mixer-type 'null)
(extra-options
`((encoder . "vorbis")
- (port . "8080"))))))))
+ (port . "8080"))))))
+ (decoders
+ (list (mpd-plugin
+ (plugin "mikmod")
+ (enabled? #f))
+ (mpd-plugin
+ (plugin "openmpt")
+ (enabled? #t)
+ (extra-options `((repeat-count . -1)
+ (interpolation-filter . 1))))))
+ (resampler (mpd-plugin
+ (plugin "libsamplerate")
+ (extra-options `((type . 0)))))))
@end lisp
diff --git a/gnu/services/audio.scm b/gnu/services/audio.scm
index 2351db8a4a..2b0447fd09 100644
--- a/gnu/services/audio.scm
+++ b/gnu/services/audio.scm
@@ -2,6 +2,7 @@
;;; Copyright © 2017 Peter Mikkelsen <petermikkelsen10@gmail.com>
;;; Copyright © 2019 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2022 Bruno Victal <mirai@makinata.eu>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -20,19 +21,27 @@
(define-module (gnu services audio)
#:use-module (guix gexp)
+ #:use-module (guix deprecation)
+ #:use-module (guix diagnostics)
+ #:use-module (guix i18n)
#:use-module (gnu services)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu services admin)
#:use-module (gnu system shadow)
#:use-module (gnu packages admin)
#:use-module (gnu packages mpd)
#:use-module (guix records)
#:use-module (ice-9 match)
- #:use-module (ice-9 format)
#:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-8)
#:use-module (srfi srfi-26)
#:export (mpd-output
mpd-output?
+ mpd-plugin
+ mpd-plugin?
+ mpd-partition
+ mpd-partition?
mpd-configuration
mpd-configuration?
mpd-service-type))
@@ -51,180 +60,397 @@ (define (uglify-field-name field-name)
#\-)
"_")))
-(define (free-form-args? val)
- (match val
- (() #t)
- ((((? symbol?) . (? string?)) . val) (free-form-args? val))
- (_ #f)))
+(define list-of-string?
+ (list-of string?))
-(define* (mpd-serialize-field field-name value #:optional (indent-level 0))
- #~(begin
- (use-modules ((ice-9 format)))
- (format #f "~v/~a \"~a\"~%" #$indent-level #$(if (string? field-name)
- field-name
- (uglify-field-name field-name)) #$value)))
+(define list-of-symbol?
+ (list-of symbol?))
-(define* (mpd-serialize-free-form-args field-name value #:optional (indent-level 0))
- (generic-serialize-alist string-append (cut mpd-serialize-field <> <> indent-level) value))
+(define (mpd-serialize-field field-name value)
+ (let ((field (if (string? field-name) field-name
+ (uglify-field-name field-name)))
+ (value (if (boolean? value) (if value "yes" "no") value)))
+ #~(format #f "~a \"~a\"~%" #$field #$value)))
(define mpd-serialize-number mpd-serialize-field)
(define mpd-serialize-string mpd-serialize-field)
-(define* (mpd-serialize-boolean field-name value #:optional (indent-level 0))
- (mpd-serialize-field field-name (if value "yes" "no") indent-level))
+(define mpd-serialize-boolean mpd-serialize-field)
-(define (mpd-serialize-list-of-mpd-output field-name value)
- #~(string-append "\naudio_output {\n"
- #$@(map (cut serialize-configuration <>
- mpd-output-fields)
- value)
- "}\n"))
+(define (mpd-serialize-alist field-name value)
+ #~(string-append #$@(generic-serialize-alist list mpd-serialize-field value)))
-(define (mpd-serialize-configuration configuration)
- (mixed-text-file
- "mpd.conf"
- (serialize-configuration configuration mpd-configuration-fields)))
+(define-maybe string (prefix mpd-))
+(define-maybe list-of-string (prefix mpd-))
+(define-maybe boolean (prefix mpd-))
+
+;;; TODO: Procedures for deprecated fields, to be removed.
+
+(define mpd-deprecated-fields '((music-dir . music-directory)
+ (playlist-dir . playlist-directory)
+ (address . addresses)))
+
+(define (port? value) (or (string? value) (integer? value)))
+
+(define (mpd-serialize-deprecated-field field-name value)
+ (if (maybe-value-set? value)
+ (begin (warn-about-deprecation field-name #f
+ #:replacement (assoc-ref mpd-deprecated-fields field-name))
+ (match field-name
+ ('playlist-dir (mpd-serialize-string "playlist_directory" value))
+ ('music-dir (mpd-serialize-string "music_directory" value))
+ ('address (mpd-serialize-string "bind_to_address" value))))
+ ""))
+
+(define (mpd-serialize-port field-name value)
+ (when (string? value)
+ (warning (G_ "string value for '~a' is deprecated, use integer instead~%") field-name))
+ (mpd-serialize-field field-name value))
+
+(define-maybe port (prefix mpd-))
+
+;;;
+
+;; Generic MPD plugin record, lists only the most prevalent fields.
+(define-configuration mpd-plugin
+ (plugin
+ maybe-string
+ "Plugin name.")
+
+ (name
+ maybe-string
+ "Name.")
+
+ (enabled?
+ maybe-boolean
+ "Whether the plugin is enabled/disabled.")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols/strings to string values to be appended to
+the plugin configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin reference}
+for available options.")
+
+ (prefix mpd-))
+
+(define (mpd-serialize-mpd-plugin field-name value)
+ #~(format #f "~a {~%~a}~%"
+ '#$field-name #$(serialize-configuration value mpd-plugin-fields)))
+
+(define (mpd-serialize-list-of-mpd-plugin field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-plugin field-name <>) value)))
-(define mpd-subsystem-serialize-field (cut mpd-serialize-field <> <> 1))
-(define mpd-subsystem-serialize-string (cut mpd-serialize-string <> <> 1))
-(define mpd-subsystem-serialize-number (cut mpd-serialize-number <> <> 1))
-(define mpd-subsystem-serialize-boolean (cut mpd-serialize-boolean <> <> 1))
-(define mpd-subsystem-serialize-free-form-args (cut mpd-serialize-free-form-args <> <> 1))
+(define list-of-mpd-plugin? (list-of mpd-plugin?))
+
+(define-maybe mpd-plugin (prefix mpd-))
+
+(define-configuration mpd-partition
+ (name
+ string
+ "Partition name.")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols/strings to string values to be appended to
+the partition configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions,Configuring Partitions}
+for available options.")
+
+ (prefix mpd-))
+
+(define (mpd-serialize-mpd-partition field-name value)
+ #~(format #f "partition {~%~a}~%"
+ #$(serialize-configuration value mpd-partition-fields)))
+
+(define (mpd-serialize-list-of-mpd-partition field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-partition #f <>) value)))
+
+(define list-of-mpd-partition?
+ (list-of mpd-partition?))
(define-configuration mpd-output
(name
(string "MPD")
"The name of the audio output.")
+
(type
(string "pulse")
"The type of audio output.")
+
(enabled?
(boolean #t)
"Specifies whether this audio output is enabled when MPD is started. By
default, all audio outputs are enabled. This is just the default
setting when there is no state file; with a state file, the previous
state is restored.")
+
+ (format
+ maybe-string
+ "Force a specific audio format on output. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#audio-output-format,Global Audio Format}
+for a more detailed description.")
+
(tags?
(boolean #t)
"If set to @code{#f}, then MPD will not send tags to this output. This
is only useful for output plugins that can receive tags, for example the
@code{httpd} output plugin.")
+
(always-on?
(boolean #f)
"If set to @code{#t}, then MPD attempts to keep this audio output always
open. This may be useful for streaming servers, when you don’t want to
disconnect all listeners even when playback is accidentally stopped.")
+
(mixer-type
(string "none")
- "This field accepts a symbol that specifies which mixer should be used
+ "This field accepts a string that specifies which mixer should be used
for this audio output: the @code{hardware} mixer, the @code{software}
mixer, the @code{null} mixer (allows setting the volume, but with no
effect; this can be used as a trick to implement an external mixer
External Mixer) or no mixer (@code{none}).")
+
+ (replay-gain-handler
+ maybe-string
+ "This field accepts a string that specifies how
+@uref{https://mpd.readthedocs.io/en/latest/user.html#replay-gain,Replay Gain} is
+to be applied. @code{software} uses an internal software volume control,
+@code{mixer} uses the configured (hardware) mixer control and @code{none}
+disables replay gain on this audio output.")
+
(extra-options
- (free-form-args '())
- "An association list of option symbols to string values to be appended to
+ (alist '())
+ "An association list of option symbols/strings to string values to be appended to
the audio output configuration.")
- (prefix mpd-subsystem-))
-(define list-of-mpd-output?
- (list-of mpd-output?))
+ (prefix mpd-))
+
+(define (mpd-serialize-mpd-output field-name value)
+ #~(format #f "audio_output {~%~a}~%"
+ #$(serialize-configuration value mpd-output-fields)))
+
+(define (mpd-serialize-list-of-mpd-plugin-or-output field-name value)
+ (receive (plugins outputs)
+ (partition mpd-plugin? value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-plugin "audio_output" <>) plugins)
+ #$@(map (cut mpd-serialize-mpd-output #f <>) outputs))))
+
+(define list-of-mpd-plugin-or-output?
+ (list-of (lambda (x)
+ (or (mpd-output? x) (mpd-plugin? x)))))
(define-configuration mpd-configuration
+ (package
+ (file-like mpd)
+ "The MPD package."
+ empty-serializer)
+
(user
(string "mpd")
"The user to run mpd as.")
- (music-dir
- (string "~/Music")
+
+ (group
+ maybe-string
+ "The group to run mpd as.")
+
+ (shepherd-requirement
+ (list-of-symbol '())
+ "This is a list of symbols naming Shepherd services that this service
+will depend on."
+ empty-serializer)
+
+ (log-file
+ (maybe-string "/var/log/mpd/lo
This message was truncated. Download the full message here.
M
[PATCH v5 1/2] services: mpd: rewrite using 'define-configuration'.
(address . 59866@debbugs.gnu.org)(name . Bruno Victal)(address . mirai@makinata.eu)
8448291fc734b10d0dc9ce50d6a5131d477b537f.1671889887.git.mirai@makinata.eu
From: Bruno Victal <mirai@makinata.eu>

* gnu/services/audio.scm
(mpd-configuration, mpd-output): Rewrite using define-configuration.
(uglify-field-name): New procedure.
(free-form-args?, list-of-mpd-output?): New predicate.
---

Changes since v4:
* Copyright line moved to patch 1/2.
* Retitled commit message and added ChangeLog formatted entries.

gnu/services/audio.scm | 218 ++++++++++++++++++++++++-----------------
1 file changed, 130 insertions(+), 88 deletions(-)

Toggle diff (261 lines)
diff --git a/gnu/services/audio.scm b/gnu/services/audio.scm
index c60053f33c..1a1026f342 100644
--- a/gnu/services/audio.scm
+++ b/gnu/services/audio.scm
@@ -2,6 +2,7 @@
;;; Copyright © 2017 Peter Mikkelsen <petermikkelsen10@gmail.com>
;;; Copyright © 2019 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2022 Bruno Victal <mirai@makinata.eu>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -21,6 +22,7 @@
(define-module (gnu services audio)
#:use-module (guix gexp)
#:use-module (gnu services)
+ #:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
#:use-module (gnu system shadow)
#:use-module (gnu packages admin)
@@ -28,6 +30,8 @@ (define-module (gnu services audio)
#:use-module (guix records)
#:use-module (ice-9 match)
#:use-module (ice-9 format)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-26)
#:export (mpd-output
mpd-output?
mpd-configuration
@@ -40,93 +44,131 @@ (define-module (gnu services audio)
;;;
;;; Code:
-(define-record-type* <mpd-output>
- mpd-output make-mpd-output
- mpd-output?
- (type mpd-output-type
- (default "pulse"))
- (name mpd-output-name
- (default "MPD"))
- (enabled? mpd-output-enabled?
- (default #t))
- (tags? mpd-output-tags?
- (default #t))
- (always-on? mpd-output-always-on?
- (default #f))
- (mixer-type mpd-output-mixer-type
- ;; valid: hardware, software, null, none
- (default #f))
- (extra-options mpd-output-extra-options
- (default '())))
-
-(define-record-type* <mpd-configuration>
- mpd-configuration make-mpd-configuration
- mpd-configuration?
- (user mpd-configuration-user
- (default "mpd"))
- (music-dir mpd-configuration-music-dir
- (default "~/Music"))
- (playlist-dir mpd-configuration-playlist-dir
- (default "~/.mpd/playlists"))
- (db-file mpd-configuration-db-file
- (default "~/.mpd/tag_cache"))
- (state-file mpd-configuration-state-file
- (default "~/.mpd/state"))
- (sticker-file mpd-configuration-sticker-file
- (default "~/.mpd/sticker.sql"))
- (port mpd-configuration-port
- (default "6600"))
- (address mpd-configuration-address
- (default "any"))
- (outputs mpd-configuration-outputs
- (default (list (mpd-output)))))
-
-(define (mpd-output->string output)
- "Convert the OUTPUT of type <mpd-output> to a configuration file snippet."
- (let ((extra (string-join
- (map (match-lambda
- ((key . value)
- (format #f " ~a \"~a\""
- (string-map
- (lambda (c) (if (char=? c #\-) #\_ c))
- (symbol->string key))
- value)))
- (mpd-output-extra-options output))
- "\n")))
- (format #f "\
-audio_output {
- type \"~a\"
- name \"~a\"
-~:[ enabled \"no\"~%~;~]\
-~:[ tags \"no\"~%~;~]\
-~:[~; always_on \"yes\"~%~]\
-~@[ mixer_type \"~a\"~%~]\
-~a~%}~%"
- (mpd-output-type output)
- (mpd-output-name output)
- (mpd-output-enabled? output)
- (mpd-output-tags? output)
- (mpd-output-always-on? output)
- (mpd-output-mixer-type output)
- extra)))
-
-(define (mpd-config->file config)
- (apply
- mixed-text-file "mpd.conf"
- "pid_file \"" (mpd-file-name config "pid") "\"\n"
- (append (map mpd-output->string
- (mpd-configuration-outputs config))
- (map (match-lambda
- ((config-name config-val)
- (string-append config-name " \"" (config-val config) "\"\n")))
- `(("user" ,mpd-configuration-user)
- ("music_directory" ,mpd-configuration-music-dir)
- ("playlist_directory" ,mpd-configuration-playlist-dir)
- ("db_file" ,mpd-configuration-db-file)
- ("state_file" ,mpd-configuration-state-file)
- ("sticker_file" ,mpd-configuration-sticker-file)
- ("port" ,mpd-configuration-port)
- ("bind_to_address" ,mpd-configuration-address))))))
+(define (uglify-field-name field-name)
+ (let ((str (symbol->string field-name)))
+ (string-join (string-split (if (string-suffix? "?" str)
+ (string-drop-right str 1)
+ str)
+ #\-)
+ "_")))
+
+(define (free-form-args? val)
+ (match val
+ (() #t)
+ ((((? symbol?) . (? string?)) . val) (free-form-args? val))
+ (_ #f)))
+
+(define* (mpd-serialize-field field-name value #:optional (indent-level 0))
+ #~(begin
+ (use-modules ((ice-9 format)))
+ (format #f "~v/~a \"~a\"~%" #$indent-level #$(if (string? field-name)
+ field-name
+ (uglify-field-name field-name)) #$value)))
+
+(define* (mpd-serialize-free-form-args field-name value #:optional (indent-level 0))
+ (generic-serialize-alist string-append (cut mpd-serialize-field <> <> indent-level) value))
+
+(define mpd-serialize-number mpd-serialize-field)
+
+(define mpd-serialize-string mpd-serialize-field)
+
+(define* (mpd-serialize-boolean field-name value #:optional (indent-level 0))
+ (mpd-serialize-field field-name (if value "yes" "no") indent-level))
+
+(define (mpd-serialize-list-of-mpd-output field-name value)
+ #~(string-append "\naudio_output {\n"
+ #$@(map (cut serialize-configuration <>
+ mpd-output-fields)
+ value)
+ "}\n"))
+
+(define (mpd-serialize-configuration configuration)
+ (mixed-text-file
+ "mpd.conf"
+ (serialize-configuration configuration mpd-configuration-fields)))
+
+(define mpd-subsystem-serialize-field (cut mpd-serialize-field <> <> 1))
+(define mpd-subsystem-serialize-string (cut mpd-serialize-string <> <> 1))
+(define mpd-subsystem-serialize-number (cut mpd-serialize-number <> <> 1))
+(define mpd-subsystem-serialize-boolean (cut mpd-serialize-boolean <> <> 1))
+(define mpd-subsystem-serialize-free-form-args (cut mpd-serialize-free-form-args <> <> 1))
+
+(define-configuration mpd-output
+ (name
+ (string "MPD")
+ "The name of the audio output.")
+ (type
+ (string "pulse")
+ "The type of audio output.")
+ (enabled?
+ (boolean #t)
+ "Specifies whether this audio output is enabled when MPD is started. By
+default, all audio outputs are enabled. This is just the default
+setting when there is no state file; with a state file, the previous
+state is restored.")
+ (tags?
+ (boolean #t)
+ "If set to @code{#f}, then MPD will not send tags to this output. This
+is only useful for output plugins that can receive tags, for example the
+@code{httpd} output plugin.")
+ (always-on?
+ (boolean #f)
+ "If set to @code{#t}, then MPD attempts to keep this audio output always
+open. This may be useful for streaming servers, when you don’t want to
+disconnect all listeners even when playback is accidentally stopped.")
+ (mixer-type
+ (string "none")
+ "This field accepts a symbol that specifies which mixer should be used
+for this audio output: the @code{hardware} mixer, the @code{software}
+mixer, the @code{null} mixer (allows setting the volume, but with no
+effect; this can be used as a trick to implement an external mixer
+External Mixer) or no mixer (@code{none}).")
+ (extra-options
+ (free-form-args '())
+ "An association list of option symbols to string values to be appended to
+the audio output configuration.")
+ (prefix mpd-subsystem-))
+
+(define list-of-mpd-output?
+ (list-of mpd-output?))
+
+(define-configuration mpd-configuration
+ (user
+ (string "mpd")
+ "The user to run mpd as.")
+ (music-dir
+ (string "~/Music")
+ "The directory to scan for music files."
+ (lambda (_ x)
+ (mpd-serialize-field "music_directory" x)))
+ (playlist-dir
+ (string "~/.mpd/playlists")
+ "The directory to store playlists."
+ (lambda (_ x)
+ (mpd-serialize-field "playlist_directory" x)))
+ (db-file
+ (string "~/.mpd/tag_cache")
+ "The location of the music database.")
+ (state-file
+ (string "~/.mpd/state")
+ "The location of the file that stores current MPD's state.")
+ (sticker-file
+ (string "~/.mpd/sticker.sql")
+ "The location of the sticker database.")
+ (port
+ (string "6600")
+ "The port to run mpd on.")
+ (address
+ (string "any")
+ "The address that mpd will bind to.
+To use a Unix domain socket, an absolute path can be specified here."
+ (lambda (_ x)
+ (mpd-serialize-field "bind_to_address" x)))
+ (outputs
+ (list-of-mpd-output (list (mpd-output)))
+ "The audio outputs that MPD can use.
+By default this is a single output using pulseaudio.")
+ (prefix mpd-))
(define (mpd-file-name config file)
"Return a path in /var/run/mpd/ that is writable
@@ -143,7 +185,7 @@ (define (mpd-shepherd-service config)
(start #~(make-forkexec-constructor
(list #$(file-append mpd "/bin/mpd")
"--no-daemon"
- #$(mpd-config->file config))
+ #$(mpd-serialize-configuration config))
#:environment-variables
;; Required to detect PulseAudio when run under a user account.
(list (string-append

base-commit: aae8371f72805cc35e31817e4120468eee4a4a80
--
2.38.1
M
[PATCH v5 2/2] services: mpd: Refactor MPD service.
(address . 59866@debbugs.gnu.org)(name . Bruno Victal)(address . mirai@makinata.eu)
fb7508dea3fb39f4d5fa572e37f18b8e1b3e442b.1671889887.git.mirai@makinata.eu
From: Bruno Victal <mirai@makinata.eu>

Introduces 'mpd-plugin' and 'mpd-partition' records.
Expands 'mpd-output' and 'mpd-configuration' records.
Deprecates redundant abbreviated fields in 'mpd-configuration' and avoids
serializing unused fields that may introduce undesired behavior.
Replace free-form-args serialization by making 'mpd-serialize-field' handle
multiple types and using it with 'generic-serialize-alist'.
Reduce code weight by removing cosmetic indented serialization procedures.
Offload logging from shepherd to MPD.
Implement log-rotation via rottlog.
Implement Shepherd actions: 'reopen' and 'configuration'.

* gnu/services/audio.scm
(mpd-plugin, mpd-partition): New record.

(mpd-plugin?, mpd-partition?, list-of-string?, list-of-symbol?)
(list-of-mpd-plugin?, list-of-mpd-partition?)
(list-of-mpd-plugin-or-output?, port?): New predicate.

(mpd-serialize-field): Handle multiple types.

(mpd-configuration)
[package, group, shepherd-requirement, log-file, log-level, music-directory]
[playlist-directory, endpoints, database, partitions, neighbors, inputs]
[archive-plugins, input-cache-size, decoders, resampler, filters]
[playlist-plugins, extra-options]: New field.
[music-dir, playlist-dir, address]: Deprecate shorthand field.
[db-file, state-file, sticker-file, port, outputs]: Change admissible type.

(mpd-log-rotation): New procedure.

(mpd-shepherd-service)
[actions]: New shepherd actions: 'reopen' and 'configuration'.
[requirement]: Splice with 'shepherd-requirement' field.
[start]: Use 'package' field. Remove #:log-file parameter.

(mpd-service-activation): Create logging directory. Handle migration
from old-style configuration.

(mpd-accounts): Do not hardcode username, change primary group to
"nogroup".

(mpd-service-type): Extend rottlog-service-type for log-rotation.
Update activation-service-type extension to reflect mpd-accounts
procedure change.

* doc/guix.texi (Audio Services)[Music Player Daemon]: Update doc.
---

Changes since v4:
* Retitled commit message and added ChangeLog formatted entries.
* Split long lines into shorter ones.
* Fix leftover mentions of "addresses".
* Document that endpoint addresses may contain ports in them.

doc/guix.texi | 177 +++++++++++++---
gnu/services/audio.scm | 463 +++++++++++++++++++++++++++++++----------
2 files changed, 506 insertions(+), 134 deletions(-)

Toggle diff (475 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index e25692fd27..5663d1913a 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -109,6 +109,7 @@ Copyright @copyright{} 2022 Reily Siegel@*
Copyright @copyright{} 2022 Simon Streit@*
Copyright @copyright{} 2022 (@*
Copyright @copyright{} 2022 John Kehayias@*
+Copyright @copyright{} 2022 Bruno Victal@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -33014,79 +33015,187 @@ The service type for @command{mpd}
Data type representing the configuration of @command{mpd}.
@table @asis
-@item @code{user} (default: @code{"mpd"})
+@item @code{package} (default: @code{mpd}) (type: file-like)
+The MPD package.
+
+@item @code{user} (default: @code{"mpd"}) (type: string)
The user to run mpd as.
-@item @code{music-dir} (default: @code{"~/Music"})
+@item @code{group} (type: maybe-string)
+The group to run mpd as.
+
+@item @code{shepherd-requirement} (default: @code{()}) (type: list-of-symbol)
+This is a list of symbols naming Shepherd services that this service
+will depend on.
+
+@item @code{log-file} (default: @code{"/var/log/mpd/log"}) (type: maybe-string)
+The location of the log file. Set to @code{syslog} to use the local
+syslog daemon or @code{%unset-value} to omit this directive from the
+configuration file.
+
+@item @code{log-level} (type: maybe-string)
+Supress any messages below this threshold. Available values:
+@code{notice}, @code{info}, @code{verbose}, @code{warning} and
+@code{error}.
+
+@item @code{music-directory} (type: maybe-string)
The directory to scan for music files.
-@item @code{playlist-dir} (default: @code{"~/.mpd/playlists"})
+@item @code{playlist-directory} (type: maybe-string)
The directory to store playlists.
-@item @code{db-file} (default: @code{"~/.mpd/tag_cache"})
+@item @code{db-file} (type: maybe-string)
The location of the music database.
-@item @code{state-file} (default: @code{"~/.mpd/state"})
+@item @code{state-file} (type: maybe-string)
The location of the file that stores current MPD's state.
-@item @code{sticker-file} (default: @code{"~/.mpd/sticker.sql"})
+@item @code{sticker-file} (type: maybe-string)
The location of the sticker database.
-@item @code{port} (default: @code{"6600"})
-The port to run mpd on.
+@item @code{default-port} (default: @code{6600}) (type: maybe-integer)
+The default port to run mpd on.
+
+@item @code{endpoints} (type: maybe-list-of-string)
+The addresses that mpd will bind to. A different port may be
+specified, e.g. @code{localhost:6602}. IPv6 addresses must be
+enclosed in square brackets if a different port is used.
+To use a Unix domain socket, an absolute path or a path starting with @code{~}
+can be specified here.
+
+@item @code{database} (type: maybe-mpd-plugin)
+MPD database plugin configuration.
+
+@item @code{partitions} (default: @code{()}) (type: list-of-mpd-partition)
+List of MPD "partitions".
-@item @code{address} (default: @code{"any"})
-The address that mpd will bind to. To use a Unix domain socket,
-an absolute path can be specified here.
+@item @code{neighbors} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD neighbor plugin configurations.
-@item @code{outputs} (default: @code{"(list (mpd-output))"})
-The audio outputs that MPD can use. By default this is a single output using pulseaudio.
+@item @code{inputs} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD input plugin configurations.
+
+@item @code{archive-plugins} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD archive plugin configurations.
+
+@item @code{input-cache-size} (type: maybe-string)
+MPD input cache size.
+
+@item @code{decoders} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD decoder plugin configurations.
+
+@item @code{resampler} (type: maybe-mpd-plugin)
+MPD resampler plugin configuration.
+
+@item @code{filters} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD filter plugin configurations.
+
+@item @code{outputs} (type: list-of-mpd-plugin-or-output)
+The audio outputs that MPD can use. By default this is a single output
+using pulseaudio.
+
+@item @code{playlist-plugins} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD playlist plugin configurations.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the configuration.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-plugin
+Data type representing a @command{mpd} plugin.
+
+@table @asis
+@item @code{plugin} (type: maybe-string)
+Plugin name.
+
+@item @code{name} (type: maybe-string)
+Name.
+
+@item @code{enabled?} (type: maybe-boolean)
+Whether the plugin is enabled/disabled.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the plugin configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin
+reference} for available options.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-partition
+Data type representing a @command{mpd} partition.
+
+@table @asis
+@item @code{name} (type: string)
+Partition name.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the partition configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions,Configuring
+Partitions} for available options.
@end table
@end deftp
@deftp {Data Type} mpd-output
-Data type representing an @command{mpd} audio output.
+Data type representing a @command{mpd} audio output.
@table @asis
-@item @code{name} (default: @code{"MPD"})
+@item @code{name} (default: @code{"MPD"}) (type: string)
The name of the audio output.
-@item @code{type} (default: @code{"pulse"})
+@item @code{type} (default: @code{"pulse"}) (type: string)
The type of audio output.
-@item @code{enabled?} (default: @code{#t})
+@item @code{enabled?} (default: @code{#t}) (type: boolean)
Specifies whether this audio output is enabled when MPD is started. By
default, all audio outputs are enabled. This is just the default
setting when there is no state file; with a state file, the previous
state is restored.
-@item @code{tags?} (default: @code{#t})
+@item @code{format} (type: maybe-string)
+Force a specific audio format on output. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#audio-output-format,Global
+Audio Format} for a more detailed description.
+
+@item @code{tags?} (default: @code{#t}) (type: boolean)
If set to @code{#f}, then MPD will not send tags to this output. This
is only useful for output plugins that can receive tags, for example the
@code{httpd} output plugin.
-@item @code{always-on?} (default: @code{#f})
+@item @code{always-on?} (default: @code{#f}) (type: boolean)
If set to @code{#t}, then MPD attempts to keep this audio output always
-open. This may be useful for streaming servers, when you don’t want to
+open. This may be useful for streaming servers, when you don?t want to
disconnect all listeners even when playback is accidentally stopped.
-@item @code{mixer-type}
-This field accepts a symbol that specifies which mixer should be used
+@item @code{mixer-type} (default: @code{"none"}) (type: string)
+This field accepts a string that specifies which mixer should be used
for this audio output: the @code{hardware} mixer, the @code{software}
mixer, the @code{null} mixer (allows setting the volume, but with no
effect; this can be used as a trick to implement an external mixer
External Mixer) or no mixer (@code{none}).
-@item @code{extra-options} (default: @code{'()})
-An association list of option symbols to string values to be appended to
-the audio output configuration.
+@item @code{replay-gain-handler} (type: maybe-string)
+This field accepts a string that specifies how
+@uref{https://mpd.readthedocs.io/en/latest/user.html#replay-gain,Replay
+Gain} is to be applied. @code{software} uses an internal software
+volume control, @code{mixer} uses the configured (hardware) mixer
+control and @code{none} disables replay gain on this audio output.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the audio output configuration.
@end table
@end deftp
-The following example shows a configuration of @code{mpd} that provides
-an HTTP audio streaming output.
+The following example shows a configuration of @command{mpd} that
+configures some of its plugins and provides a HTTP audio streaming output.
@lisp
(service mpd-service-type
@@ -33098,7 +33207,19 @@ an HTTP audio streaming output.
(mixer-type 'null)
(extra-options
`((encoder . "vorbis")
- (port . "8080"))))))))
+ (port . "8080"))))))
+ (decoders
+ (list (mpd-plugin
+ (plugin "mikmod")
+ (enabled? #f))
+ (mpd-plugin
+ (plugin "openmpt")
+ (enabled? #t)
+ (extra-options `((repeat-count . -1)
+ (interpolation-filter . 1))))))
+ (resampler (mpd-plugin
+ (plugin "libsamplerate")
+ (extra-options `((type . 0)))))))
@end lisp
diff --git a/gnu/services/audio.scm b/gnu/services/audio.scm
index 1a1026f342..54b00157b0 100644
--- a/gnu/services/audio.scm
+++ b/gnu/services/audio.scm
@@ -21,19 +21,27 @@
(define-module (gnu services audio)
#:use-module (guix gexp)
+ #:use-module (guix deprecation)
+ #:use-module (guix diagnostics)
+ #:use-module (guix i18n)
#:use-module (gnu services)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu services admin)
#:use-module (gnu system shadow)
#:use-module (gnu packages admin)
#:use-module (gnu packages mpd)
#:use-module (guix records)
#:use-module (ice-9 match)
- #:use-module (ice-9 format)
#:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-8)
#:use-module (srfi srfi-26)
#:export (mpd-output
mpd-output?
+ mpd-plugin
+ mpd-plugin?
+ mpd-partition
+ mpd-partition?
mpd-configuration
mpd-configuration?
mpd-service-type))
@@ -52,180 +60,421 @@ (define (uglify-field-name field-name)
#\-)
"_")))
-(define (free-form-args? val)
- (match val
- (() #t)
- ((((? symbol?) . (? string?)) . val) (free-form-args? val))
- (_ #f)))
+(define list-of-string?
+ (list-of string?))
-(define* (mpd-serialize-field field-name value #:optional (indent-level 0))
- #~(begin
- (use-modules ((ice-9 format)))
- (format #f "~v/~a \"~a\"~%" #$indent-level #$(if (string? field-name)
- field-name
- (uglify-field-name field-name)) #$value)))
+(define list-of-symbol?
+ (list-of symbol?))
-(define* (mpd-serialize-free-form-args field-name value #:optional (indent-level 0))
- (generic-serialize-alist string-append (cut mpd-serialize-field <> <> indent-level) value))
+(define (mpd-serialize-field field-name value)
+ (let ((field (if (string? field-name) field-name
+ (uglify-field-name field-name)))
+ (value (if (boolean? value) (if value "yes" "no") value)))
+ #~(format #f "~a \"~a\"~%" #$field #$value)))
(define mpd-serialize-number mpd-serialize-field)
(define mpd-serialize-string mpd-serialize-field)
-(define* (mpd-serialize-boolean field-name value #:optional (indent-level 0))
- (mpd-serialize-field field-name (if value "yes" "no") indent-level))
+(define mpd-serialize-boolean mpd-serialize-field)
-(define (mpd-serialize-list-of-mpd-output field-name value)
- #~(string-append "\naudio_output {\n"
- #$@(map (cut serialize-configuration <>
- mpd-output-fields)
- value)
- "}\n"))
+(define (mpd-serialize-alist field-name value)
+ #~(string-append #$@(generic-serialize-alist list
+ mpd-serialize-field
+ value)))
-(define (mpd-serialize-configuration configuration)
- (mixed-text-file
- "mpd.conf"
- (serialize-configuration configuration mpd-configuration-fields)))
+(define-maybe string (prefix mpd-))
+(define-maybe list-of-string (prefix mpd-))
+(define-maybe boolean (prefix mpd-))
+
+;;; TODO: Procedures for deprecated fields, to be removed.
+
+(define mpd-deprecated-fields '((music-dir . music-directory)
+ (playlist-dir . playlist-directory)
+ (address . endpoints)))
+
+(define (port? value) (or (string? value) (integer? value)))
+
+(define (mpd-serialize-deprecated-field field-name value)
+ (if (maybe-value-set? value)
+ (begin
+ (warn-about-deprecation
+ field-name #f
+ #:replacement (assoc-ref mpd-deprecated-fields field-name))
+ (match field-name
+ ('playlist-dir (mpd-serialize-string "playlist_directory" value))
+ ('music-dir (mpd-serialize-string "music_directory" value))
+ ('address (mpd-serialize-string "bind_to_address" value))))
+ ""))
+
+(define (mpd-serialize-port field-name value)
+ (when (string? value)
+ (warning
+ (G_ "string value for '~a' is deprecated, use integer instead~%")
+ field-name))
+ (mpd-serialize-field field-name value))
+
+(define-maybe port (prefix mpd-))
+
+;;;
+
+;; Generic MPD plugin record, lists only the most prevalent fields.
+(define-configuration mpd-plugin
+ (plugin
+ maybe-string
+ "Plugin name.")
+
+ (name
+ maybe-string
+ "Name.")
+
+ (enabled?
+ maybe-boolean
+ "Whether the plugin is enabled/disabled.")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols/strings to string values
+to be appended to the plugin configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin reference}
+for available options.")
+
+ (prefix mpd-))
+
+(define (mpd-serialize-mpd-plugin field-name value)
+ #~(format #f "~a {~%~a}~%"
+ '#$field-name
+ #$(serialize-configuration value mpd-plugin-fields)))
+
+(define (mpd-serialize-list-of-mpd-plugin field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-plugin field-name <>)
+ value)))
-(define mpd-subsystem-serialize-field (cut mpd-serialize-field <> <> 1))
-(define mpd-subsystem-serialize-string (cut mpd-serialize-string <> <> 1))
-(define mpd-subsystem-serialize-number (cut mpd-serialize-number <> <> 1))
-(define mpd-subsystem-serialize-boolean (cut mpd-serialize-boolean <> <> 1))
-(define mpd-subsystem-serialize-free-form-args (cut mpd-serialize-free-form-args <> <> 1))
+(define list-of-mpd-plugin? (list-of mpd-plugin?))
+
+(define-maybe mpd-plugin (prefix mpd-))
+
+(define-configuration mpd-partition
+ (name
+ string
+ "Partition name.")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols/strings to string values
+to be appended to the partition configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions,Configuring Partitions}
+for available options.")
+
+ (prefix mpd-))
+
+(define (mpd-serialize-mpd-partition field-name value)
+ #~(format #f "partition {~%~a}~%"
+ #$(serialize-configuration value mpd-partition-fields)))
+
+(define (mpd-serialize-list-of-mpd-partition field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-partition #f <>) value)))
+
+(define list-of-mpd-partition?
+ (list-of mpd-partition?))
(define-configuration mpd-output
(name
(string "MPD")
"The name of the audio output.")
+
(type
(string "pulse")
"The type of audio output.")
+
(enabled?
(boolean #t)
"Specifies whether this audio output is enabled when MPD is started. By
default, all audio outputs are enabled. This is just the default
setting when there is no state file; with a state file, the previous
state is restored.")
+
+ (format
+ maybe-string
+ "Force a specific audio format on output. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#audio-output-format,Global Audio Format}
+for a more detailed description.")
+
(tags?
(boolean #t)
"If set to @code{#f}, then MPD will not send tags to this output. This
is only useful for output plugins that can receive tags, for example the
@code{httpd} output plugin.")
+
(always-on?
(boolean #f)
"If set to @code{#t}, then MPD attempts to keep this audio output always
open. This may be useful for streaming servers, when you don’t want to
disconnect all listeners even when playback is accidentally stopped.")
+
(mixer-type
(string "none")
- "This field accepts a symbol that specifies which mixer should be used
+ "This field accepts a string that specifies which mixer should be used
for this audio output: the @code{hardware} mixer, the @code{software}
mixer, the @code{null} mixer (allows setting the volume, but with no
effect; this can be used as a trick to implement an external mixer
E
This message was truncated. Download the full message here.
M
[PATCH v5.1] services: mpd: Refactor MPD service.
(address . 59866@debbugs.gnu.org)(name . Bruno Victal)(address . mirai@makinata.eu)
099d959851cb9ce9bf4afdedd01825b7ccf78a73.1671891059.git.mirai@makinata.eu
From: Bruno Victal <mirai@makinata.eu>

Introduces 'mpd-plugin' and 'mpd-partition' records.
Expands 'mpd-output' and 'mpd-configuration' records.
Deprecates redundant abbreviated fields in 'mpd-configuration' and avoids
serializing unused fields that may introduce undesired behavior.
Replace free-form-args serialization by making 'mpd-serialize-field' handle
multiple types and using it with 'generic-serialize-alist'.
Reduce code weight by removing cosmetic indented serialization procedures.
Offload logging from shepherd to MPD.
Implement log-rotation via rottlog.
Implement Shepherd actions: 'reopen' and 'configuration'.

* gnu/services/audio.scm
(mpd-plugin, mpd-partition): New record.

(mpd-plugin?, mpd-partition?, list-of-string?, list-of-symbol?)
(list-of-mpd-plugin?, list-of-mpd-partition?)
(list-of-mpd-plugin-or-output?, port?): New predicate.

(mpd-serialize-field): Handle multiple types.

(mpd-configuration)
[package, group, shepherd-requirement, log-file, log-level, music-directory]
[playlist-directory, endpoints, database, partitions, neighbors, inputs]
[archive-plugins, input-cache-size, decoders, resampler, filters]
[playlist-plugins, extra-options]: New field.
[music-dir, playlist-dir, address]: Deprecate shorthand field.
[db-file, state-file, sticker-file, port, outputs]: Change admissible type.

(mpd-log-rotation): New procedure.

(mpd-shepherd-service)
[actions]: New shepherd actions: 'reopen' and 'configuration'.
[requirement]: Splice with 'shepherd-requirement' field.
[start]: Use 'package' field. Remove #:log-file parameter.

(mpd-service-activation): Create logging directory. Handle migration
from old-style configuration.

(mpd-accounts): Do not hardcode username, change primary group to
"nogroup".

(mpd-service-type): Extend rottlog-service-type for log-rotation.
Update activation-service-type extension to reflect mpd-accounts
procedure change.

* doc/guix.texi (Audio Services)[Music Player Daemon]: Update doc.
---

Changes since v5:
* Serialize default_port field-name as "port".

doc/guix.texi | 177 +++++++++++++---
gnu/services/audio.scm | 463 +++++++++++++++++++++++++++++++----------
2 files changed, 506 insertions(+), 134 deletions(-)

Toggle diff (480 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index e25692fd27..5663d1913a 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -109,6 +109,7 @@ Copyright @copyright{} 2022 Reily Siegel@*
Copyright @copyright{} 2022 Simon Streit@*
Copyright @copyright{} 2022 (@*
Copyright @copyright{} 2022 John Kehayias@*
+Copyright @copyright{} 2022 Bruno Victal@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -33014,79 +33015,187 @@ The service type for @command{mpd}
Data type representing the configuration of @command{mpd}.
@table @asis
-@item @code{user} (default: @code{"mpd"})
+@item @code{package} (default: @code{mpd}) (type: file-like)
+The MPD package.
+
+@item @code{user} (default: @code{"mpd"}) (type: string)
The user to run mpd as.
-@item @code{music-dir} (default: @code{"~/Music"})
+@item @code{group} (type: maybe-string)
+The group to run mpd as.
+
+@item @code{shepherd-requirement} (default: @code{()}) (type: list-of-symbol)
+This is a list of symbols naming Shepherd services that this service
+will depend on.
+
+@item @code{log-file} (default: @code{"/var/log/mpd/log"}) (type: maybe-string)
+The location of the log file. Set to @code{syslog} to use the local
+syslog daemon or @code{%unset-value} to omit this directive from the
+configuration file.
+
+@item @code{log-level} (type: maybe-string)
+Supress any messages below this threshold. Available values:
+@code{notice}, @code{info}, @code{verbose}, @code{warning} and
+@code{error}.
+
+@item @code{music-directory} (type: maybe-string)
The directory to scan for music files.
-@item @code{playlist-dir} (default: @code{"~/.mpd/playlists"})
+@item @code{playlist-directory} (type: maybe-string)
The directory to store playlists.
-@item @code{db-file} (default: @code{"~/.mpd/tag_cache"})
+@item @code{db-file} (type: maybe-string)
The location of the music database.
-@item @code{state-file} (default: @code{"~/.mpd/state"})
+@item @code{state-file} (type: maybe-string)
The location of the file that stores current MPD's state.
-@item @code{sticker-file} (default: @code{"~/.mpd/sticker.sql"})
+@item @code{sticker-file} (type: maybe-string)
The location of the sticker database.
-@item @code{port} (default: @code{"6600"})
-The port to run mpd on.
+@item @code{default-port} (default: @code{6600}) (type: maybe-integer)
+The default port to run mpd on.
+
+@item @code{endpoints} (type: maybe-list-of-string)
+The addresses that mpd will bind to. A different port may be
+specified, e.g. @code{localhost:6602}. IPv6 addresses must be
+enclosed in square brackets if a different port is used.
+To use a Unix domain socket, an absolute path or a path starting with @code{~}
+can be specified here.
+
+@item @code{database} (type: maybe-mpd-plugin)
+MPD database plugin configuration.
+
+@item @code{partitions} (default: @code{()}) (type: list-of-mpd-partition)
+List of MPD "partitions".
-@item @code{address} (default: @code{"any"})
-The address that mpd will bind to. To use a Unix domain socket,
-an absolute path can be specified here.
+@item @code{neighbors} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD neighbor plugin configurations.
-@item @code{outputs} (default: @code{"(list (mpd-output))"})
-The audio outputs that MPD can use. By default this is a single output using pulseaudio.
+@item @code{inputs} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD input plugin configurations.
+
+@item @code{archive-plugins} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD archive plugin configurations.
+
+@item @code{input-cache-size} (type: maybe-string)
+MPD input cache size.
+
+@item @code{decoders} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD decoder plugin configurations.
+
+@item @code{resampler} (type: maybe-mpd-plugin)
+MPD resampler plugin configuration.
+
+@item @code{filters} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD filter plugin configurations.
+
+@item @code{outputs} (type: list-of-mpd-plugin-or-output)
+The audio outputs that MPD can use. By default this is a single output
+using pulseaudio.
+
+@item @code{playlist-plugins} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD playlist plugin configurations.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the configuration.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-plugin
+Data type representing a @command{mpd} plugin.
+
+@table @asis
+@item @code{plugin} (type: maybe-string)
+Plugin name.
+
+@item @code{name} (type: maybe-string)
+Name.
+
+@item @code{enabled?} (type: maybe-boolean)
+Whether the plugin is enabled/disabled.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the plugin configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin
+reference} for available options.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-partition
+Data type representing a @command{mpd} partition.
+
+@table @asis
+@item @code{name} (type: string)
+Partition name.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the partition configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions,Configuring
+Partitions} for available options.
@end table
@end deftp
@deftp {Data Type} mpd-output
-Data type representing an @command{mpd} audio output.
+Data type representing a @command{mpd} audio output.
@table @asis
-@item @code{name} (default: @code{"MPD"})
+@item @code{name} (default: @code{"MPD"}) (type: string)
The name of the audio output.
-@item @code{type} (default: @code{"pulse"})
+@item @code{type} (default: @code{"pulse"}) (type: string)
The type of audio output.
-@item @code{enabled?} (default: @code{#t})
+@item @code{enabled?} (default: @code{#t}) (type: boolean)
Specifies whether this audio output is enabled when MPD is started. By
default, all audio outputs are enabled. This is just the default
setting when there is no state file; with a state file, the previous
state is restored.
-@item @code{tags?} (default: @code{#t})
+@item @code{format} (type: maybe-string)
+Force a specific audio format on output. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#audio-output-format,Global
+Audio Format} for a more detailed description.
+
+@item @code{tags?} (default: @code{#t}) (type: boolean)
If set to @code{#f}, then MPD will not send tags to this output. This
is only useful for output plugins that can receive tags, for example the
@code{httpd} output plugin.
-@item @code{always-on?} (default: @code{#f})
+@item @code{always-on?} (default: @code{#f}) (type: boolean)
If set to @code{#t}, then MPD attempts to keep this audio output always
-open. This may be useful for streaming servers, when you don’t want to
+open. This may be useful for streaming servers, when you don?t want to
disconnect all listeners even when playback is accidentally stopped.
-@item @code{mixer-type}
-This field accepts a symbol that specifies which mixer should be used
+@item @code{mixer-type} (default: @code{"none"}) (type: string)
+This field accepts a string that specifies which mixer should be used
for this audio output: the @code{hardware} mixer, the @code{software}
mixer, the @code{null} mixer (allows setting the volume, but with no
effect; this can be used as a trick to implement an external mixer
External Mixer) or no mixer (@code{none}).
-@item @code{extra-options} (default: @code{'()})
-An association list of option symbols to string values to be appended to
-the audio output configuration.
+@item @code{replay-gain-handler} (type: maybe-string)
+This field accepts a string that specifies how
+@uref{https://mpd.readthedocs.io/en/latest/user.html#replay-gain,Replay
+Gain} is to be applied. @code{software} uses an internal software
+volume control, @code{mixer} uses the configured (hardware) mixer
+control and @code{none} disables replay gain on this audio output.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the audio output configuration.
@end table
@end deftp
-The following example shows a configuration of @code{mpd} that provides
-an HTTP audio streaming output.
+The following example shows a configuration of @command{mpd} that
+configures some of its plugins and provides a HTTP audio streaming output.
@lisp
(service mpd-service-type
@@ -33098,7 +33207,19 @@ an HTTP audio streaming output.
(mixer-type 'null)
(extra-options
`((encoder . "vorbis")
- (port . "8080"))))))))
+ (port . "8080"))))))
+ (decoders
+ (list (mpd-plugin
+ (plugin "mikmod")
+ (enabled? #f))
+ (mpd-plugin
+ (plugin "openmpt")
+ (enabled? #t)
+ (extra-options `((repeat-count . -1)
+ (interpolation-filter . 1))))))
+ (resampler (mpd-plugin
+ (plugin "libsamplerate")
+ (extra-options `((type . 0)))))))
@end lisp
diff --git a/gnu/services/audio.scm b/gnu/services/audio.scm
index 1a1026f342..e456205e99 100644
--- a/gnu/services/audio.scm
+++ b/gnu/services/audio.scm
@@ -21,19 +21,27 @@
(define-module (gnu services audio)
#:use-module (guix gexp)
+ #:use-module (guix deprecation)
+ #:use-module (guix diagnostics)
+ #:use-module (guix i18n)
#:use-module (gnu services)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu services admin)
#:use-module (gnu system shadow)
#:use-module (gnu packages admin)
#:use-module (gnu packages mpd)
#:use-module (guix records)
#:use-module (ice-9 match)
- #:use-module (ice-9 format)
#:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-8)
#:use-module (srfi srfi-26)
#:export (mpd-output
mpd-output?
+ mpd-plugin
+ mpd-plugin?
+ mpd-partition
+ mpd-partition?
mpd-configuration
mpd-configuration?
mpd-service-type))
@@ -52,180 +60,421 @@ (define (uglify-field-name field-name)
#\-)
"_")))
-(define (free-form-args? val)
- (match val
- (() #t)
- ((((? symbol?) . (? string?)) . val) (free-form-args? val))
- (_ #f)))
+(define list-of-string?
+ (list-of string?))
-(define* (mpd-serialize-field field-name value #:optional (indent-level 0))
- #~(begin
- (use-modules ((ice-9 format)))
- (format #f "~v/~a \"~a\"~%" #$indent-level #$(if (string? field-name)
- field-name
- (uglify-field-name field-name)) #$value)))
+(define list-of-symbol?
+ (list-of symbol?))
-(define* (mpd-serialize-free-form-args field-name value #:optional (indent-level 0))
- (generic-serialize-alist string-append (cut mpd-serialize-field <> <> indent-level) value))
+(define (mpd-serialize-field field-name value)
+ (let ((field (if (string? field-name) field-name
+ (uglify-field-name field-name)))
+ (value (if (boolean? value) (if value "yes" "no") value)))
+ #~(format #f "~a \"~a\"~%" #$field #$value)))
(define mpd-serialize-number mpd-serialize-field)
(define mpd-serialize-string mpd-serialize-field)
-(define* (mpd-serialize-boolean field-name value #:optional (indent-level 0))
- (mpd-serialize-field field-name (if value "yes" "no") indent-level))
+(define mpd-serialize-boolean mpd-serialize-field)
-(define (mpd-serialize-list-of-mpd-output field-name value)
- #~(string-append "\naudio_output {\n"
- #$@(map (cut serialize-configuration <>
- mpd-output-fields)
- value)
- "}\n"))
+(define (mpd-serialize-alist field-name value)
+ #~(string-append #$@(generic-serialize-alist list
+ mpd-serialize-field
+ value)))
-(define (mpd-serialize-configuration configuration)
- (mixed-text-file
- "mpd.conf"
- (serialize-configuration configuration mpd-configuration-fields)))
+(define-maybe string (prefix mpd-))
+(define-maybe list-of-string (prefix mpd-))
+(define-maybe boolean (prefix mpd-))
+
+;;; TODO: Procedures for deprecated fields, to be removed.
+
+(define mpd-deprecated-fields '((music-dir . music-directory)
+ (playlist-dir . playlist-directory)
+ (address . endpoints)))
+
+(define (port? value) (or (string? value) (integer? value)))
+
+(define (mpd-serialize-deprecated-field field-name value)
+ (if (maybe-value-set? value)
+ (begin
+ (warn-about-deprecation
+ field-name #f
+ #:replacement (assoc-ref mpd-deprecated-fields field-name))
+ (match field-name
+ ('playlist-dir (mpd-serialize-string "playlist_directory" value))
+ ('music-dir (mpd-serialize-string "music_directory" value))
+ ('address (mpd-serialize-string "bind_to_address" value))))
+ ""))
+
+(define (mpd-serialize-port field-name value)
+ (when (string? value)
+ (warning
+ (G_ "string value for '~a' is deprecated, use integer instead~%")
+ field-name))
+ (mpd-serialize-field "port" value))
+
+(define-maybe port (prefix mpd-))
+
+;;;
+
+;; Generic MPD plugin record, lists only the most prevalent fields.
+(define-configuration mpd-plugin
+ (plugin
+ maybe-string
+ "Plugin name.")
+
+ (name
+ maybe-string
+ "Name.")
+
+ (enabled?
+ maybe-boolean
+ "Whether the plugin is enabled/disabled.")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols/strings to string values
+to be appended to the plugin configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin reference}
+for available options.")
+
+ (prefix mpd-))
+
+(define (mpd-serialize-mpd-plugin field-name value)
+ #~(format #f "~a {~%~a}~%"
+ '#$field-name
+ #$(serialize-configuration value mpd-plugin-fields)))
+
+(define (mpd-serialize-list-of-mpd-plugin field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-plugin field-name <>)
+ value)))
-(define mpd-subsystem-serialize-field (cut mpd-serialize-field <> <> 1))
-(define mpd-subsystem-serialize-string (cut mpd-serialize-string <> <> 1))
-(define mpd-subsystem-serialize-number (cut mpd-serialize-number <> <> 1))
-(define mpd-subsystem-serialize-boolean (cut mpd-serialize-boolean <> <> 1))
-(define mpd-subsystem-serialize-free-form-args (cut mpd-serialize-free-form-args <> <> 1))
+(define list-of-mpd-plugin? (list-of mpd-plugin?))
+
+(define-maybe mpd-plugin (prefix mpd-))
+
+(define-configuration mpd-partition
+ (name
+ string
+ "Partition name.")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols/strings to string values
+to be appended to the partition configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions,Configuring Partitions}
+for available options.")
+
+ (prefix mpd-))
+
+(define (mpd-serialize-mpd-partition field-name value)
+ #~(format #f "partition {~%~a}~%"
+ #$(serialize-configuration value mpd-partition-fields)))
+
+(define (mpd-serialize-list-of-mpd-partition field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-partition #f <>) value)))
+
+(define list-of-mpd-partition?
+ (list-of mpd-partition?))
(define-configuration mpd-output
(name
(string "MPD")
"The name of the audio output.")
+
(type
(string "pulse")
"The type of audio output.")
+
(enabled?
(boolean #t)
"Specifies whether this audio output is enabled when MPD is started. By
default, all audio outputs are enabled. This is just the default
setting when there is no state file; with a state file, the previous
state is restored.")
+
+ (format
+ maybe-string
+ "Force a specific audio format on output. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#audio-output-format,Global Audio Format}
+for a more detailed description.")
+
(tags?
(boolean #t)
"If set to @code{#f}, then MPD will not send tags to this output. This
is only useful for output plugins that can receive tags, for example the
@code{httpd} output plugin.")
+
(always-on?
(boolean #f)
"If set to @code{#t}, then MPD attempts to keep this audio output always
open. This may be useful for streaming servers, when you don’t want to
disconnect all listeners even when playback is accidentally stopped.")
+
(mixer-type
(string "none")
- "This field accepts a symbol that specifies which mixer should be used
+ "This field accepts a string that specifies which mixer should be used
for this audio output: the @code{hardware} mixer, the @code{software}
mixer, the @code{null} mixer (allows setting the volume, but with no
effect; this can be used as a trick to implement an external mixer
External Mixer) or no mixer (@code{none}).")
+
+ (replay-gain-handler
+ maybe-string
+ "This field accepts a string that specifies how
+@uref{https://mpd.readthe
This message was truncated. Download the full message here.
L
L
Liliana Marie Prikler wrote on 24 Dec 2022 18:20
4a93981e06259ffac47ecc6f988c37d1910593ee.camel@gmail.com
Am Samstag, dem 24.12.2022 um 14:11 +0000 schrieb mirai@makinata.eu:
Toggle quote (15 lines)
> From: Bruno Victal <mirai@makinata.eu>
>
> Introduces 'mpd-plugin' and 'mpd-partition' records.
> Expands 'mpd-output' and 'mpd-configuration' records.
> Deprecates redundant abbreviated fields in 'mpd-configuration' and
> avoids
> serializing unused fields that may introduce undesired behavior.
> Replace free-form-args serialization by making 'mpd-serialize-field'
> handle
> multiple types and using it with 'generic-serialize-alist'.
> Reduce code weight by removing cosmetic indented serialization
> procedures.
> Offload logging from shepherd to MPD.
> Implement log-rotation via rottlog.
> Implement Shepherd actions: 'reopen' and 'configuration'.
The things you wrote here read a like a less structured ChangeLog. The
section between header and ChangeLog should instead be used to give
some wider context if needed, imo.

Toggle quote (32 lines)
> * gnu/services/audio.scm
> (mpd-plugin, mpd-partition): New record.
>
> (mpd-plugin?, mpd-partition?, list-of-string?, list-of-symbol?)
> (list-of-mpd-plugin?, list-of-mpd-partition?)
> (list-of-mpd-plugin-or-output?, port?): New predicate.
>
> (mpd-serialize-field): Handle multiple types.
>
> (mpd-configuration)
> [package, group, shepherd-requirement, log-file, log-level, music-
> directory]
> [playlist-directory, endpoints, database, partitions, neighbors,
> inputs]
> [archive-plugins, input-cache-size, decoders, resampler, filters]
> [playlist-plugins, extra-options]: New field.
> [music-dir, playlist-dir, address]: Deprecate shorthand field.
> [db-file, state-file, sticker-file, port, outputs]: Change admissible
> type.
>
> (mpd-log-rotation): New procedure.
>
> (mpd-shepherd-service)
> [actions]: New shepherd actions: 'reopen' and 'configuration'.
> [requirement]: Splice with 'shepherd-requirement' field.
> [start]: Use 'package' field. Remove #:log-file parameter.
>
> (mpd-service-activation): Create logging directory. Handle migration
> from old-style configuration.
>
> (mpd-accounts): Do not hardcode username, change primary group to
> "nogroup".
While I welcome the extensibility here, I think the default should
still be mpd:mpd. I think you should make it so that you can pass a
user-account and user-group to the mpd service so that they can be
reused (with a sanitizer that creates a user/group from string).

Toggle quote (92 lines)
> (mpd-service-type): Extend rottlog-service-type for log-rotation.
> Update activation-service-type extension to reflect mpd-accounts
> procedure change.
>
> * doc/guix.texi (Audio Services)[Music Player Daemon]: Update doc.
> ---
>
>  Changes since v5:
>  * Serialize default_port field-name as "port".
>
>  doc/guix.texi          | 177 +++++++++++++---
>  gnu/services/audio.scm | 463 +++++++++++++++++++++++++++++++--------
> --
>  2 files changed, 506 insertions(+), 134 deletions(-)
>
> diff --git a/doc/guix.texi b/doc/guix.texi
> index e25692fd27..5663d1913a 100644
> --- a/doc/guix.texi
> +++ b/doc/guix.texi
> @@ -109,6 +109,7 @@ Copyright @copyright{} 2022 Reily Siegel@*
>  Copyright @copyright{} 2022 Simon Streit@*
>  Copyright @copyright{} 2022 (@*
>  Copyright @copyright{} 2022 John Kehayias@*
> +Copyright @copyright{} 2022 Bruno Victal@*
>  
>  Permission is granted to copy, distribute and/or modify this
> document
>  under the terms of the GNU Free Documentation License, Version 1.3
> or
> @@ -33014,79 +33015,187 @@ The service type for @command{mpd}
>  Data type representing the configuration of @command{mpd}.
>  
>  @table @asis
> -@item @code{user} (default: @code{"mpd"})
> +@item @code{package} (default: @code{mpd}) (type: file-like)
> +The MPD package.
> +
> +@item @code{user} (default: @code{"mpd"}) (type: string)
>  The user to run mpd as.
>  
> -@item @code{music-dir} (default: @code{"~/Music"})
> +@item @code{group} (type: maybe-string)
> +The group to run mpd as.
> +
> +@item @code{shepherd-requirement} (default: @code{()}) (type: list-
> of-symbol)
> +This is a list of symbols naming Shepherd services that this service
> +will depend on.
> +
> +@item @code{log-file} (default: @code{"/var/log/mpd/log"}) (type:
> maybe-string)
> +The location of the log file.  Set to @code{syslog} to use the local
> +syslog daemon or @code{%unset-value} to omit this directive from the
> +configuration file.
> +
> +@item @code{log-level} (type: maybe-string)
> +Supress any messages below this threshold.  Available values:
> +@code{notice}, @code{info}, @code{verbose}, @code{warning} and
> +@code{error}.
> +
> +@item @code{music-directory} (type: maybe-string)
>  The directory to scan for music files.
>  
> -@item @code{playlist-dir} (default: @code{"~/.mpd/playlists"})
> +@item @code{playlist-directory} (type: maybe-string)
>  The directory to store playlists.
>  
> -@item @code{db-file} (default: @code{"~/.mpd/tag_cache"})
> +@item @code{db-file} (type: maybe-string)
>  The location of the music database.
>  
> -@item @code{state-file} (default: @code{"~/.mpd/state"})
> +@item @code{state-file} (type: maybe-string)
>  The location of the file that stores current MPD's state.
>  
> -@item @code{sticker-file} (default: @code{"~/.mpd/sticker.sql"})
> +@item @code{sticker-file} (type: maybe-string)
>  The location of the sticker database.
>  
> -@item @code{port} (default: @code{"6600"})
> -The port to run mpd on.
> +@item @code{default-port} (default: @code{6600}) (type: maybe-
> integer)
> +The default port to run mpd on.
> +
> +@item @code{endpoints} (type: maybe-list-of-string)
> +The addresses that mpd will bind to.  A different port may be
> +specified, e.g. @code{localhost:6602}.  IPv6 addresses must be
> +enclosed in square brackets if a different port is used.
> +To use a Unix domain socket, an absolute path or a path starting
> with @code{~}
> +can be specified here.
LGTM, but "A different port" should probably be "A port different from
@var{default-port}.

Toggle quote (406 lines)
> +@item @code{database} (type: maybe-mpd-plugin)
> +MPD database plugin configuration.
> +
> +@item @code{partitions} (default: @code{()}) (type: list-of-mpd-
> partition)
> +List of MPD "partitions".
>  
> -@item @code{address} (default: @code{"any"})
> -The address that mpd will bind to.  To use a Unix domain socket,
> -an absolute path can be specified here.
> +@item @code{neighbors} (default: @code{()}) (type: list-of-mpd-
> plugin)
> +List of MPD neighbor plugin configurations.
>  
> -@item @code{outputs} (default: @code{"(list (mpd-output))"})
> -The audio outputs that MPD can use.  By default this is a single
> output using pulseaudio.
> +@item @code{inputs} (default: @code{()}) (type: list-of-mpd-plugin)
> +List of MPD input plugin configurations.
> +
> +@item @code{archive-plugins} (default: @code{()}) (type: list-of-
> mpd-plugin)
> +List of MPD archive plugin configurations.
> +
> +@item @code{input-cache-size} (type: maybe-string)
> +MPD input cache size.
> +
> +@item @code{decoders} (default: @code{()}) (type: list-of-mpd-
> plugin)
> +List of MPD decoder plugin configurations.
> +
> +@item @code{resampler} (type: maybe-mpd-plugin)
> +MPD resampler plugin configuration.
> +
> +@item @code{filters} (default: @code{()}) (type: list-of-mpd-plugin)
> +List of MPD filter plugin configurations.
> +
> +@item @code{outputs} (type: list-of-mpd-plugin-or-output)
> +The audio outputs that MPD can use.  By default this is a single
> output
> +using pulseaudio.
> +
> +@item @code{playlist-plugins} (default: @code{()}) (type: list-of-
> mpd-plugin)
> +List of MPD playlist plugin configurations.
> +
> +@item @code{extra-options} (default: @code{()}) (type: alist)
> +An association list of option symbols/strings to string values to be
> +appended to the configuration.
> +
> +@end table
> +@end deftp
> +
> +@deftp {Data Type} mpd-plugin
> +Data type representing a @command{mpd} plugin.
> +
> +@table @asis
> +@item @code{plugin} (type: maybe-string)
> +Plugin name.
> +
> +@item @code{name} (type: maybe-string)
> +Name.
> +
> +@item @code{enabled?} (type: maybe-boolean)
> +Whether the plugin is enabled/disabled.
> +
> +@item @code{extra-options} (default: @code{()}) (type: alist)
> +An association list of option symbols/strings to string values to be
> +appended to the plugin configuration.  See
> +@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin
> +reference} for available options.
> +
> +@end table
> +@end deftp
> +
> +@deftp {Data Type} mpd-partition
> +Data type representing a @command{mpd} partition.
> +
> +@table @asis
> +@item @code{name} (type: string)
> +Partition name.
> +
> +@item @code{extra-options} (default: @code{()}) (type: alist)
> +An association list of option symbols/strings to string values to be
> +appended to the partition configuration.  See
> +@uref{
> https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions
> ,Configuring
> +Partitions} for available options.
>  
>  @end table
>  @end deftp
>  
>  @deftp {Data Type} mpd-output
> -Data type representing an @command{mpd} audio output.
> +Data type representing a @command{mpd} audio output.
>  
>  @table @asis
> -@item @code{name} (default: @code{"MPD"})
> +@item @code{name} (default: @code{"MPD"}) (type: string)
>  The name of the audio output.
>  
> -@item @code{type} (default: @code{"pulse"})
> +@item @code{type} (default: @code{"pulse"}) (type: string)
>  The type of audio output.
>  
> -@item @code{enabled?} (default: @code{#t})
> +@item @code{enabled?} (default: @code{#t}) (type: boolean)
>  Specifies whether this audio output is enabled when MPD is started. 
> By
>  default, all audio outputs are enabled.  This is just the default
>  setting when there is no state file; with a state file, the previous
>  state is restored.
>  
> -@item @code{tags?} (default: @code{#t})
> +@item @code{format} (type: maybe-string)
> +Force a specific audio format on output.  See
> +@uref{
> https://mpd.readthedocs.io/en/latest/user.html#audio-output-format,Gl
> obal
> +Audio Format} for a more detailed description.
> +
> +@item @code{tags?} (default: @code{#t}) (type: boolean)
>  If set to @code{#f}, then MPD will not send tags to this output. 
> This
>  is only useful for output plugins that can receive tags, for example
> the
>  @code{httpd} output plugin.
>  
> -@item @code{always-on?} (default: @code{#f})
> +@item @code{always-on?} (default: @code{#f}) (type: boolean)
>  If set to @code{#t}, then MPD attempts to keep this audio output
> always
> -open.  This may be useful for streaming servers, when you don’t want
> to
> +open.  This may be useful for streaming servers, when you don?t want
> to
>  disconnect all listeners even when playback is accidentally stopped.
>  
> -@item @code{mixer-type}
> -This field accepts a symbol that specifies which mixer should be
> used
> +@item @code{mixer-type} (default: @code{"none"}) (type: string)
> +This field accepts a string that specifies which mixer should be
> used
>  for this audio output: the @code{hardware} mixer, the
> @code{software}
>  mixer, the @code{null} mixer (allows setting the volume, but with no
>  effect; this can be used as a trick to implement an external mixer
>  External Mixer) or no mixer (@code{none}).
>  
> -@item @code{extra-options} (default: @code{'()})
> -An association list of option symbols to string values to be
> appended to
> -the audio output configuration.
> +@item @code{replay-gain-handler} (type: maybe-string)
> +This field accepts a string that specifies how
> +@uref{
> https://mpd.readthedocs.io/en/latest/user.html#replay-gain,Replay
> +Gain} is to be applied.  @code{software} uses an internal software
> +volume control, @code{mixer} uses the configured (hardware) mixer
> +control and @code{none} disables replay gain on this audio output.
> +
> +@item @code{extra-options} (default: @code{()}) (type: alist)
> +An association list of option symbols/strings to string values to be
> +appended to the audio output configuration.
>  
>  @end table
>  @end deftp
>  
> -The following example shows a configuration of @code{mpd} that
> provides
> -an HTTP audio streaming output.
> +The following example shows a configuration of @command{mpd} that
> +configures some of its plugins and provides a HTTP audio streaming
> output.
>  
>  @lisp
>  (service mpd-service-type
> @@ -33098,7 +33207,19 @@ an HTTP audio streaming output.
>                       (mixer-type 'null)
>                       (extra-options
>                        `((encoder . "vorbis")
> -                        (port    . "8080"))))))))
> +                        (port    . "8080"))))))
> +           (decoders
> +             (list (mpd-plugin
> +                     (plugin "mikmod")
> +                     (enabled? #f))
> +                   (mpd-plugin
> +                     (plugin "openmpt")
> +                     (enabled? #t)
> +                     (extra-options `((repeat-count . -1)
> +                                      (interpolation-filter .
> 1))))))
> +           (resampler (mpd-plugin
> +                        (plugin "libsamplerate")
> +                        (extra-options `((type . 0)))))))
>  @end lisp
>  
>  
> diff --git a/gnu/services/audio.scm b/gnu/services/audio.scm
> index 1a1026f342..e456205e99 100644
> --- a/gnu/services/audio.scm
> +++ b/gnu/services/audio.scm
> @@ -21,19 +21,27 @@
>  
>  (define-module (gnu services audio)
>    #:use-module (guix gexp)
> +  #:use-module (guix deprecation)
> +  #:use-module (guix diagnostics)
> +  #:use-module (guix i18n)
>    #:use-module (gnu services)
>    #:use-module (gnu services configuration)
>    #:use-module (gnu services shepherd)
> +  #:use-module (gnu services admin)
>    #:use-module (gnu system shadow)
>    #:use-module (gnu packages admin)
>    #:use-module (gnu packages mpd)
>    #:use-module (guix records)
>    #:use-module (ice-9 match)
> -  #:use-module (ice-9 format)
>    #:use-module (srfi srfi-1)
> +  #:use-module (srfi srfi-8)
>    #:use-module (srfi srfi-26)
>    #:export (mpd-output
>              mpd-output?
> +            mpd-plugin
> +            mpd-plugin?
> +            mpd-partition
> +            mpd-partition?
>              mpd-configuration
>              mpd-configuration?
>              mpd-service-type))
> @@ -52,180 +60,421 @@ (define (uglify-field-name field-name)
>                                 #\-)
>                   "_")))
>  
> -(define (free-form-args? val)
> -  (match val
> -    (() #t)
> -    ((((? symbol?) . (? string?)) . val) (free-form-args? val))
> -    (_ #f)))
> +(define list-of-string?
> +  (list-of string?))
>  
> -(define* (mpd-serialize-field field-name value #:optional (indent-
> level 0))
> -  #~(begin
> -      (use-modules ((ice-9 format)))
> -      (format #f "~v/~a \"~a\"~%" #$indent-level #$(if (string?
> field-name)
> -                                                       field-name
> -                                                       (uglify-
> field-name field-name)) #$value)))
> +(define list-of-symbol?
> +  (list-of symbol?))
>  
> -(define* (mpd-serialize-free-form-args field-name value #:optional
> (indent-level 0))
> -  (generic-serialize-alist string-append (cut mpd-serialize-field <>
> <> indent-level) value))
> +(define (mpd-serialize-field field-name value)
> +  (let ((field (if (string? field-name) field-name
> +                   (uglify-field-name field-name)))
> +        (value (if (boolean? value) (if value "yes" "no") value)))
> +    #~(format #f "~a \"~a\"~%" #$field #$value)))
>  
>  (define mpd-serialize-number mpd-serialize-field)
>  
>  (define mpd-serialize-string mpd-serialize-field)
>  
> -(define* (mpd-serialize-boolean field-name value #:optional (indent-
> level 0))
> -  (mpd-serialize-field field-name (if value "yes" "no") indent-
> level))
> +(define mpd-serialize-boolean mpd-serialize-field)
>  
> -(define (mpd-serialize-list-of-mpd-output field-name value)
> -  #~(string-append "\naudio_output {\n"
> -                   #$@(map (cut serialize-configuration <>
> -                                mpd-output-fields)
> -                           value)
> -                   "}\n"))
> +(define (mpd-serialize-alist field-name value)
> +  #~(string-append #$@(generic-serialize-alist list
> +                                               mpd-serialize-field
> +                                               value)))
>  
> -(define (mpd-serialize-configuration configuration)
> -  (mixed-text-file
> -   "mpd.conf"
> -   (serialize-configuration configuration mpd-configuration-
> fields)))
> +(define-maybe string (prefix mpd-))
> +(define-maybe list-of-string (prefix mpd-))
> +(define-maybe boolean (prefix mpd-))
> +
> +;;; TODO: Procedures for deprecated fields, to be removed.
> +
> +(define mpd-deprecated-fields '((music-dir . music-directory)
> +                                (playlist-dir . playlist-directory)
> +                                (address . endpoints)))
> +
> +(define (port? value) (or (string? value) (integer? value)))
> +
> +(define (mpd-serialize-deprecated-field field-name value)
> +  (if (maybe-value-set? value)
> +      (begin
> +        (warn-about-deprecation
> +         field-name #f
> +         #:replacement (assoc-ref mpd-deprecated-fields field-name))
> +        (match field-name
> +          ('playlist-dir (mpd-serialize-string "playlist_directory"
> value))
> +          ('music-dir (mpd-serialize-string "music_directory"
> value))
> +          ('address (mpd-serialize-string "bind_to_address"
> value))))
> +      ""))
> +
> +(define (mpd-serialize-port field-name value)
> +  (when (string? value)
> +    (warning
> +     (G_ "string value for '~a' is deprecated, use integer
> instead~%")
> +     field-name))
> +  (mpd-serialize-field "port" value))
> +
> +(define-maybe port (prefix mpd-))
> +
> +;;;
> +
> +;; Generic MPD plugin record, lists only the most prevalent fields.
> +(define-configuration mpd-plugin
> +  (plugin
> +   maybe-string
> +   "Plugin name.")
> +
> +  (name
> +   maybe-string
> +   "Name.")
> +
> +  (enabled?
> +   maybe-boolean
> +   "Whether the plugin is enabled/disabled.")
> +
> +  (extra-options
> +   (alist '())
> +   "An association list of option symbols/strings to string values
> +to be appended to the plugin configuration. See
> +@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin
> reference}
> +for available options.")
> +
> +  (prefix mpd-))
> +
> +(define (mpd-serialize-mpd-plugin field-name value)
> +  #~(format #f "~a {~%~a}~%"
> +            '#$field-name
> +            #$(serialize-configuration value mpd-plugin-fields)))
> +
> +(define (mpd-serialize-list-of-mpd-plugin field-name value)
> +  #~(string-append #$@(map (cut mpd-serialize-mpd-plugin field-name
> <>)
> +                           value)))
>  
> -(define mpd-subsystem-serialize-field (cut mpd-serialize-field <> <>
> 1))
> -(define mpd-subsystem-serialize-string (cut mpd-serialize-string <>
> <> 1))
> -(define mpd-subsystem-serialize-number (cut mpd-serialize-number <>
> <> 1))
> -(define mpd-subsystem-serialize-boolean (cut mpd-serialize-boolean
> <> <> 1))
> -(define mpd-subsystem-serialize-free-form-args (cut mpd-serialize-
> free-form-args <> <> 1))
> +(define list-of-mpd-plugin? (list-of mpd-plugin?))
> +
> +(define-maybe mpd-plugin (prefix mpd-))
> +
> +(define-configuration mpd-partition
> +  (name
> +   string
> +   "Partition name.")
> +
> +  (extra-options
> +   (alist '())
> +   "An association list of option symbols/strings to string values
> +to be appended to the partition configuration. See
> +@uref{
> https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions
> ,Configuring Partitions}
> +for available options.")
> +
> +  (prefix mpd-))
> +
> +(define (mpd-serialize-mpd-partition field-name value)
> +  #~(format #f "partition {~%~a}~%"
> +            #$(serialize-configuration value mpd-partition-fields)))
> +
> +(define (mpd-serialize-list-of-mpd-partition field-name value)
> +  #~(string-append #$@(map (cut mpd-serialize-mpd-partition #f <>)
> value)))
> +
> +(define list-of-mpd
This message was truncated. Download the full message here.
M
[PATCH v5.2] services: mpd: Refactor MPD service.
(address . 59866@debbugs.gnu.org)(name . Bruno Victal)(address . mirai@makinata.eu)
adb84c94a7eae70b53b384bfe5f251a238324a62.1671907977.git.mirai@makinata.eu
From: Bruno Victal <mirai@makinata.eu>

Refactor mpd-service-type to support additional mpd.conf
directives.

* gnu/services/audio.scm
(mpd-plugin, mpd-partition): New record.

(mpd-plugin?, mpd-partition?, list-of-string?, list-of-symbol?)
(list-of-mpd-plugin?, list-of-mpd-partition?)
(list-of-mpd-plugin-or-output?, port?): New predicate.

(mpd-serialize-field): Handle multiple types.

(mpd-configuration)
[package, group, shepherd-requirement, log-file, log-level, music-directory]
[playlist-directory, endpoints, database, partitions, neighbors, inputs]
[archive-plugins, input-cache-size, decoders, resampler, filters]
[playlist-plugins, extra-options]: New field.
[music-dir, playlist-dir, address]: Deprecate shorthand field.
[db-file, state-file, sticker-file, port, outputs]: Change admissible type.

(mpd-log-rotation): New procedure.

(mpd-shepherd-service)
[actions]: New shepherd actions: 'reopen' and 'configuration'.
[requirement]: Splice with 'shepherd-requirement' field.
[start]: Use 'package' field. Remove #:log-file parameter.

(mpd-service-activation): Create logging directory. Handle migration
from old-style configuration.

(mpd-accounts): Do not hardcode user and group names.

(mpd-service-type): Extend rottlog-service-type for log-rotation.
Update activation-service-type extension to reflect mpd-accounts
procedure change.

* doc/guix.texi (Audio Services)[Music Player Daemon]: Update doc.
---

Changes since v5.1:
* Rewrite commit message.
* Create group and user, by default mpd:mpd.
* Reword endpoints doc.

doc/guix.texi | 177 +++++++++++++---
gnu/services/audio.scm | 467 +++++++++++++++++++++++++++++++----------
2 files changed, 510 insertions(+), 134 deletions(-)

Toggle diff (490 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index e25692fd27..870efdbe2d 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -109,6 +109,7 @@ Copyright @copyright{} 2022 Reily Siegel@*
Copyright @copyright{} 2022 Simon Streit@*
Copyright @copyright{} 2022 (@*
Copyright @copyright{} 2022 John Kehayias@*
+Copyright @copyright{} 2022 Bruno Victal@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -33014,79 +33015,187 @@ The service type for @command{mpd}
Data type representing the configuration of @command{mpd}.
@table @asis
-@item @code{user} (default: @code{"mpd"})
+@item @code{package} (default: @code{mpd}) (type: file-like)
+The MPD package.
+
+@item @code{user} (default: @code{"mpd"}) (type: string)
The user to run mpd as.
-@item @code{music-dir} (default: @code{"~/Music"})
+@item @code{group} (default: @code{"mpd"}) (type: string)
+The group to run mpd as.
+
+@item @code{shepherd-requirement} (default: @code{()}) (type: list-of-symbol)
+This is a list of symbols naming Shepherd services that this service
+will depend on.
+
+@item @code{log-file} (default: @code{"/var/log/mpd/log"}) (type: maybe-string)
+The location of the log file. Set to @code{syslog} to use the local
+syslog daemon or @code{%unset-value} to omit this directive from the
+configuration file.
+
+@item @code{log-level} (type: maybe-string)
+Supress any messages below this threshold. Available values:
+@code{notice}, @code{info}, @code{verbose}, @code{warning} and
+@code{error}.
+
+@item @code{music-directory} (type: maybe-string)
The directory to scan for music files.
-@item @code{playlist-dir} (default: @code{"~/.mpd/playlists"})
+@item @code{playlist-directory} (type: maybe-string)
The directory to store playlists.
-@item @code{db-file} (default: @code{"~/.mpd/tag_cache"})
+@item @code{db-file} (type: maybe-string)
The location of the music database.
-@item @code{state-file} (default: @code{"~/.mpd/state"})
+@item @code{state-file} (type: maybe-string)
The location of the file that stores current MPD's state.
-@item @code{sticker-file} (default: @code{"~/.mpd/sticker.sql"})
+@item @code{sticker-file} (type: maybe-string)
The location of the sticker database.
-@item @code{port} (default: @code{"6600"})
-The port to run mpd on.
+@item @code{default-port} (default: @code{6600}) (type: maybe-integer)
+The default port to run mpd on.
+
+@item @code{endpoints} (type: maybe-list-of-string)
+The addresses that mpd will bind to. A port different from @var{default-port}
+may be specified, e.g. @code{localhost:6602} and IPv6 addresses must be
+enclosed in square brackets when a different port is used.
+To use a Unix domain socket, an absolute path or a path starting with @code{~}
+can be specified here.
+
+@item @code{database} (type: maybe-mpd-plugin)
+MPD database plugin configuration.
+
+@item @code{partitions} (default: @code{()}) (type: list-of-mpd-partition)
+List of MPD "partitions".
-@item @code{address} (default: @code{"any"})
-The address that mpd will bind to. To use a Unix domain socket,
-an absolute path can be specified here.
+@item @code{neighbors} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD neighbor plugin configurations.
-@item @code{outputs} (default: @code{"(list (mpd-output))"})
-The audio outputs that MPD can use. By default this is a single output using pulseaudio.
+@item @code{inputs} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD input plugin configurations.
+
+@item @code{archive-plugins} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD archive plugin configurations.
+
+@item @code{input-cache-size} (type: maybe-string)
+MPD input cache size.
+
+@item @code{decoders} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD decoder plugin configurations.
+
+@item @code{resampler} (type: maybe-mpd-plugin)
+MPD resampler plugin configuration.
+
+@item @code{filters} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD filter plugin configurations.
+
+@item @code{outputs} (type: list-of-mpd-plugin-or-output)
+The audio outputs that MPD can use. By default this is a single output
+using pulseaudio.
+
+@item @code{playlist-plugins} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD playlist plugin configurations.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the configuration.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-plugin
+Data type representing a @command{mpd} plugin.
+
+@table @asis
+@item @code{plugin} (type: maybe-string)
+Plugin name.
+
+@item @code{name} (type: maybe-string)
+Name.
+
+@item @code{enabled?} (type: maybe-boolean)
+Whether the plugin is enabled/disabled.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the plugin configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin
+reference} for available options.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-partition
+Data type representing a @command{mpd} partition.
+
+@table @asis
+@item @code{name} (type: string)
+Partition name.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the partition configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions,Configuring
+Partitions} for available options.
@end table
@end deftp
@deftp {Data Type} mpd-output
-Data type representing an @command{mpd} audio output.
+Data type representing a @command{mpd} audio output.
@table @asis
-@item @code{name} (default: @code{"MPD"})
+@item @code{name} (default: @code{"MPD"}) (type: string)
The name of the audio output.
-@item @code{type} (default: @code{"pulse"})
+@item @code{type} (default: @code{"pulse"}) (type: string)
The type of audio output.
-@item @code{enabled?} (default: @code{#t})
+@item @code{enabled?} (default: @code{#t}) (type: boolean)
Specifies whether this audio output is enabled when MPD is started. By
default, all audio outputs are enabled. This is just the default
setting when there is no state file; with a state file, the previous
state is restored.
-@item @code{tags?} (default: @code{#t})
+@item @code{format} (type: maybe-string)
+Force a specific audio format on output. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#audio-output-format,Global
+Audio Format} for a more detailed description.
+
+@item @code{tags?} (default: @code{#t}) (type: boolean)
If set to @code{#f}, then MPD will not send tags to this output. This
is only useful for output plugins that can receive tags, for example the
@code{httpd} output plugin.
-@item @code{always-on?} (default: @code{#f})
+@item @code{always-on?} (default: @code{#f}) (type: boolean)
If set to @code{#t}, then MPD attempts to keep this audio output always
-open. This may be useful for streaming servers, when you don’t want to
+open. This may be useful for streaming servers, when you don?t want to
disconnect all listeners even when playback is accidentally stopped.
-@item @code{mixer-type}
-This field accepts a symbol that specifies which mixer should be used
+@item @code{mixer-type} (default: @code{"none"}) (type: string)
+This field accepts a string that specifies which mixer should be used
for this audio output: the @code{hardware} mixer, the @code{software}
mixer, the @code{null} mixer (allows setting the volume, but with no
effect; this can be used as a trick to implement an external mixer
External Mixer) or no mixer (@code{none}).
-@item @code{extra-options} (default: @code{'()})
-An association list of option symbols to string values to be appended to
-the audio output configuration.
+@item @code{replay-gain-handler} (type: maybe-string)
+This field accepts a string that specifies how
+@uref{https://mpd.readthedocs.io/en/latest/user.html#replay-gain,Replay
+Gain} is to be applied. @code{software} uses an internal software
+volume control, @code{mixer} uses the configured (hardware) mixer
+control and @code{none} disables replay gain on this audio output.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the audio output configuration.
@end table
@end deftp
-The following example shows a configuration of @code{mpd} that provides
-an HTTP audio streaming output.
+The following example shows a configuration of @command{mpd} that
+configures some of its plugins and provides a HTTP audio streaming output.
@lisp
(service mpd-service-type
@@ -33098,7 +33207,19 @@ an HTTP audio streaming output.
(mixer-type 'null)
(extra-options
`((encoder . "vorbis")
- (port . "8080"))))))))
+ (port . "8080"))))))
+ (decoders
+ (list (mpd-plugin
+ (plugin "mikmod")
+ (enabled? #f))
+ (mpd-plugin
+ (plugin "openmpt")
+ (enabled? #t)
+ (extra-options `((repeat-count . -1)
+ (interpolation-filter . 1))))))
+ (resampler (mpd-plugin
+ (plugin "libsamplerate")
+ (extra-options `((type . 0)))))))
@end lisp
diff --git a/gnu/services/audio.scm b/gnu/services/audio.scm
index 1a1026f342..6caacf8c68 100644
--- a/gnu/services/audio.scm
+++ b/gnu/services/audio.scm
@@ -21,19 +21,27 @@
(define-module (gnu services audio)
#:use-module (guix gexp)
+ #:use-module (guix deprecation)
+ #:use-module (guix diagnostics)
+ #:use-module (guix i18n)
#:use-module (gnu services)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu services admin)
#:use-module (gnu system shadow)
#:use-module (gnu packages admin)
#:use-module (gnu packages mpd)
#:use-module (guix records)
#:use-module (ice-9 match)
- #:use-module (ice-9 format)
#:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-8)
#:use-module (srfi srfi-26)
#:export (mpd-output
mpd-output?
+ mpd-plugin
+ mpd-plugin?
+ mpd-partition
+ mpd-partition?
mpd-configuration
mpd-configuration?
mpd-service-type))
@@ -52,180 +60,425 @@ (define (uglify-field-name field-name)
#\-)
"_")))
-(define (free-form-args? val)
- (match val
- (() #t)
- ((((? symbol?) . (? string?)) . val) (free-form-args? val))
- (_ #f)))
+(define list-of-string?
+ (list-of string?))
-(define* (mpd-serialize-field field-name value #:optional (indent-level 0))
- #~(begin
- (use-modules ((ice-9 format)))
- (format #f "~v/~a \"~a\"~%" #$indent-level #$(if (string? field-name)
- field-name
- (uglify-field-name field-name)) #$value)))
+(define list-of-symbol?
+ (list-of symbol?))
-(define* (mpd-serialize-free-form-args field-name value #:optional (indent-level 0))
- (generic-serialize-alist string-append (cut mpd-serialize-field <> <> indent-level) value))
+(define (mpd-serialize-field field-name value)
+ (let ((field (if (string? field-name) field-name
+ (uglify-field-name field-name)))
+ (value (if (boolean? value) (if value "yes" "no") value)))
+ #~(format #f "~a \"~a\"~%" #$field #$value)))
(define mpd-serialize-number mpd-serialize-field)
(define mpd-serialize-string mpd-serialize-field)
-(define* (mpd-serialize-boolean field-name value #:optional (indent-level 0))
- (mpd-serialize-field field-name (if value "yes" "no") indent-level))
+(define mpd-serialize-boolean mpd-serialize-field)
-(define (mpd-serialize-list-of-mpd-output field-name value)
- #~(string-append "\naudio_output {\n"
- #$@(map (cut serialize-configuration <>
- mpd-output-fields)
- value)
- "}\n"))
+(define (mpd-serialize-alist field-name value)
+ #~(string-append #$@(generic-serialize-alist list
+ mpd-serialize-field
+ value)))
-(define (mpd-serialize-configuration configuration)
- (mixed-text-file
- "mpd.conf"
- (serialize-configuration configuration mpd-configuration-fields)))
+(define-maybe string (prefix mpd-))
+(define-maybe list-of-string (prefix mpd-))
+(define-maybe boolean (prefix mpd-))
+
+;;; TODO: Procedures for deprecated fields, to be removed.
+
+(define mpd-deprecated-fields '((music-dir . music-directory)
+ (playlist-dir . playlist-directory)
+ (address . endpoints)))
+
+(define (port? value) (or (string? value) (integer? value)))
+
+(define (mpd-serialize-deprecated-field field-name value)
+ (if (maybe-value-set? value)
+ (begin
+ (warn-about-deprecation
+ field-name #f
+ #:replacement (assoc-ref mpd-deprecated-fields field-name))
+ (match field-name
+ ('playlist-dir (mpd-serialize-string "playlist_directory" value))
+ ('music-dir (mpd-serialize-string "music_directory" value))
+ ('address (mpd-serialize-string "bind_to_address" value))))
+ ""))
+
+(define (mpd-serialize-port field-name value)
+ (when (string? value)
+ (warning
+ (G_ "string value for '~a' is deprecated, use integer instead~%")
+ field-name))
+ (mpd-serialize-field "port" value))
+
+(define-maybe port (prefix mpd-))
+
+;;;
+
+;; Generic MPD plugin record, lists only the most prevalent fields.
+(define-configuration mpd-plugin
+ (plugin
+ maybe-string
+ "Plugin name.")
+
+ (name
+ maybe-string
+ "Name.")
+
+ (enabled?
+ maybe-boolean
+ "Whether the plugin is enabled/disabled.")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols/strings to string values
+to be appended to the plugin configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin reference}
+for available options.")
+
+ (prefix mpd-))
+
+(define (mpd-serialize-mpd-plugin field-name value)
+ #~(format #f "~a {~%~a}~%"
+ '#$field-name
+ #$(serialize-configuration value mpd-plugin-fields)))
+
+(define (mpd-serialize-list-of-mpd-plugin field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-plugin field-name <>)
+ value)))
-(define mpd-subsystem-serialize-field (cut mpd-serialize-field <> <> 1))
-(define mpd-subsystem-serialize-string (cut mpd-serialize-string <> <> 1))
-(define mpd-subsystem-serialize-number (cut mpd-serialize-number <> <> 1))
-(define mpd-subsystem-serialize-boolean (cut mpd-serialize-boolean <> <> 1))
-(define mpd-subsystem-serialize-free-form-args (cut mpd-serialize-free-form-args <> <> 1))
+(define list-of-mpd-plugin? (list-of mpd-plugin?))
+
+(define-maybe mpd-plugin (prefix mpd-))
+
+(define-configuration mpd-partition
+ (name
+ string
+ "Partition name.")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols/strings to string values
+to be appended to the partition configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions,Configuring Partitions}
+for available options.")
+
+ (prefix mpd-))
+
+(define (mpd-serialize-mpd-partition field-name value)
+ #~(format #f "partition {~%~a}~%"
+ #$(serialize-configuration value mpd-partition-fields)))
+
+(define (mpd-serialize-list-of-mpd-partition field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-partition #f <>) value)))
+
+(define list-of-mpd-partition?
+ (list-of mpd-partition?))
(define-configuration mpd-output
(name
(string "MPD")
"The name of the audio output.")
+
(type
(string "pulse")
"The type of audio output.")
+
(enabled?
(boolean #t)
"Specifies whether this audio output is enabled when MPD is started. By
default, all audio outputs are enabled. This is just the default
setting when there is no state file; with a state file, the previous
state is restored.")
+
+ (format
+ maybe-string
+ "Force a specific audio format on output. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#audio-output-format,Global Audio Format}
+for a more detailed description.")
+
(tags?
(boolean #t)
"If set to @code{#f}, then MPD will not send tags to this output. This
is only useful for output plugins that can receive tags, for example the
@code{httpd} output plugin.")
+
(always-on?
(boolean #f)
"If set to @code{#t}, then MPD attempts to keep this audio output always
open. This may be useful for streaming servers, when you don’t want to
disconnect all listeners even when playback is accidentally stopped.")
+
(mixer-type
(string "none")
- "This field accepts a symbol that specifies which mixer should be used
+ "This field accepts a string that specifies which mixer should be used
for this audio output: the @code{hardware} mixer, the @code{software}
mixer, the @code{null} mixer (allows setting the volume, but with no
effect; this can be used as a trick to implement an external mixer
External Mixer) or no mixer (@code{none}).")
+
+ (replay-gain-handler
+ maybe-string
+ "This field accepts a string that specifies how
+@uref{https://mpd.readthedocs.io/en/latest/user.html#replay-gain,Replay Gain}
+is to be applied. @code{software} uses an internal software volume control,
+@code{mixer} uses the configured (hardware) mixer control and @code{none}
+disables replay gain on this audio output.")
+
(extra-options
- (free-form-args '())
- "An association list of option symbols to string values to be appended to
-the audio output configuration.")
- (prefix mpd-subsystem-))
+ (ali
This message was truncated. Download the full message here.
M
Re: [PATCH v5.1] services: mpd: Refactor MPD service.
9d585ea3-2cab-6aee-16ee-672e4cf1cf81@makinata.eu
On 2022-12-24 17:20, Liliana Marie Prikler wrote:
Toggle quote (32 lines)
>> -(define %mpd-accounts
>> -  ;; Default account and group for MPD.
>> -  (list (user-group (name "mpd") (system? #t))
>> -        (user-account
>> -         (name "mpd")
>> -         (group "mpd")
>> -         (system? #t)
>> -         (comment "Music Player Daemon (MPD) user")
>>  
>> -         ;; Note: /var/run/mpd hosts one sub-directory per user, of
>> which
>> -         ;; /var/run/mpd/mpd corresponds to the "mpd" user.
>> -         (home-directory "/var/run/mpd/mpd")
>> +        ;; TODO: remove me, migrates from the old location at
>> /var/run/mpd
>> +        ;;       to the new one at /var/lib/mpd.
>> +        (let* ((user (getpw #$user))
>> +               (deprecated-directory (string-append "/var/run/mpd/"
>> +                                                    #$user "/.mpd"))
>> +               (new-directory (string-append (passwd:dir user)
>> +                                             "/.config/mpd")))
>> +          (when (and (file-exists? deprecated-directory)
>> +                     (not (file-exists? new-directory)))
>> +            (rename-file deprecated-directory new-directory)
>> +            (chown new-directory (passwd:uid user) (passwd:gid
>> user))))
>> +        (mkdir-p (dirname #$log-file)))))
> I'm not sure whether we should migrate the logs here. I do think the
> logs should be stored in /var/log rather than /var/run, but other than
> that I'm not even sure there's much value in changing the /var/run/mpd
> structure. What's your use case for doing so?

I agree with you that they belong to /var/log, though I prefer leaving this to
MPDs default behavior when the directives are not explicitly set.
IIRC MPD will store them under $XDG_CONFIG_HOME/log if the "log_file" directive
is absent.

They were previously stored under /var/run because it was set by default to store
under ~/.mpd and the user account had its $HOME set as "/var/run/mpd/mpd".
(Looking closer to the old behavior, this also meant that the user field was actually
ignored as the user-group/user-account was hardcoded).
Since /var/run/mpd(/mpd) is the $HOME directory in the old style configuration, it
also contains other files of interest, as most of the fields default values are
directives relative to $HOME.

Personally I've no use to this migration part and I'd prefer an outright breaking
change to the new style, most of the MPD generated files can be regenerated without much hardship
and this is introducing complexity and soon™ to be obsolete code.

Cheers
M
(no subject)
(name . control)(address . control@debbugs.gnu.org)
135bf584-4149-9da1-2188-1980f489e45d@makinata.eu
# remove WIP tag.
retitle 59866 [PATCH 0/2] services: mpd: Refactor MPD service.
quit
M
Pulseaudio woes
(name . 59866)(address . 59866@debbugs.gnu.org)(name . Liliana Marie Prikler)(address . liliana.prikler@gmail.com)
b381d59b-2278-1a6b-5f93-7b008504d544@makinata.eu
I've found out that this service (even before this patch-set)
is biased towards being used as a home service rather than a system service.

The reason is that it sets the XDG_RUNTIME_DIR environment variable through shepherd
to:

Toggle snippet (5 lines)
(list (string-append
"XDG_RUNTIME_DIR=/run/user/"
(number->string (passwd:uid (getpwnam #$user)))))

This directory does not exist if this is launched as a system-wide service.
I presume that this went unnoticed because most of the times you'd want to
use this service system-wide you also configure this either as a MPD
'satellite instance' or the audio-outputs are always network-streaming ones.

This falls apart if you configure a system-wide mpd-service with a pulseaudio
output as it will try to access XDG_RUNTIME_DIR which is not created for the
system? #t 'mpd' user.

Now, pulseaudio is usually launched as a 'per-user' daemon although it can
be used in a system-pulse configuration though this is strongly discouraged.
Under most conditions (pulseaudio config mostly unchanged),
a system-wide mpd-service-type is also able to launch its own pulse instance
but due to the inherent assumptions present in the original service we have that:
- The XDG_RUNTIME_DIR env var is set when it should only be set for 'system? #f' users.
- PULSE_CLIENTCONFIG and PULSE_CONFIG are not set by shepherd.
These usually correspond to:
PULSE_CONFIG =/etc/pulse/client.conf
PULSE_CONFIG=/etc/pulse/daemon.conf
But if you use mpd-service-type as a home service, you could also have
these set to a user-specific pulse config, which will be overridden by shepherd
if these are set in mpd-service-type.
But again, if you don't set these env vars and use it as a systemwide service
with a pulseaudio output and you workaround the XDG_RUNTIME_DIR by unsetting it,
your pulse daemon won't read its config and there will be no output.

This is not just a theoretical issue, consider this snippet:

Toggle snippet (17 lines)
;; Set a systemwide mpd service that streams with pulseaudio RTP output.

(service pulseaudio-service-type (pulseaudio-configuration
(extra-script-files
(list
(plain-file
"rtp.pa"
(string-join (list "load-module module-null-sink sink_name=rtp"
"load-module module-rtp-send source=rtp.monitor"
"set-default-sink rtp") "\n" 'suffix))))))
(service mpd-service-type
(mpd-configuration
(outputs (list (mpd-output (name "Pulseaudio over RTP")
(type "pulse")
(extra-options '((sink . "rtp")))))))

The problem here is how to conditionally select which environment variables should be set based
on whether this is being launched as a system-wide or home-instance service. I'm all ears for
suggestions.


Regarding the current patch-set, I'm thinking that some of the non-public procedures could
be defined with 'define-deprecated' instead which should (?) be cleaner than the current
comments that are demarcating deprecated code.

Regards,
Bruno
L
L
Liliana Marie Prikler wrote on 3 Jan 20:38 +0100
8af12f33afa0ef406711e8ed144d49b3e67935c3.camel@gmail.com
Am Dienstag, dem 03.01.2023 um 14:43 +0000 schrieb mirai:
Toggle quote (24 lines)
> I've found out that this service (even before this patch-set)
> is biased towards being used as a home service rather than a system
> service.
>
> The reason is that it sets the XDG_RUNTIME_DIR environment variable
> through shepherd
> to:
>
> --8<---------------cut here---------------start------------->8---
> (list (string-append
>         "XDG_RUNTIME_DIR=/run/user/"
>         (number->string (passwd:uid (getpwnam #$user)))))
> --8<---------------cut here---------------end--------------->8---
>
> This directory does not exist if this is launched as a system-wide
> service.
> I presume that this went unnoticed because most of the times you'd
> want to use this service system-wide you also configure this either
> as a MPD 'satellite instance' or the audio-outputs are always
> network-streaming ones.
>
> This falls apart if you configure a system-wide mpd-service with a
> pulseaudio output as it will try to access XDG_RUNTIME_DIR which is
> not created for the system? #t 'mpd' user.
As far as I'm aware, you can instantiate the MPD service system-wide
while also pointing it to a non-system user. Admittedly, this is a
somewhat degenerate use case, but that's how things were handled before
home services were a thing, and there might still be valid reasons to
support it.

Toggle quote (3 lines)
> Now, pulseaudio is usually launched as a 'per-user' daemon although
> it can be used in a system-pulse configuration though this is
> strongly discouraged.
If it is, then XDG_RUNTIME_DIR doesn't matter.

Toggle quote (21 lines)
> Under most conditions (pulseaudio config mostly unchanged),
> a system-wide mpd-service-type is also able to launch its own pulse
> instance but due to the inherent assumptions present in the original
> service we have that:
>   - The XDG_RUNTIME_DIR env var is set when it should only be set for
> 'system? #f' users.
>   - PULSE_CLIENTCONFIG and PULSE_CONFIG are not set by shepherd.
>         These usually correspond to:
>           PULSE_CONFIG =/etc/pulse/client.conf
>           PULSE_CONFIG=/etc/pulse/daemon.conf
>         But if you use mpd-service-type as a home service, you could
> also have
>       these set to a user-specific pulse config, which will be
> overridden by shepherd
>       if these are set in mpd-service-type.
>         But again, if you don't set these env vars and use it as a
> systemwide service
>       with a pulseaudio output and you workaround the XDG_RUNTIME_DIR
> by unsetting it,
>       your pulse daemon won't read its config and there will be no
> output.
何?

Toggle quote (30 lines)
> This is not just a theoretical issue, consider this snippet:
>
> --8<---------------cut here---------------start------------->8---
> ;; Set a systemwide mpd service that streams with pulseaudio RTP
> output.
>
> (service pulseaudio-service-type (pulseaudio-configuration
>                                     (extra-script-files
>                                        (list
>                                          (plain-file
>                                            "rtp.pa"
>                                            (string-join (list "load-
> module module-null-sink sink_name=rtp"
>                                                               "load-
> module module-rtp-send source=rtp.monitor"
>                                                               "set-
> default-sink rtp") "\n" 'suffix))))))
> (service mpd-service-type
>                    (mpd-configuration
>                     (outputs (list (mpd-output (name "Pulseaudio over
> RTP")
>                                      (type "pulse")
>                                      (extra-options '((sink .
> "rtp")))))))
> --8<---------------cut here---------------end--------------->8---
>
> The problem here is how to conditionally select which environment
> variables should be set based on whether this is being launched as a
> system-wide or home-instance service. I'm all ears for
> suggestions.
I don't think you need to think about the home service too hard here.
If you can't get the two use cases to work together, you can define a
home-mpd-service-type which takes a regular mpd-configuration or a
particularly crafted home-mpd-configuration, whichever makes more
sense. Not saying that this would be a good design idea, just that the
option exists.

I do think trying to figure out what you're going to do with your
environment variables is a more worthwhile exercise at the moment.

Toggle quote (4 lines)
> Regarding the current patch-set, I'm thinking that some of the non-
> public procedures could be defined with 'define-deprecated' instead
> which should (?) be cleaner than the current
> comments that are demarcating deprecated code.
If they aren't public, what is the point of define-deprecated? You
will only trip over your own uses.

Cheers
B
B
Bruno Victal wrote 7 days ago
[PATCH v6 1/3] services: mpd: Rewrite using 'define-configuration'.
(address . 59866@debbugs.gnu.org)
23b0f83d1fd58fce9262d0b281b34e9678e21471.1675367583.git.mirai@makinata.eu
* gnu/services/audio.scm
(mpd-configuration, mpd-output): Rewrite using define-configuration.
(uglify-field-name, mpd-serialize-field, mpd-serialize-alist)
(mpd-serialize-number, mpd-serialize-boolean, mpd-serialize-list-of-mpd-output)
(mpd-serialize-configuration): New procedure.
(list-of-mpd-output?): New predicate.
(mpd-config->file, mpd-output->string): Remove procedure.
---

Notable changes since v5:
* Completely eliminate indented procedures from patchset.

gnu/services/audio.scm | 222 ++++++++++++++++++++++++-----------------
1 file changed, 133 insertions(+), 89 deletions(-)

Toggle diff (262 lines)
diff --git a/gnu/services/audio.scm b/gnu/services/audio.scm
index c60053f33c..b7cb0ebe38 100644
--- a/gnu/services/audio.scm
+++ b/gnu/services/audio.scm
@@ -2,6 +2,7 @@
;;; Copyright © 2017 Peter Mikkelsen <petermikkelsen10@gmail.com>
;;; Copyright © 2019 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2022 Bruno Victal <mirai@makinata.eu>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -21,13 +22,15 @@
(define-module (gnu services audio)
#:use-module (guix gexp)
#:use-module (gnu services)
+ #:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
#:use-module (gnu system shadow)
#:use-module (gnu packages admin)
#:use-module (gnu packages mpd)
#:use-module (guix records)
#:use-module (ice-9 match)
- #:use-module (ice-9 format)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-26)
#:export (mpd-output
mpd-output?
mpd-configuration
@@ -40,93 +43,134 @@ (define-module (gnu services audio)
;;;
;;; Code:
-(define-record-type* <mpd-output>
- mpd-output make-mpd-output
- mpd-output?
- (type mpd-output-type
- (default "pulse"))
- (name mpd-output-name
- (default "MPD"))
- (enabled? mpd-output-enabled?
- (default #t))
- (tags? mpd-output-tags?
- (default #t))
- (always-on? mpd-output-always-on?
- (default #f))
- (mixer-type mpd-output-mixer-type
- ;; valid: hardware, software, null, none
- (default #f))
- (extra-options mpd-output-extra-options
- (default '())))
-
-(define-record-type* <mpd-configuration>
- mpd-configuration make-mpd-configuration
- mpd-configuration?
- (user mpd-configuration-user
- (default "mpd"))
- (music-dir mpd-configuration-music-dir
- (default "~/Music"))
- (playlist-dir mpd-configuration-playlist-dir
- (default "~/.mpd/playlists"))
- (db-file mpd-configuration-db-file
- (default "~/.mpd/tag_cache"))
- (state-file mpd-configuration-state-file
- (default "~/.mpd/state"))
- (sticker-file mpd-configuration-sticker-file
- (default "~/.mpd/sticker.sql"))
- (port mpd-configuration-port
- (default "6600"))
- (address mpd-configuration-address
- (default "any"))
- (outputs mpd-configuration-outputs
- (default (list (mpd-output)))))
-
-(define (mpd-output->string output)
- "Convert the OUTPUT of type <mpd-output> to a configuration file snippet."
- (let ((extra (string-join
- (map (match-lambda
- ((key . value)
- (format #f " ~a \"~a\""
- (string-map
- (lambda (c) (if (char=? c #\-) #\_ c))
- (symbol->string key))
- value)))
- (mpd-output-extra-options output))
- "\n")))
- (format #f "\
-audio_output {
- type \"~a\"
- name \"~a\"
-~:[ enabled \"no\"~%~;~]\
-~:[ tags \"no\"~%~;~]\
-~:[~; always_on \"yes\"~%~]\
-~@[ mixer_type \"~a\"~%~]\
-~a~%}~%"
- (mpd-output-type output)
- (mpd-output-name output)
- (mpd-output-enabled? output)
- (mpd-output-tags? output)
- (mpd-output-always-on? output)
- (mpd-output-mixer-type output)
- extra)))
-
-(define (mpd-config->file config)
- (apply
- mixed-text-file "mpd.conf"
- "pid_file \"" (mpd-file-name config "pid") "\"\n"
- (append (map mpd-output->string
- (mpd-configuration-outputs config))
- (map (match-lambda
- ((config-name config-val)
- (string-append config-name " \"" (config-val config) "\"\n")))
- `(("user" ,mpd-configuration-user)
- ("music_directory" ,mpd-configuration-music-dir)
- ("playlist_directory" ,mpd-configuration-playlist-dir)
- ("db_file" ,mpd-configuration-db-file)
- ("state_file" ,mpd-configuration-state-file)
- ("sticker_file" ,mpd-configuration-sticker-file)
- ("port" ,mpd-configuration-port)
- ("bind_to_address" ,mpd-configuration-address))))))
+(define (uglify-field-name field-name)
+ (let ((str (symbol->string field-name)))
+ (string-join (string-split (if (string-suffix? "?" str)
+ (string-drop-right str 1)
+ str)
+ #\-) "_")))
+
+(define (mpd-serialize-field field-name value)
+ #~(format #f "~a ~s~%" #$(if (string? field-name)
+ field-name
+ (uglify-field-name field-name))
+ #$(if (string? value)
+ value
+ (object->string value))))
+
+(define (mpd-serialize-alist field-name value)
+ #~(string-append #$@(generic-serialize-alist list mpd-serialize-field
+ value)))
+
+(define mpd-serialize-string mpd-serialize-field)
+
+(define (mpd-serialize-boolean field-name value)
+ (mpd-serialize-field field-name (if value "yes" "no")))
+
+(define (mpd-serialize-list-of-mpd-output field-name value)
+ #~(string-append "\naudio_output {\n"
+ #$@(map (cut serialize-configuration <>
+ mpd-output-fields)
+ value)
+ "}\n"))
+
+(define (mpd-serialize-configuration configuration)
+ (mixed-text-file
+ "mpd.conf"
+ (serialize-configuration configuration mpd-configuration-fields)))
+
+(define-configuration mpd-output
+ (name
+ (string "MPD")
+ "The name of the audio output.")
+
+ (type
+ (string "pulse")
+ "The type of audio output.")
+
+ (enabled?
+ (boolean #t)
+ "Specifies whether this audio output is enabled when MPD is started. By
+default, all audio outputs are enabled. This is just the default
+setting when there is no state file; with a state file, the previous
+state is restored.")
+
+ (tags?
+ (boolean #t)
+ "If set to @code{#f}, then MPD will not send tags to this output. This
+is only useful for output plugins that can receive tags, for example the
+@code{httpd} output plugin.")
+
+ (always-on?
+ (boolean #f)
+ "If set to @code{#t}, then MPD attempts to keep this audio output always
+open. This may be useful for streaming servers, when you don’t want to
+disconnect all listeners even when playback is accidentally stopped.")
+
+ (mixer-type
+ (string "none")
+ "This field accepts a symbol that specifies which mixer should be used
+for this audio output: the @code{hardware} mixer, the @code{software}
+mixer, the @code{null} mixer (allows setting the volume, but with no
+effect; this can be used as a trick to implement an external mixer
+External Mixer) or no mixer (@code{none}).")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols to string values to be appended to
+the audio output configuration.")
+
+ (prefix mpd-))
+
+(define list-of-mpd-output?
+ (list-of mpd-output?))
+
+(define-configuration mpd-configuration
+ (user
+ (string "mpd")
+ "The user to run mpd as.")
+
+ (music-dir
+ (string "~/Music")
+ "The directory to scan for music files."
+ (lambda (_ x)
+ (mpd-serialize-field "music_directory" x)))
+
+ (playlist-dir
+ (string "~/.mpd/playlists")
+ "The directory to store playlists."
+ (lambda (_ x)
+ (mpd-serialize-field "playlist_directory" x)))
+
+ (db-file
+ (string "~/.mpd/tag_cache")
+ "The location of the music database.")
+
+ (state-file
+ (string "~/.mpd/state")
+ "The location of the file that stores current MPD's state.")
+
+ (sticker-file
+ (string "~/.mpd/sticker.sql")
+ "The location of the sticker database.")
+
+ (port
+ (string "6600")
+ "The port to run mpd on.")
+
+ (address
+ (string "any")
+ "The address that mpd will bind to.
+To use a Unix domain socket, an absolute path can be specified here."
+ (lambda (_ x)
+ (mpd-serialize-field "bind_to_address" x)))
+
+ (outputs
+ (list-of-mpd-output (list (mpd-output)))
+ "The audio outputs that MPD can use.
+By default this is a single output using pulseaudio.")
+
+ (prefix mpd-))
(define (mpd-file-name config file)
"Return a path in /var/run/mpd/ that is writable
@@ -143,7 +187,7 @@ (define (mpd-shepherd-service config)
(start #~(make-forkexec-constructor
(list #$(file-append mpd "/bin/mpd")
"--no-daemon"
- #$(mpd-config->file config))
+ #$(mpd-serialize-configuration config))
#:environment-variables
;; Required to detect PulseAudio when run under a user account.
(list (string-append
--
2.38.1
B
B
Bruno Victal wrote 7 days ago
[PATCH v6 2/3] services: mpd: Refactor MPD service.
(address . 59866@debbugs.gnu.org)
fe3fc24ea98bf66c95d0c589064a5c2edd7218bd.1675367583.git.mirai@makinata.eu
Refactor mpd-service-type to support additional mpd.conf directives
and move activation-service-extension into service constructor.

* gnu/services/audio.scm
(mpd-plugin, mpd-partition): New record.

(mpd-serialize-boolean, mpd-serialize-field): Integrate serializers
into a single procedure for alist interop.

(mpd-plugin?, mpd-partition?, list-of-string?, list-of-symbol?)
(list-of-mpd-plugin?, list-of-mpd-partition?)
(list-of-mpd-plugin-or-output?, port?): New predicate.

(mpd-file-name, mpd-service-activation): Remove procedure.

(mpd-configuration)
[package, group, shepherd-requirement, log-file, log-level, music-directory]
[playlist-directory, endpoints, database, partitions, neighbors, inputs]
[archive-plugins, input-cache-size, decoders, resampler, filters]
[playlist-plugins, extra-options]: New field.
[music-dir, playlist-dir, address]: Deprecate shorthand field.
[db-file, state-file, sticker-file, port, outputs]: Change admissible type.

(mpd-shepherd-service)
[actions]: New shepherd actions: 'reopen' and 'configuration'.
[requirement]: Splice with 'shepherd-requirement' field.
[start]: Use 'package' field. Remove #:log-file parameter.
Move activation-service extension into constructor.

(mpd-accounts): Honor user and group names from configuration.
(mpd-log-rotation): New procedure.
(mpd-service-type)[extensions]: Add rottlog-service-type extension.
Remove activation-service-type extension.

(mpd-output-name, mpd-output-type, mpd-output-enabled?, mpd-output-format)
(mpd-output-tags?, mpd-output-always-on?, mpd-output-mixer-type)
(mpd-output-replay-gain-handler, mpd-output-extra-options)
(mpd-plugin-plugin, mpd-plugin-name, mpd-plugin-enabled?)
(mpd-plugin-extra-options)
(mpd-partition-name, mpd-partition-extra-options)
(mpd-configuration-package, mpd-configuration-user)
(mpd-configuration-group, mpd-configuration-shepherd-requirement)
(mpd-configuration-log-file, mpd-configuration-log-level)
(mpd-configuration-music-directory, mpd-configuration-music-dir)
(mpd-configuration-playlist-directory, mpd-configuration-playlist-dir)
(mpd-configuration-db-file, mpd-configuration-state-file)
(mpd-configuration-sticker-file, mpd-configuration-default-port)
(mpd-configuration-endpoints, mpd-configuration-address)
(mpd-configuration-database, mpd-configuration-partitions)
(mpd-configuration-neighbors, mpd-configuration-inputs)
(mpd-configuration-archive-plugins, mpd-configuration-input-cache-size)
(mpd-configuration-decoders, mpd-configuration-resampler)
(mpd-configuration-filters, mpd-configuration-outputs)
(mpd-configuration-playlist-plugins, mpd-configuration-extra-options): Export accessors.

* doc/guix.texi (Audio Services)[Music Player Daemon]: Update doc.
---


Notable changes since v5:
* Export accessors.
* Integrate activation-service-type extension into service constructor.
* Honor existing directories and permissions, only create when absent.

doc/guix.texi | 177 ++++++++++++---
gnu/services/audio.scm | 492 +++++++++++++++++++++++++++++++++--------
2 files changed, 543 insertions(+), 126 deletions(-)

Toggle diff (468 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 64873db00b..8e220e0631 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -109,6 +109,7 @@
Copyright @copyright{} 2022 Simon Streit@*
Copyright @copyright{} 2022 (@*
Copyright @copyright{} 2022 John Kehayias@*
+Copyright @copyright{} 2022 Bruno Victal@*
Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@*
Copyright @copyright{} 2023 Giacomo Leidi@*
Copyright @copyright{} 2022 Antero Mejr@*
@@ -33185,79 +33186,187 @@ Audio Services
Data type representing the configuration of @command{mpd}.
@table @asis
-@item @code{user} (default: @code{"mpd"})
+@item @code{package} (default: @code{mpd}) (type: file-like)
+The MPD package.
+
+@item @code{user} (default: @code{"mpd"}) (type: string)
The user to run mpd as.
-@item @code{music-dir} (default: @code{"~/Music"})
+@item @code{group} (default: @code{"mpd"}) (type: string)
+The group to run mpd as.
+
+@item @code{shepherd-requirement} (default: @code{()}) (type: list-of-symbol)
+This is a list of symbols naming Shepherd services that this service
+will depend on.
+
+@item @code{log-file} (default: @code{"/var/log/mpd/log"}) (type: maybe-string)
+The location of the log file. Set to @code{syslog} to use the local
+syslog daemon or @code{%unset-value} to omit this directive from the
+configuration file.
+
+@item @code{log-level} (type: maybe-string)
+Supress any messages below this threshold. Available values:
+@code{notice}, @code{info}, @code{verbose}, @code{warning} and
+@code{error}.
+
+@item @code{music-directory} (type: maybe-string)
The directory to scan for music files.
-@item @code{playlist-dir} (default: @code{"~/.mpd/playlists"})
+@item @code{playlist-directory} (type: maybe-string)
The directory to store playlists.
-@item @code{db-file} (default: @code{"~/.mpd/tag_cache"})
+@item @code{db-file} (type: maybe-string)
The location of the music database.
-@item @code{state-file} (default: @code{"~/.mpd/state"})
+@item @code{state-file} (type: maybe-string)
The location of the file that stores current MPD's state.
-@item @code{sticker-file} (default: @code{"~/.mpd/sticker.sql"})
+@item @code{sticker-file} (type: maybe-string)
The location of the sticker database.
-@item @code{port} (default: @code{"6600"})
-The port to run mpd on.
+@item @code{default-port} (default: @code{6600}) (type: maybe-integer)
+The default port to run mpd on.
+
+@item @code{endpoints} (type: maybe-list-of-string)
+The addresses that mpd will bind to. A port different from @var{default-port}
+may be specified, e.g. @code{localhost:6602} and IPv6 addresses must be
+enclosed in square brackets when a different port is used.
+To use a Unix domain socket, an absolute path or a path starting with @code{~}
+can be specified here.
+
+@item @code{database} (type: maybe-mpd-plugin)
+MPD database plugin configuration.
+
+@item @code{partitions} (default: @code{()}) (type: list-of-mpd-partition)
+List of MPD "partitions".
-@item @code{address} (default: @code{"any"})
-The address that mpd will bind to. To use a Unix domain socket,
-an absolute path can be specified here.
+@item @code{neighbors} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD neighbor plugin configurations.
-@item @code{outputs} (default: @code{"(list (mpd-output))"})
-The audio outputs that MPD can use. By default this is a single output using pulseaudio.
+@item @code{inputs} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD input plugin configurations.
+
+@item @code{archive-plugins} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD archive plugin configurations.
+
+@item @code{input-cache-size} (type: maybe-string)
+MPD input cache size.
+
+@item @code{decoders} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD decoder plugin configurations.
+
+@item @code{resampler} (type: maybe-mpd-plugin)
+MPD resampler plugin configuration.
+
+@item @code{filters} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD filter plugin configurations.
+
+@item @code{outputs} (type: list-of-mpd-plugin-or-output)
+The audio outputs that MPD can use. By default this is a single output
+using pulseaudio.
+
+@item @code{playlist-plugins} (default: @code{()}) (type: list-of-mpd-plugin)
+List of MPD playlist plugin configurations.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the configuration.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-plugin
+Data type representing a @command{mpd} plugin.
+
+@table @asis
+@item @code{plugin} (type: maybe-string)
+Plugin name.
+
+@item @code{name} (type: maybe-string)
+Name.
+
+@item @code{enabled?} (type: maybe-boolean)
+Whether the plugin is enabled/disabled.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the plugin configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin
+reference} for available options.
+
+@end table
+@end deftp
+
+@deftp {Data Type} mpd-partition
+Data type representing a @command{mpd} partition.
+
+@table @asis
+@item @code{name} (type: string)
+Partition name.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the partition configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions,Configuring
+Partitions} for available options.
@end table
@end deftp
@deftp {Data Type} mpd-output
-Data type representing an @command{mpd} audio output.
+Data type representing a @command{mpd} audio output.
@table @asis
-@item @code{name} (default: @code{"MPD"})
+@item @code{name} (default: @code{"MPD"}) (type: string)
The name of the audio output.
-@item @code{type} (default: @code{"pulse"})
+@item @code{type} (default: @code{"pulse"}) (type: string)
The type of audio output.
-@item @code{enabled?} (default: @code{#t})
+@item @code{enabled?} (default: @code{#t}) (type: boolean)
Specifies whether this audio output is enabled when MPD is started. By
default, all audio outputs are enabled. This is just the default
setting when there is no state file; with a state file, the previous
state is restored.
-@item @code{tags?} (default: @code{#t})
+@item @code{format} (type: maybe-string)
+Force a specific audio format on output. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#audio-output-format,Global
+Audio Format} for a more detailed description.
+
+@item @code{tags?} (default: @code{#t}) (type: boolean)
If set to @code{#f}, then MPD will not send tags to this output. This
is only useful for output plugins that can receive tags, for example the
@code{httpd} output plugin.
-@item @code{always-on?} (default: @code{#f})
+@item @code{always-on?} (default: @code{#f}) (type: boolean)
If set to @code{#t}, then MPD attempts to keep this audio output always
-open. This may be useful for streaming servers, when you don’t want to
+open. This may be useful for streaming servers, when you don?t want to
disconnect all listeners even when playback is accidentally stopped.
-@item @code{mixer-type}
-This field accepts a symbol that specifies which mixer should be used
+@item @code{mixer-type} (default: @code{"none"}) (type: string)
+This field accepts a string that specifies which mixer should be used
for this audio output: the @code{hardware} mixer, the @code{software}
mixer, the @code{null} mixer (allows setting the volume, but with no
effect; this can be used as a trick to implement an external mixer
External Mixer) or no mixer (@code{none}).
-@item @code{extra-options} (default: @code{'()})
-An association list of option symbols to string values to be appended to
-the audio output configuration.
+@item @code{replay-gain-handler} (type: maybe-string)
+This field accepts a string that specifies how
+@uref{https://mpd.readthedocs.io/en/latest/user.html#replay-gain,Replay
+Gain} is to be applied. @code{software} uses an internal software
+volume control, @code{mixer} uses the configured (hardware) mixer
+control and @code{none} disables replay gain on this audio output.
+
+@item @code{extra-options} (default: @code{()}) (type: alist)
+An association list of option symbols/strings to string values to be
+appended to the audio output configuration.
@end table
@end deftp
-The following example shows a configuration of @code{mpd} that provides
-an HTTP audio streaming output.
+The following example shows a configuration of @command{mpd} that
+configures some of its plugins and provides a HTTP audio streaming output.
@lisp
(service mpd-service-type
@@ -33269,7 +33378,19 @@ Audio Services
(mixer-type 'null)
(extra-options
`((encoder . "vorbis")
- (port . "8080"))))))))
+ (port . "8080"))))))
+ (decoders
+ (list (mpd-plugin
+ (plugin "mikmod")
+ (enabled? #f))
+ (mpd-plugin
+ (plugin "openmpt")
+ (enabled? #t)
+ (extra-options `((repeat-count . -1)
+ (interpolation-filter . 1))))))
+ (resampler (mpd-plugin
+ (plugin "libsamplerate")
+ (extra-options `((type . 0)))))))
@end lisp
diff --git a/gnu/services/audio.scm b/gnu/services/audio.scm
index b7cb0ebe38..7168320635 100644
--- a/gnu/services/audio.scm
+++ b/gnu/services/audio.scm
@@ -21,20 +21,75 @@
(define-module (gnu services audio)
#:use-module (guix gexp)
+ #:use-module (guix deprecation)
+ #:use-module (guix diagnostics)
+ #:use-module (guix i18n)
#:use-module (gnu services)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu services admin)
#:use-module (gnu system shadow)
#:use-module (gnu packages admin)
#:use-module (gnu packages mpd)
#:use-module (guix records)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-8)
#:use-module (srfi srfi-26)
#:export (mpd-output
mpd-output?
+ mpd-output-name
+ mpd-output-type
+ mpd-output-enabled?
+ mpd-output-format
+ mpd-output-tags?
+ mpd-output-always-on?
+ mpd-output-mixer-type
+ mpd-output-replay-gain-handler
+ mpd-output-extra-options
+
+ mpd-plugin
+ mpd-plugin?
+ mpd-plugin-plugin
+ mpd-plugin-name
+ mpd-plugin-enabled?
+ mpd-plugin-extra-options
+
+ mpd-partition
+ mpd-partition?
+ mpd-partition-name
+ mpd-partition-extra-options
+
mpd-configuration
mpd-configuration?
+ mpd-configuration-package
+ mpd-configuration-user
+ mpd-configuration-group
+ mpd-configuration-shepherd-requirement
+ mpd-configuration-log-file
+ mpd-configuration-log-level
+ mpd-configuration-music-directory
+ mpd-configuration-music-dir
+ mpd-configuration-playlist-directory
+ mpd-configuration-playlist-dir
+ mpd-configuration-db-file
+ mpd-configuration-state-file
+ mpd-configuration-sticker-file
+ mpd-configuration-default-port
+ mpd-configuration-endpoints
+ mpd-configuration-address
+ mpd-configuration-database
+ mpd-configuration-partitions
+ mpd-configuration-neighbors
+ mpd-configuration-inputs
+ mpd-configuration-archive-plugins
+ mpd-configuration-input-cache-size
+ mpd-configuration-decoders
+ mpd-configuration-resampler
+ mpd-configuration-filters
+ mpd-configuration-outputs
+ mpd-configuration-playlist-plugins
+ mpd-configuration-extra-options
mpd-service-type))
;;; Commentary:
@@ -50,13 +105,20 @@ (define (uglify-field-name field-name)
str)
#\-) "_")))
+(define list-of-string?
+ (list-of string?))
+
+(define list-of-symbol?
+ (list-of symbol?))
+
(define (mpd-serialize-field field-name value)
- #~(format #f "~a ~s~%" #$(if (string? field-name)
- field-name
- (uglify-field-name field-name))
- #$(if (string? value)
- value
- (object->string value))))
+ (let ((field (if (string? field-name) field-name
+ (uglify-field-name field-name)))
+ (value (cond
+ ((boolean? value) (if value "yes" "no"))
+ ((string? value) value)
+ (else (object->string value)))))
+ #~(format #f "~a ~s~%" #$field #$value)))
(define (mpd-serialize-alist field-name value)
#~(string-append #$@(generic-serialize-alist list mpd-serialize-field
@@ -64,20 +126,103 @@ (define (mpd-serialize-alist field-name value)
(define mpd-serialize-string mpd-serialize-field)
-(define (mpd-serialize-boolean field-name value)
- (mpd-serialize-field field-name (if value "yes" "no")))
+(define mpd-serialize-boolean mpd-serialize-field)
-(define (mpd-serialize-list-of-mpd-output field-name value)
- #~(string-append "\naudio_output {\n"
- #$@(map (cut serialize-configuration <>
- mpd-output-fields)
- value)
- "}\n"))
-(define (mpd-serialize-configuration configuration)
- (mixed-text-file
- "mpd.conf"
- (serialize-configuration configuration mpd-configuration-fields)))
+(define-maybe string (prefix mpd-))
+(define-maybe list-of-string (prefix mpd-))
+(define-maybe boolean (prefix mpd-))
+
+;;; TODO: Procedures for deprecated fields, to be removed.
+
+(define mpd-deprecated-fields '((music-dir . music-directory)
+ (playlist-dir . playlist-directory)
+ (address . endpoints)))
+
+(define (port? value) (or (string? value) (integer? value)))
+
+(define (mpd-serialize-deprecated-field field-name value)
+ (if (maybe-value-set? value)
+ (begin
+ (warn-about-deprecation
+ field-name #f
+ #:replacement (assoc-ref mpd-deprecated-fields field-name))
+ (match field-name
+ ('playlist-dir (mpd-serialize-string "playlist_directory" value))
+ ('music-dir (mpd-serialize-string "music_directory" value))
+ ('address (mpd-serialize-string "bind_to_address" value))))
+ ""))
+
+(define (mpd-serialize-port field-name value)
+ (when (string? value)
+ (warning
+ (G_ "string value for '~a' is deprecated, use integer instead~%")
+ field-name))
+ (mpd-serialize-field "port" value))
+
+(define-maybe port (prefix mpd-))
+
+;;;
+
+;; Generic MPD plugin record, lists only the most prevalent fields.
+(define-configuration mpd-plugin
+ (plugin
+ maybe-string
+ "Plugin name.")
+
+ (name
+ maybe-string
+ "Name.")
+
+ (enabled?
+ maybe-boolean
+ "Whether the plugin is enabled/disabled.")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols/strings to string values
+to be appended to the plugin configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/plugins.html,MPD plugin reference}
+for available options.")
+
+ (prefix mpd-))
+
+(define (mpd-serialize-mpd-plugin field-name value)
+ #~(format #f "~a {~%~a}~%"
+ '#$field-name
+ #$(serialize-configuration value mpd-plugin-fields)))
+
+(define (mpd-serialize-list-of-mpd-plugin field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-plugin field-name <>)
+ value)))
+
+(define list-of-mpd-plugin? (list-of mpd-plugin?))
+
+(define-maybe mpd-plugin (prefix mpd-))
+
+(define-configuration mpd-partition
+ (name
+ string
+ "Partition name.")
+
+ (extra-options
+ (alist '())
+ "An association list of option symbols/strings to string values
+to be appended to the partition configuration. See
+@uref{https://mpd.readthedocs.io/en/latest/user.html#configuring-partitions,Configuring Partitions}
+for available options.")
+
+ (prefix mpd-))
+
+(define (mpd-serialize-mpd-partition field-name value)
+ #~(format #f "partition {~%~a}~%"
+ #$(serialize-configuration value mpd-partition-fields)))
+
+(define (mpd-serialize-list-of-mpd-partition field-name value)
+ #~(string-append #$@(map (cut mpd-serialize-mpd-partition #f <>) value)))
+
+(define list-of-mpd-partition?
+ (list-o
This message was truncated. Download the full message here.
B
B
Bruno Victal wrote 7 days ago
[PATCH v6 3/3] services: mpd: Do not hardcode environment variables.
(address . 59866@debbugs.gnu.org)
6b51304098d50a01c207abddb3b02ba863034e56.1675367583.git.mirai@makinata.eu
Services should not expect for XDG_RUNTIME_DIR to be set.
Inferring from the past comment, this seemed to resolve an issue when
the service was launched with an _interactive_ user-account,
which tends to have this variable set by the login-manager.

This is not the case for system accounts and setting this variable
results in PulseAudio (launched by the same system user) failing to start
since it attempts to use a nonexistent directory.

Ideally, this service should have a home-service counterpart but the old
behavior can be emulated by setting the environment-variables field to:

(environment-variables
(list
(string-append "XDG_RUNTIME_DIR=/run/user/"
(number->string (passwd:uid (getpwnam "myuser"))))))

* gnu/services/audio.scm
(mpd-configuration)[environment-variables]: New field.
(mpd-shepherd-service)[start]: Use new field.
* doc/guix.texi (Audio Services)[Music Player Daemon]: Document it.
---

Notable changes since v5:
* NEW. Fixes PulseAudio issues resulting from hardcoded environment variables.

doc/guix.texi | 3 +++
gnu/services/audio.scm | 14 ++++++++------
2 files changed, 11 insertions(+), 6 deletions(-)

Toggle diff (55 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 8e220e0631..05c216bec4 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -33199,6 +33199,9 @@ Audio Services
This is a list of symbols naming Shepherd services that this service
will depend on.
+@item @code{environment-variables} (default: @code{()}) (type: list-of-string)
+A list of strings specifying environment variables.
+
@item @code{log-file} (default: @code{"/var/log/mpd/log"}) (type: maybe-string)
The location of the log file. Set to @code{syslog} to use the local
syslog daemon or @code{%unset-value} to omit this directive from the
diff --git a/gnu/services/audio.scm b/gnu/services/audio.scm
index 7168320635..96b27660e5 100644
--- a/gnu/services/audio.scm
+++ b/gnu/services/audio.scm
@@ -316,6 +316,11 @@ (define-configuration mpd-configuration
will depend on."
empty-serializer)
+ (environment-variables
+ (list-of-string '())
+ "A list of strings specifying environment variables."
+ empty-serializer)
+
(log-file
(maybe-string "/var/log/mpd/log")
"The location of the log file. Set to @code{syslog} to use the
@@ -465,7 +470,8 @@ (define (mpd-log-rotation config)
(define (mpd-shepherd-service config)
(match-record config <mpd-configuration> (user package shepherd-requirement
log-file playlist-directory
- db-file state-file sticker-file)
+ db-file state-file sticker-file
+ environment-variables)
(let* ((config-file (mpd-serialize-configuration config)))
(shepherd-service
(documentation "Run the MPD (Music Player Daemon)")
@@ -490,11 +496,7 @@ (define (mpd-shepherd-service config)
(list #$(file-append package "/bin/mpd")
"--no-daemon"
#$config-file)
- #:environment-variables
- ;; Required to detect PulseAudio when run under a user account.
- (list (string-append
- "XDG_RUNTIME_DIR=/run/user/"
- (number->string (passwd:uid (getpwnam #$user))))))))
+ #:environment-variables '#$environment-variables)))
(stop #~(make-kill-destructor))
(actions
(list (shepherd-configuration-action config-file)
--
2.38.1
L
L
Liliana Marie Prikler wrote 7 days ago
Re: [PATCH v6 2/3] services: mpd: Refactor MPD service.
88e71adf846525fbc1a964b24fb45e3e4b5c9208.camel@gmail.com
Not a complete review yet, just some quick pointers.

Am Donnerstag, dem 02.02.2023 um 20:07 +0000 schrieb Bruno Victal:
Toggle quote (5 lines)
> Refactor mpd-service-type to support additional mpd.conf directives
> and move activation-service-extension into service constructor.
>
> * gnu/services/audio.scm
> (mpd-plugin, mpd-partition): New record.
No need to break the line before the paren here.

Toggle quote (2 lines)
> (mpd-serialize-boolean, mpd-serialize-field): Integrate serializers
> into a single procedure for alist interop.
That's not a good docstring here. I think 
(mpd-serialize-boolean): Delete variable. Move logic into...
(mpd-serialize-field): ... this.
explains what's going on better.

Toggle quote (4 lines)
> [...]
>    #:use-module (srfi srfi-1)
> +  #:use-module (srfi srfi-8)
>    #:use-module (srfi srfi-26)
We generally prefer SRFI 71.


Cheers
L
L
Liliana Marie Prikler wrote 4 days ago
c7b9c0caf3e54cf0a9c27c2bba1e1a78e5733d2b.camel@gmail.com
Am Donnerstag, dem 02.02.2023 um 22:08 +0100 schrieb Liliana Marie
Prikler:
Toggle quote (16 lines)
> Not a complete review yet, just some quick pointers.
>
> Am Donnerstag, dem 02.02.2023 um 20:07 +0000 schrieb Bruno Victal:
> > Refactor mpd-service-type to support additional mpd.conf directives
> > and move activation-service-extension into service constructor.
> >
> > * gnu/services/audio.scm
> > (mpd-plugin, mpd-partition): New record.
> No need to break the line before the paren here.
>
> > (mpd-serialize-boolean, mpd-serialize-field): Integrate serializers
> > into a single procedure for alist interop.
> That's not a good docstring here.  I think 
>   (mpd-serialize-boolean): Delete variable.  Move logic into...
>   (mpd-serialize-field): ... this.
> explains what's going on better.
It actually didn't, because mpd-serialize-boolean wasn't deleted, but
aliased.

Toggle quote (5 lines)
> > [...]
> >    #:use-module (srfi srfi-1)
> > +  #:use-module (srfi srfi-8)
> >    #:use-module (srfi srfi-26)
> We generally prefer SRFI 71.
I fixed up the above, plus some compiler warnings and pushed it.

Cheers
Closed
?