3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4 ;; This program is free software; you can redistribute it and/or ;;
5 ;; modify it under the terms of the GNU General Public License as ;;
6 ;; published by the Free Software Foundation; either version 3, or (at ;;
7 ;; your option) any later version. ;;
9 ;; This program is distributed in the hope that it will be useful, but ;;
10 ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;;
11 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;
12 ;; General Public License for more details. ;;
14 ;; You should have received a copy of the GNU General Public License ;;
15 ;; along with this program. If not, see <http://www.gnu.org/licenses/>. ;;
17 ;; Written by and Copyright (C) Francois Fleuret ;;
18 ;; Contact <francois@fleuret.org> for comments & bug reports ;;
19 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
21 ;; Is it me, or the slave mode of mplayer is ugly to parse? Did I miss
24 (defcustom media/mplayer/args nil
25 "List of arguments for mplayer."
29 (defcustom media/mplayer/timing-request-period 0.25
30 "Period for the timing requests in second(s). Larger values
31 load Emacs less. Nil means no timing."
35 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
37 ;; It is impossible to tell mplayer to send information every time dt
38 ;; or so, hence this mess with a timer to avoid overloading emacs with
39 ;; the processing of the information
41 (defvar media/mplayer/timer nil
42 "A timer to request the timing position.")
44 (defun media/mplayer/timing-request ()
45 (if media/mplayer/process
46 (unless media/mplayer/paused
47 (media/mplayer/write "get_time_pos\n")
49 (media/mplayer/stop-timing-requests)
52 (defun media/mplayer/start-timing-requests ()
53 (when media/mplayer/timing-request-period
54 (media/mplayer/stop-timing-requests)
55 (setq media/mplayer/timer
57 media/mplayer/timing-request-period
58 'media/mplayer/timing-request))
62 (defun media/mplayer/stop-timing-requests ()
63 (when media/mplayer/timer
64 (cancel-timer media/mplayer/timer)
65 (setq media/mplayer/timer nil)
68 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
70 (defun media/mplayer/filter-subfunctions (cmd param)
71 ;; (unless (string= cmd "A:")
72 ;; (message "cmd=%s param=%s" cmd param)
80 ;; ----------------------------------------
83 (if (string-match "StreamTitle='\\([^;]*\\)';" param)
84 (setq media/mplayer/current-stream-song (match-string 1 param))
85 (message "ICY Info \"%s\"" param)))
87 ;; ----------------------------------------
91 (setq media/song-duration
92 (string-to-number (substring param 1))))
94 ;; ----------------------------------------
96 ("ANS_TIME_POSITION" .
99 (setq media/song-current-time
100 (string-to-number (substring param 1)))
102 (when (and media/duration-to-history
103 (< media/mplayer/cumulated-duration media/duration-to-history))
105 (when media/mplayer/last-current-time
106 (setq media/mplayer/cumulated-duration
107 (+ media/mplayer/cumulated-duration
108 (- media/song-current-time media/mplayer/last-current-time))))
110 (when (>= media/mplayer/cumulated-duration media/duration-to-history)
111 (media/put-in-history)
114 (setq media/mplayer/last-current-time media/song-current-time)
121 ;; ----------------------------------------
126 ;; param = "44100 Hz, 2 ch, s16le, 128.0 kbit/9.07% (ratio: 16000->176400)"
127 (when (string-match "^\\([0-9]+\\) Hz, \\([0-9]+\\) ch.* \\([0-9.]+\\) kbit"
129 (setq media/current-information
130 (list media/mplayer/url
131 (string-to-number (match-string 1 param))
132 (string-to-number (match-string 2 param))
133 (string-to-number (match-string 3 param))))
135 (run-hooks 'media/play-hook)
138 ;; ----------------------------------------
141 (media/mplayer/write "get_time_length\n"))
143 ;; ----------------------------------------
147 (when (string-match "(\\([0-9]+\\) bytes" param)
148 (message "Caching stream (%dkb)"
149 (/ (string-to-number (match-string 1 param)) 1024))))
151 ;; ----------------------------------------
156 (setq media/mplayer/exit-type
157 (cdr (assoc param '(("(End of file)" . file-finished)
159 media/current-information nil
160 media/song-duration nil
161 media/song-current-time nil)
163 (when media/mplayer/process (kill-process media/mplayer/process))
165 (force-mode-line-update)))
167 ;; ----------------------------------------
172 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
174 (defun media/mplayer/filter (process str)
175 (setq media/mplayer/buffer (concat media/mplayer/buffer str))
177 (while (and (< start (length media/mplayer/buffer))
178 (string-match "\\(.*\\)[\n
\r]+" media/mplayer/buffer start))
179 (setq start (1+ (match-end 1)))
180 (let ((line (match-string 1 media/mplayer/buffer)))
181 (when (string-match "^\\(AUDIO:\\|Exiting...\\|Starting\\|ANS_LENGTH\\|ANS_TIME_POSITION\\|Cache fill:\\|ICY Info:\\) *\\(.*\\)$" line)
182 (media/mplayer/filter-subfunctions (match-string 1 line) (match-string 2 line))))
184 (when (and media/mplayer/current-stream-song media/current-information)
185 (message "Now in stream (%s) \"%s\""
186 (current-time-string) media/mplayer/current-stream-song)
187 (setq media/mplayer/current-stream-song nil))
189 (setq media/mplayer/buffer (substring media/mplayer/buffer start)))
192 (defun media/mplayer/sentinel (process str) ()
193 ;; (message "Media process got \"%s\"" (replace-regexp-in-string "\n" "" str))
194 (unless (eq (process-status media/mplayer/process) 'run)
195 (setq media/current-information nil
196 media/mplayer/process nil
197 media/song-current-time nil
198 media/song-duration nil)
200 (media/mplayer/stop-timing-requests)
202 (if (eq media/mplayer/exit-type 'file-finished)
203 (run-hooks 'media/finished-hook)
204 (run-hooks 'media/error-hook))
206 (force-mode-line-update))
209 (defun media/mplayer/write (&rest l)
210 ;; (message "****** WROTE \"%s\"" (replace-regexp-in-string "\n" "[RETURN]" (apply 'format l)))
211 (if media/mplayer/process (process-send-string media/mplayer/process (apply 'format l))
212 (error "No mplayer process"))
215 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
216 ;; Player control abstract layer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
217 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
219 (defun media/api/init () "Called once when the media application starts"
220 (setq media/player-id "MPlayer"
221 media/mplayer/url nil
222 media/mplayer/buffer "" ;; Used as read buffer
223 media/mplayer/process nil
224 media/mplayer/exit-type nil
225 media/mplayer/paused nil
226 media/song-duration nil
227 media/song-current-time nil
228 media/mplayer/current-stream-song nil
229 media/mplayer/cumulated-duration 0
230 media/mplayer/last-current-time nil
233 (defun media/api/cleanup () "Called when killing the application's buffer"
234 (when media/mplayer/process
235 (delete-process media/mplayer/process)
236 (media/mplayer/stop-timing-requests)
237 (setq media/mplayer/process nil)))
239 (defun media/api/play (url) (interactive)
240 (setq media/mplayer/url url)
242 (when media/mplayer/process (kill-process media/mplayer/process))
244 ;; (if media/mplayer/process
245 ;; (media/mplayer/write (concat "loadfile "
246 ;; (replace-regexp-in-string "^file://" "" media/mplayer/url)
249 (setq media/mplayer/process
253 '("mplayer" nil "mplayer" "-slave" "-quiet")
255 (if (string-match "\\(asx\\|m3u\\|pls\\|ram\\)$" media/mplayer/url)
257 (list (replace-regexp-in-string "^file://" "" media/mplayer/url))))
258 media/mplayer/exit-type 'unknown
259 media/mplayer/paused nil
260 media/song-duration nil
261 media/song-current-time nil
262 media/mplayer/cumulated-duration 0
263 media/mplayer/last-current-time nil
266 (set-process-filter media/mplayer/process 'media/mplayer/filter)
267 (set-process-sentinel media/mplayer/process 'media/mplayer/sentinel)
268 (process-kill-without-query media/mplayer/process)
269 (media/mplayer/start-timing-requests)
270 (media/mplayer/write "get_time_pos\n")
274 (defun media/api/stop () (interactive)
275 (media/mplayer/write "quit\n")
278 (defun media/api/pause () (interactive)
279 (media/mplayer/write "pause\n")
280 (setq media/mplayer/paused (not media/mplayer/paused))
283 (defun media/api/set-volume (mode value) (interactive)
284 (if (eq mode 'absolute)
285 (media/mplayer/write "volume %s 1\n" value)
287 (media/mplayer/write "volume +%s\n" value)
288 (media/mplayer/write "volume %s\n" value))))
290 (defun media/api/jump-at-percent (percent) (interactive)
291 (setq media/song-current-time nil)
292 (when (< media/mplayer/cumulated-duration media/duration-to-history)
293 (setq media/mplayer/cumulated-duration 0
294 media/mplayer/last-current-time nil))
295 (media/mplayer/write "seek %s 1\n" percent)
296 (media/mplayer/write "get_time_pos\n")
299 (defun media/api/jump-at-time (mode time) (interactive)
300 (setq media/song-current-time nil)
301 (when (< media/mplayer/cumulated-duration media/duration-to-history)
302 (setq media/mplayer/cumulated-duration 0
303 media/mplayer/last-current-time nil))
304 (if (eq mode 'absolute)
305 (media/mplayer/write "seek %s 2\n" time)
306 (media/mplayer/write "seek %s 0\n" time))
307 (media/mplayer/write "get_time_pos\n")
310 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;