fordon_greeman / voidfiles

Branch: 2079ec5
Switch branches/tags
main
theidioticdev add emacs 2079ec5 22h ago
161 lines · 7.1 KB Raw Blame History
1 ;;; emms-volume-mpv.el --- Volume function to adjust mpv volume easily -*- lexical-binding: t; -*-
2
3 ;; Copyright (C) 2025 Free Software Foundation, Inc.
4
5 ;; Author: Richard Sent <richard@freakingpenguin.com>
6
7 ;; This file is part of EMMS.
8
9 ;; EMMS is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 3, or (at your option)
12 ;; any later version.
13 ;;
14 ;; EMMS is distributed in the hope that it will be useful,
15 ;; but 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 EMMS; see the file COPYING. If not, write to the
21 ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22 ;; Boston, MA 02110-1301, USA.
23
24 ;;; Commentary:
25 ;;
26 ;; This file defines a function to raise or lower the volume of mpv.
27 ;; It can be used stand-alone by passing a process object, though it
28 ;; is meant for usage with Emms, particularly with emms-volume.el and
29 ;; emms-player-mpv.el.
30 ;;
31 ;; To use add the following to your Emms configuration
32 ;; (setq emms-volume-change-function 'emms-volume-mpv-change)
33
34 ;;; History:
35
36 ;; January 2025: First release, partly based on emms-volume-pulse.el.
37
38 ;;; Code:
39
40 (require 'emms-player-mpv)
41
42 (defcustom emms-volume-mpv-method 'native
43 "How Emms should attempt to adjust mpv's volume.
44
45 If `native', Emms will adjust mpv's volume property. This
46 provides the same experience as adjusting the volume slider in
47 mpv.
48
49 If `system', Emms will adjust mpv's ao-volume property, which
50 adjusts the volume using the system audio service, such as
51 Pulseaudio. Depending on what audio service is being used this
52 may either change the mpv application's volume or global volume.
53
54 If `smart', Emms will adjust both mpv's volume and ao-volume
55 properties. When raising volume, the native volume will be raised
56 to 100. Emms will then switch to adjusting system volume to 100
57 before raising the native volume again. When lowering volume,
58 Emms will lower the software volume to 100, then lower system
59 volume to 0.
60
61 Both `system' and `smart' require mpv to expose the ao-volume
62 property. This property is only available while mpv audio output
63 is active. If audio output is not active, the volume will not be
64 changed.
65
66 Additionally, the percentage provided by and set for ao-volume
67 and thus this module may not match what is reported by the system
68 audio program."
69 :type '(choice (const :tag "MPV Volume" native)
70 (const :tag "System Volume" system)
71 (const :tag "Smart" smart))
72 :group 'emms-volume)
73
74 (defvar emms-volume-mpv--volume-sync (make-mutex "emms-volume-mpv--volume-sync")
75 "Ensure only one volume-change function runs to completion at a
76 time.")
77
78 (defun emms-volume-mpv-synchronous-ipc (cmd &optional proc)
79 "Run mpv command and get result synchronously for current thread.
80
81 This must not be run by the main thread. The handler for
82 emms-player-mpv-ipc-req-send runs in the main thread, potentially
83 causing a deadlock."
84 (when (eq main-thread (current-thread))
85 (error "This function cannot be invoked by the main thread"))
86 (let* ((emms-volume-mpv--ipc-sync (make-mutex "emms-volume-mpv--ipc-sync"))
87 (emms-volume-mpv--ipc-sync-check (make-condition-variable emms-volume-mpv--ipc-sync
88 "emms-volume-mpv--ipc-sync-check"))
89 (emms-volume-mpv--ipc-sync-reply nil))
90 (with-mutex emms-volume-mpv--ipc-sync
91 (emms-player-mpv-ipc-req-send
92 cmd
93 #'(lambda (data err)
94 (with-mutex emms-volume-mpv--ipc-sync
95 (setq emms-volume-mpv--ipc-sync-reply (list data err))
96 (condition-notify emms-volume-mpv--ipc-sync-check)))
97 proc)
98 (while (not emms-volume-mpv--ipc-sync-reply)
99 (condition-wait emms-volume-mpv--ipc-sync-check))
100 (cl-multiple-value-bind (data err) emms-volume-mpv--ipc-sync-reply
101 (if err (error "Failed to run %s, %s" cmd err) data)))))
102
103 (defun emms-volume-mpv-limit (vol volume-max &optional volume-min)
104 "Limit VOL to the range [0 - volume-max]."
105 (max (min vol volume-max) (or volume-min 0)))
106
107 (defun emms-volume-mpv--smart-increment (native-old system-old amount native-max)
108 (cond
109 ((< native-old 100)
110 (list (emms-volume-mpv-limit (+ native-old amount) 100) system-old))
111 ((< system-old 100)
112 (list native-old (emms-volume-mpv-limit (+ system-old amount) 100)))
113 (t (list (emms-volume-mpv-limit (+ native-old amount) native-max) system-old))))
114
115 (defun emms-volume-mpv--smart-decrement (native-old system-old amount native-max)
116 (cond
117 ((> native-old 100)
118 (list (emms-volume-mpv-limit (+ native-old amount) native-max 100) system-old))
119 (t (list native-old (emms-volume-mpv-limit (+ system-old amount) 100)))))
120
121 (defun emms-volume-mpv--smart-change (native-old system-old amount native-max)
122 (if (>= amount 0)
123 (emms-volume-mpv--smart-increment native-old system-old amount native-max)
124 (emms-volume-mpv--smart-decrement native-old system-old amount native-max)))
125
126 ;;;###autoload
127 (defun emms-volume-mpv-change (amount &optional proc)
128 "Change volume by AMOUNT using mpv process PROC."
129 (unless (or emms-player-mpv-ipc-proc proc)
130 (error "mpv is not currently running"))
131 ;; mpv does not protect against storing volumes > volume-max. We
132 ;; must retrieve volume-max and verify the target volume.
133 (make-thread
134 (lambda ()
135 (with-mutex emms-volume-mpv--volume-sync
136 (with-demoted-errors "Failed to adjust the volume: %s"
137 (let* ((native-max (emms-volume-mpv-synchronous-ipc '(get_property volume-max) proc))
138 (native-old (emms-volume-mpv-synchronous-ipc '(get_property volume) proc))
139 (system-old (emms-volume-mpv-synchronous-ipc '(get_property ao-volume) proc)))
140 (pcase emms-volume-mpv-method
141 ('native (let ((volume (emms-volume-mpv-limit (+ native-old amount) native-max)))
142 (emms-volume-mpv-synchronous-ipc
143 `(set_property volume ,volume) proc)
144 (message "Native volume is %d%%" volume)))
145 ('system (let ((volume (emms-volume-mpv-limit (+ system-old amount) 100)))
146 (emms-volume-mpv-synchronous-ipc
147 `(set_property ao-volume ,volume) proc)
148 (message "System volume is %d%%" volume)))
149 ('smart (cl-multiple-value-bind (native system)
150 (emms-volume-mpv--smart-change native-old system-old
151 amount native-max)
152 (emms-volume-mpv-synchronous-ipc
153 `(set_property volume ,native) proc)
154 (emms-volume-mpv-synchronous-ipc
155 `(set_property ao-volume ,system) proc)
156 (message "Native volume is %d%% and system volume is %d%%"
157 native system))))))))))
158
159 (provide 'emms-volume-mpv)
160
161 ;;; emms-volume-mpv.el ends here