forked from markhepburn/mplayer-mode
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mplayer-mode.el
278 lines (229 loc) · 9.74 KB
/
mplayer-mode.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
;;; mplayer-mode.el --- control mplayer, facilitating transcription and note-taking.
;; Copyright (C) 2011 Mark Hepburn
;; Author: Mark Hepburn ([email protected])
;; Compatibility: Emacs20, Emacs21, Emacs22, Emacs23
;; This file is not part of GNU Emacs.
;; This is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; This is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; Owes a lot in initial idea to the emacs video editor gneve
;; (http://www.1010.co.uk/gneve.html). This mode controls mplayer
;; directly, using its slave-mode (see
;; http://www.mplayerhq.hu/DOCS/tech/slave.txt), which accepts
;; commands on stdin. The original motivation was to facilitate note
;; taking from videos; hence it is possible to pause, skip backwards
;; and forwards, and insert a timestamp of the current position.
;;; Use:
;;; Install:
;; Put something similar to the following in your ~/.emacs to use this file:
;;
;; (load "~/path/to/mplayer-mode.el")
;;
;;; Dependency:
;; mplayer
;;; TODO:
;; - Proper org-mode integration would probably be nice (eg, a link to the file)
;; - Error handling and clean-up
;;; Code:
(defgroup mplayer nil
"Group used to store various mplayer-mode variables.")
(defcustom mplayer-executable "mplayer"
"Name or path to the mplayer executable."
:type 'file
:group 'mplayer)
(defvar mplayer-mode-map nil
"Local keymap for mplayer-mode")
;;; This prefix is chosen for ergonomic accessibility; it does ignore
;;; the recomendations about C-x being for global combinations, etc,
;;; so change if it's inconvenient.
(defcustom mplayer-prefix-command "\C-x "
"The prefix for all mplayer minor-mode commands. Default C-x SPC."
:type 'key-sequence
:group 'mplayer)
(defcustom mplayer-default-seek-step 10
"The number of seconds that the skip command will use."
:type 'integer
:group 'mplayer)
(defcustom mplayer-default-speed-step 10
"The increase/decrease of playback speed that the faster/slower commands will use (percent of standard playback speed)."
:type 'integer
:group 'mplayer)
(defcustom mplayer-osd-level 3
"OSD level used by mplayer. 3 (the default) means position/length."
:type 'integer
:group 'mplayer)
(defcustom mplayer-timestamp-format "%H:%M:%S"
"Format used for inserting timestamps."
:type 'string
:group 'mplayer)
;;; Utilities:
(defun mplayer--send (cmd)
(process-send-string mplayer-process (concat cmd "\n")))
(defun mplayer--parse-seconds (seconds)
(cond
((null seconds) mplayer-default-seek-step)
((numberp seconds) seconds)
((listp seconds)
(* mplayer-default-seek-step (log (abs (car seconds)) 4)))))
(defun mplayer--parse-speedstep (speedstep)
(cond
((null speedstep) (/ mplayer-default-speed-step 100.0))
((numberp speedstep) (/ speedstep 100.0))
((listp speedstep)
(/ (* mplayer-default-speed-step (+ 1 (log (abs (car speedstep)) 4))) 100.0))))
(defun mplayer--format-time (time)
"Return a formatted time string, using the format string
`mplayer-timestamp-format'. The argument is in seconds, and
can be an integer or a string."
(message "format-time: %s" time)
(if (stringp time)
(setq time (round (string-to-number time))))
(message "time to format: %s" time)
(format-time-string mplayer-timestamp-format `(0 ,time 0) t))
;;; Interactive Commands:
(defun mplayer-find-file (filename)
"Entry point to this mode. Starts playing the file using
mplayer, and enables some keybindings to support it; see the
documentation for `mplayer-mode' for available bindings."
(interactive "fOpen recording file: ")
(set (make-local-variable 'mplayer--osd-enabled) nil)
(set (make-local-variable 'mplayer-process-buffer) (generate-new-buffer "*mplayer*"))
(set (make-local-variable 'mplayer-process)
(start-process "mplayer" mplayer-process-buffer
mplayer-executable
"-quiet" "-slave"
filename))
(mplayer-mode t))
(defun mplayer-toggle-pause ()
"Pause or play the currently-open recording."
(interactive)
(mplayer--send "pause"))
(defun mplayer-seek-forward (seconds)
"Skip forward in the recording. By default this is
`mplayer-default-seek-step' seconds; it can also be specified as
a numeric prefix arg, or plain prefix args act as a
successive (linear) multipliers of `mplayer-default-seek-step'."
(interactive "P")
(let ((seconds (mplayer--parse-seconds seconds)))
(mplayer--send (format "seek %d 0" seconds))))
(defun mplayer-seek-backward (seconds)
"Skip backward in the recording. By default this is
`mplayer-default-seek-step' seconds; it can also be specified as
a numeric prefix arg, or plain prefix args act as a
successive (linear) multipliers of `mplayer-default-seek-step'."
(interactive "P")
(let ((seconds (- (mplayer--parse-seconds seconds))))
(mplayer--send (format "seek %d 0" seconds))))
(defun mplayer-faster (speedstep)
"Increase playback speed. By default by `mplayer-default-speed-step' percentage points; it can also be set with a numeric prefix arg, or plain prefix args acts as successive multipliers (2,3,4...) of `mplayer-default-speed-step'"
(interactive "P")
(let ((speedstep (mplayer--parse-speedstep speedstep)))
(mplayer--send (format "speed_incr %.2f" speedstep))))
(defun mplayer-slower (speedstep)
"Decreaser playback speed. By default by `mplayer-default-speed-step' percentage points; it can also be set with a numeric prefix arg, or plain prefix args acts as successive multipliers (2,3,4...) of `mplayer-default-speed-step'"
(interactive "P")
(let ((speedstep (mplayer--parse-speedstep speedstep)))
(mplayer--send (format "speed_incr -%.2f" speedstep))))
(defun mplayer-reset-speed ()
"Reset playback speed."
(interactive)
(mplayer--send "speed_set 1"))
(defun mplayer-toggle-osd ()
"Toggle on-screen display on or off. See `mplayer-osd-level'
for the type of display."
(interactive)
(if mplayer--osd-enabled
(mplayer--send "osd")
(mplayer--send (format "osd %d" mplayer-osd-level)))
(setq mplayer--osd-enabled (not mplayer--osd-enabled)))
(defun mplayer-insert-timestamp ()
"Insert a time-stamp of the current recording position in the
buffer. See `mplayer-timestamp-format' for the insertion
format."
(interactive)
(let (time)
(set-process-filter
mplayer-process
;; wait for output, process, and remove filter:
(lambda (process output)
(message "process: %s output: %s" process output)
(string-match "^ANS_TIME_POSITION=\\(.*\\)$" output)
(setq time (match-string 1 output))
(if time
(insert (mplayer--format-time time))
(message "MPlayer: couldn't detect current time."))
(set-process-filter mplayer-process nil)))
;; Then send the command:
(mplayer--send "get_time_pos")))
(defun mplayer-insert-position ()
"Insert the current recording position in seconds,
into the buffer."
(interactive)
(let (time)
(set-process-filter
mplayer-process
;; wait for output, process, and remove filter:
(lambda (process output)
(message "process: %s output: %s" process output)
(string-match "^ANS_TIME_POSITION=\\(.*\\)$" output)
(setq time (match-string 1 output))
(if time
(insert time)
(message "MPlayer: couldn't detect current time."))
(set-process-filter mplayer-process nil)))
;; Then send the command:
(mplayer--send "get_time_pos")))
(defun mplayer-seek-position (position)
"Seek to some place in the recording."
;; (interactive "P")
(interactive "nEnter seek position: ")
;; (message "Seeking to position: %n" position)
(mplayer--send (format "seek %d 2" position)))
(defun mplayer-quit-mplayer ()
"Quit mplayer and exit this mode."
(interactive)
(mplayer--send "quit")
(set-process-filter
mplayer-process
(lambda (process output)
(kill-buffer mplayer-process-buffer)))
(mplayer-mode nil))
;;; Mode setup:
(unless mplayer-mode-map
(setq mplayer-mode-map (make-sparse-keymap)))
(let ((map (make-sparse-keymap)))
;; (define-key map (kbd "f") 'mplayer-find-file)
(define-key map (kbd "SPC") 'mplayer-toggle-pause)
(define-key map (kbd "<right>") 'mplayer-seek-forward)
(define-key map (kbd "<left>") 'mplayer-seek-backward)
(define-key map (kbd "f") 'mplayer-faster)
(define-key map (kbd "s") 'mplayer-slower)
(define-key map (kbd "r") 'mplayer-reset-speed)
(define-key map (kbd "p") 'mplayer-seek-position)
(define-key map (kbd "t") 'mplayer-insert-position)
(define-key map (kbd "d") 'mplayer-toggle-osd)
(define-key map (kbd "i") 'mplayer-insert-timestamp)
(define-key map (kbd "q") 'mplayer-quit-mplayer)
(define-key mplayer-mode-map mplayer-prefix-command map))
(define-minor-mode mplayer-mode
"Control mplayer from within Emacs. Mainly intended for
transcription purposes, so commands exist to pause, seek, set playback speed, and
insert the current time as a timestamp. This mode should not be
invoked directly; see `mplayer-find-file' and
`mplayer-quit-mplayer' for the entry and exit points.
Key bindings:
\\{mplayer-mode-map}"
nil ; initial value
" MPlayer" ; mode-line string
mplayer-mode-map)
(provide 'mplayer-mode)