sound.scm 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2018, 2020 Oleg Pykhalov <go.wigust@gmail.com>
  3. ;;; Copyright © 2020 Liliana Marie Prikler <liliana.prikler@gmail.com>
  4. ;;; Copyright © 2020 Marius Bakke <mbakke@fastmail.com>
  5. ;;; Copyright © 2022 Maxim Cournoyer <maxim.cournoyer@gmail.com>
  6. ;;;
  7. ;;; This file is part of GNU Guix.
  8. ;;;
  9. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  10. ;;; under the terms of the GNU General Public License as published by
  11. ;;; the Free Software Foundation; either version 3 of the License, or (at
  12. ;;; your option) any later version.
  13. ;;;
  14. ;;; GNU Guix is distributed in the hope that it will be useful, but
  15. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ;;; GNU General Public License for more details.
  18. ;;;
  19. ;;; You should have received a copy of the GNU General Public License
  20. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  21. (define-module (gnu services sound)
  22. #:use-module (gnu services base)
  23. #:use-module (gnu services configuration)
  24. #:use-module (gnu services shepherd)
  25. #:use-module (gnu services)
  26. #:use-module (gnu system pam)
  27. #:use-module (gnu system shadow)
  28. #:use-module (guix diagnostics)
  29. #:use-module (guix gexp)
  30. #:use-module (guix packages)
  31. #:use-module (guix records)
  32. #:use-module (guix store)
  33. #:use-module (guix ui)
  34. #:use-module (gnu packages audio)
  35. #:use-module (gnu packages linux)
  36. #:use-module (gnu packages pulseaudio)
  37. #:use-module (ice-9 match)
  38. #:use-module (srfi srfi-1)
  39. #:export (alsa-configuration
  40. alsa-service-type
  41. pulseaudio-configuration
  42. pulseaudio-service-type
  43. ladspa-configuration
  44. ladspa-service-type))
  45. ;;; Commentary:
  46. ;;;
  47. ;;; Sound services.
  48. ;;;
  49. ;;; Code:
  50. ;;;
  51. ;;; ALSA
  52. ;;;
  53. (define-record-type* <alsa-configuration>
  54. alsa-configuration make-alsa-configuration alsa-configuration?
  55. (alsa-plugins alsa-configuration-alsa-plugins ;file-like
  56. (default alsa-plugins))
  57. (pulseaudio? alsa-configuration-pulseaudio? ;boolean
  58. (default #t))
  59. (extra-options alsa-configuration-extra-options ;string
  60. (default "")))
  61. (define alsa-config-file
  62. ;; Return the ALSA configuration file.
  63. (match-lambda
  64. (($ <alsa-configuration> alsa-plugins pulseaudio? extra-options)
  65. (apply mixed-text-file "asound.conf"
  66. `("# Generated by 'alsa-service'.\n\n"
  67. ,@(if pulseaudio?
  68. `("# Use PulseAudio by default
  69. pcm_type.pulse {
  70. lib \"" ,#~(string-append #$alsa-plugins:pulseaudio
  71. "/lib/alsa-lib/libasound_module_pcm_pulse.so") "\"
  72. }
  73. ctl_type.pulse {
  74. lib \"" ,#~(string-append #$alsa-plugins:pulseaudio
  75. "/lib/alsa-lib/libasound_module_ctl_pulse.so") "\"
  76. }
  77. pcm.!default {
  78. type pulse
  79. fallback \"sysdefault\"
  80. hint {
  81. show on
  82. description \"Default ALSA Output (currently PulseAudio Sound Server)\"
  83. }
  84. }
  85. ctl.!default {
  86. type pulse
  87. fallback \"sysdefault\"
  88. }\n\n")
  89. '())
  90. ,extra-options)))))
  91. (define (alsa-etc-service config)
  92. (list `("asound.conf" ,(alsa-config-file config))))
  93. (define alsa-service-type
  94. (service-type
  95. (name 'alsa)
  96. (extensions
  97. (list (service-extension etc-service-type alsa-etc-service)))
  98. (default-value (alsa-configuration))
  99. (description "Configure low-level Linux sound support, ALSA.")))
  100. ;;;
  101. ;;; PulseAudio
  102. ;;;
  103. (define-record-type* <pulseaudio-configuration>
  104. pulseaudio-configuration make-pulseaudio-configuration
  105. pulseaudio-configuration?
  106. (client-conf pulseaudio-configuration-client-conf
  107. (default '()))
  108. (daemon-conf pulseaudio-configuration-daemon-conf
  109. ;; Flat volumes may cause unpleasant experiences to users
  110. ;; when applications inadvertently max out the system volume
  111. ;; (see e.g. <https://bugs.gnu.org/38172>).
  112. (default '((flat-volumes . no))))
  113. (script-file pulseaudio-configuration-script-file
  114. (default (file-append pulseaudio "/etc/pulse/default.pa")))
  115. (extra-script-files pulseaudio-configuration-extra-script-files
  116. (default '()))
  117. (system-script-file pulseaudio-configuration-system-script-file
  118. (default
  119. (file-append pulseaudio "/etc/pulse/system.pa"))))
  120. (define (pulseaudio-conf-entry arg)
  121. (match arg
  122. ((key . value)
  123. (format #f "~a = ~s~%" key value))
  124. ((? string? _)
  125. (string-append arg "\n"))))
  126. (define pulseaudio-environment
  127. (match-lambda
  128. (($ <pulseaudio-configuration> client-conf daemon-conf default-script-file)
  129. ;; These config files kept at a fixed location, so that the following
  130. ;; environment values are stable and do not require the user to reboot to
  131. ;; effect their PulseAudio configuration changes.
  132. '(("PULSE_CONFIG" . "/etc/pulse/daemon.conf")
  133. ("PULSE_CLIENTCONFIG" . "/etc/pulse/client.conf")))))
  134. (define (extra-script-files->file-union extra-script-files)
  135. "Return a G-exp obtained by processing EXTRA-SCRIPT-FILES with FILE-UNION."
  136. (define (file-like->name file)
  137. (match file
  138. ((? local-file?)
  139. (local-file-name file))
  140. ((? plain-file?)
  141. (plain-file-name file))
  142. ((? computed-file?)
  143. (computed-file-name file))
  144. (_ (leave (G_ "~a is not a local-file, plain-file or \
  145. computed-file object~%") file))))
  146. (define (assert-pulseaudio-script-file-name name)
  147. (unless (string-suffix? ".pa" name)
  148. (leave (G_ "`~a' lacks the required `.pa' file name extension~%") name))
  149. name)
  150. (let ((labels (map (compose assert-pulseaudio-script-file-name
  151. file-like->name)
  152. extra-script-files)))
  153. (file-union "default.pa.d" (zip labels extra-script-files))))
  154. (define (append-include-directive script-file)
  155. "Append an include directive to source scripts under /etc/pulse/default.pa.d."
  156. (computed-file "default.pa"
  157. #~(begin
  158. (use-modules (ice-9 textual-ports))
  159. (define script-text
  160. (call-with-input-file #$script-file get-string-all))
  161. (call-with-output-file #$output
  162. (lambda (port)
  163. (format port (string-append script-text "
  164. ### Added by Guix to include scripts specified in extra-script-files.
  165. .nofail
  166. .include /etc/pulse/default.pa.d~%")))))))
  167. (define pulseaudio-etc
  168. (match-lambda
  169. (($ <pulseaudio-configuration> client-conf daemon-conf default-script-file
  170. extra-script-files system-script-file)
  171. `(("pulse"
  172. ,(file-union
  173. "pulse"
  174. `(("default.pa"
  175. ,(if (null? extra-script-files)
  176. default-script-file
  177. (append-include-directive default-script-file)))
  178. ("system.pa" ,system-script-file)
  179. ,@(if (null? extra-script-files)
  180. '()
  181. `(("default.pa.d" ,(extra-script-files->file-union
  182. extra-script-files))))
  183. ,@(if (null? daemon-conf)
  184. '()
  185. `(("daemon.conf"
  186. ,(apply mixed-text-file "daemon.conf"
  187. "default-script-file = " default-script-file "\n"
  188. (map pulseaudio-conf-entry daemon-conf)))))
  189. ,@(if (null? client-conf)
  190. '()
  191. `(("client.conf"
  192. ,(apply mixed-text-file "client.conf"
  193. (map pulseaudio-conf-entry client-conf))))))))))))
  194. (define pulseaudio-service-type
  195. (service-type
  196. (name 'pulseaudio)
  197. (extensions
  198. (list (service-extension session-environment-service-type
  199. pulseaudio-environment)
  200. (service-extension etc-service-type pulseaudio-etc)
  201. (service-extension udev-service-type (const (list pulseaudio)))))
  202. (default-value (pulseaudio-configuration))
  203. (description "Configure PulseAudio sound support.")))
  204. ;;;
  205. ;;; LADSPA
  206. ;;;
  207. (define-record-type* <ladspa-configuration>
  208. ladspa-configuration make-ladspa-configuration
  209. ladspa-configuration?
  210. (plugins ladspa-plugins (default '())))
  211. (define (ladspa-environment config)
  212. ;; Define this variable in the global environment such that
  213. ;; pulseaudio swh-plugins (and similar LADSPA plugins) work.
  214. `(("LADSPA_PATH" .
  215. (string-join
  216. ',(map (lambda (package) (file-append package "/lib/ladspa"))
  217. (ladspa-plugins config))
  218. ":"))))
  219. (define ladspa-service-type
  220. (service-type
  221. (name 'ladspa)
  222. (extensions
  223. (list (service-extension session-environment-service-type
  224. ladspa-environment)))
  225. (default-value (ladspa-configuration))
  226. (description "Configure LADSPA plugins.")))
  227. ;;; sound.scm ends here