fordon_greeman / voidfiles

Branch: main
Switch branches/tags
main
theidioticdev add emacs 2079ec5 1d ago
225 lines · 8.2 KB Raw Blame History
1 ;;; emms-player-mpg321-remote.el --- play files with mpg321 -R -*- lexical-binding: t; -*-
2
3 ;; Copyright (C) 2006-2021 Free Software Foundation, Inc.
4
5 ;; Author: Damien Elmes <emacs@repose.cx>
6 ;; Keywords: emms, mp3, mpeg, multimedia
7
8 ;; This file is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation; either version 3, or (at your option)
11 ;; any later version.
12
13 ;; This file is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;; GNU General Public License for more details.
17
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with GNU Emacs; see the file COPYING. If not, write to
20 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 ;; Boston, MA 02111-1307, USA.
22
23 ;;; Commentary:
24
25 ;; This file provides an emms-player which uses mpg321's remote mode
26 ;; to play files. This is a persistent process which isn't killed each
27 ;; time a new file is played.
28
29 ;; The remote process copes graciously with errors in music files, and
30 ;; allows you to seek in files.
31
32 ;; To enable this code, add the following to your emacs configuration:
33
34 ;; (require 'emms-player-mpg321-remote)
35 ;; (push 'emms-player-mpg321-remote emms-player-list)
36
37 ;;; Code:
38
39 (require 'emms)
40 (require 'emms-player-simple)
41
42 ;; --------------------------------------------------
43 ;; Variables and configuration
44 ;; --------------------------------------------------
45
46 (defgroup emms-player-mpg321-remote nil
47 "*EMMS player using mpg321's remote mode."
48 :group 'emms-player
49 :prefix "emms-player-mpg321-remote")
50
51 (defcustom emms-player-mpg321-remote-command "mpg321"
52 "The command name of mpg321."
53 :type 'string)
54
55 (defcustom emms-player-mpg321-remote-parameters nil
56 "Extra arguments to pass to mpg321 when using remote mode
57 For example: (list \"-o\" \"alsa\")"
58 :type '(repeat string))
59
60 (defcustom emms-player-mpg321-remote
61 (emms-player #'emms-player-mpg321-remote-start-playing
62 #'emms-player-mpg321-remote-stop-playing
63 #'emms-player-mpg321-remote-playable-p)
64 "A player for EMMS."
65 :type '(cons symbol alist))
66
67 (defvar emms-player-mpg321-remote-initial-args
68 (list "--skip-printing-frames=10" "-R" "-")
69 "Initial args to pass to the mpg321 process.")
70
71 (defvar emms-player-mpg321-remote-process-name "emms-player-mpg321-remote-proc"
72 "The name of the mpg321 remote player process")
73
74 (defvar emms-player-mpg321-remote-ignore-stop 0
75 "Number of stop messages to ignore, due to user action.")
76
77 (defmacro emms-player-mpg321-remote-add (cmd func)
78 `(emms-player-set 'emms-player-mpg321-remote
79 ,cmd ,func))
80
81 (emms-player-mpg321-remote-add
82 'regex (emms-player-simple-regexp "mp3" "mp2"))
83 (emms-player-mpg321-remote-add
84 'pause 'emms-player-mpg321-remote-pause)
85 (emms-player-mpg321-remote-add
86 'resume 'emms-player-mpg321-remote-pause)
87 (emms-player-mpg321-remote-add
88 'seek 'emms-player-mpg321-remote-seek)
89
90 ;; --------------------------------------------------
91 ;; Process maintenence
92 ;; --------------------------------------------------
93
94 (defun emms-player-mpg321-remote-start-process ()
95 "Start a new remote process, and return the process."
96 (let ((process (apply #'start-process
97 emms-player-mpg321-remote-process-name
98 nil
99 emms-player-mpg321-remote-command
100 (append emms-player-mpg321-remote-initial-args
101 emms-player-mpg321-remote-parameters))))
102 (set-process-sentinel process #'emms-player-mpg321-remote-sentinel)
103 (set-process-filter process #'emms-player-mpg321-remote-filter)
104 process))
105
106 (defun emms-player-mpg321-remote-stop ()
107 "Stop the currently playing process, if indeed there is one"
108 (let ((process (emms-player-mpg321-remote-process)))
109 (when process
110 (kill-process process)
111 (delete-process process))))
112
113 (defun emms-player-mpg321-remote-process ()
114 "Return the remote process, if it exists."
115 (get-process emms-player-mpg321-remote-process-name))
116
117 (defun emms-player-mpg321-remote-running-p ()
118 "True if the remote process exists and is running."
119 (let ((proc (emms-player-mpg321-remote-process)))
120 (and proc
121 (eq (process-status proc) 'run))))
122
123 (defun emms-player-mpg321-remote-sentinel (proc str)
124 "Sentinel for determining the end of process"
125 (ignore str)
126 (when (or (eq (process-status proc) 'exit)
127 (eq (process-status proc) 'signal))
128 ;; reset
129 (setq emms-player-mpg321-remote-ignore-stop 0)
130 (message "Remote process died!")))
131
132 (defun emms-player-mpg321-remote-send (text)
133 "Send TEXT to the mpg321 remote process, and add a newline."
134 (let (proc)
135 ;; we shouldn't be trying to send to a dead process
136 (unless (emms-player-mpg321-remote-running-p)
137 (emms-player-mpg321-remote-start-process))
138 (setq proc (emms-player-mpg321-remote-process))
139 (process-send-string proc (concat text "\n"))))
140
141 ;; --------------------------------------------------
142 ;; Interfacing with emms
143 ;; --------------------------------------------------
144
145 (defun emms-player-mpg321-remote-filter (proc str)
146 (ignore proc)
147 (let* ((data-lines (split-string str "\n" t))
148 data
149 cmd)
150 (dolist (line data-lines)
151 (setq data (split-string line))
152 (setq cmd (car data))
153 (cond
154 ;; stop notice
155 ((and (string= cmd "@P")
156 (or (string= (cadr data) "0")
157 (string= (cadr data) "3")))
158 (emms-player-mpg321-remote-notify-emms))
159 ;; frame notice
160 ((string= cmd "@F")
161 ;; even though a timer is constantly updating this variable,
162 ;; updating it here will cause it to stay pretty much in sync.
163 (run-hook-with-args 'emms-player-time-set-functions
164 (truncate (string-to-number (nth 3 data)))))))))
165
166 (defun emms-player-mpg321-remote-start-playing (track)
167 "Start playing a song by telling the remote process to play it.
168 If the remote process is not running, launch it."
169 (unless (emms-player-mpg321-remote-running-p)
170 (emms-player-mpg321-remote-start-process))
171 (emms-player-mpg321-remote-play-track track))
172
173 (defvar emms-player-ignore-stop)
174
175 (defun emms-player-mpg321-remote-notify-emms (&optional user-action)
176 "Tell emms that the current song has finished.
177 If USER-ACTION, set `emms-player-mpg321-remote-ignore-stop' so that we
178 ignore the next message from mpg321."
179 (if user-action
180 (let ((emms-player-ignore-stop t))
181 ;; so we ignore the next stop message
182 (setq emms-player-mpg321-remote-ignore-stop
183 (1+ emms-player-mpg321-remote-ignore-stop))
184 (emms-player-stopped))
185 ;; not a user action
186 (if (not (zerop emms-player-mpg321-remote-ignore-stop))
187 (setq emms-player-mpg321-remote-ignore-stop
188 (1- emms-player-mpg321-remote-ignore-stop))
189 (emms-player-stopped))))
190
191 (defun emms-player-mpg321-remote-stop-playing ()
192 "Stop the current song playing."
193 (emms-player-mpg321-remote-notify-emms t)
194 (emms-player-mpg321-remote-send "stop"))
195
196 (defun emms-player-mpg321-remote-play-track (track)
197 "Send a play command to the remote, based on TRACK."
198 (emms-player-mpg321-remote-send
199 (concat "load " (emms-track-get track 'name)))
200 (emms-player-started 'emms-player-mpg321-remote))
201
202 (defun emms-player-mpg321-remote-playable-p (track)
203 ;; use the simple definition.
204 (emms-player-mpg321-playable-p track))
205
206 (defun emms-player-mpg321-remote-pause ()
207 "Pause the player."
208 (emms-player-mpg321-remote-send "pause"))
209
210 (defun emms-player-mpg321-remote-resume ()
211 "Resume the player."
212 (emms-player-mpg321-remote-send "pause"))
213
214 (defun emms-player-mpg321-remote-seek (seconds)
215 "Seek forward or backward in the file."
216 ;; since mpg321 only supports seeking by frames, not seconds, we
217 ;; make a very rough guess as to how much a second constitutes
218 (let ((frame-string (number-to-string (* 35 seconds))))
219 ;; if we're not going backwards, we need to add a '+'
220 (unless (eq ?- (string-to-char frame-string))
221 (setq frame-string (concat "+" frame-string)))
222 (emms-player-mpg321-remote-send (concat "jump " frame-string))))
223
224 (provide 'emms-player-mpg321-remote)
225 ;;; emms-player-mpg321-remote.el ends here