Browse Source

org-hacks: audio/video playback code moved to code/elisp/org-player.el

Paul Sexton 9 years ago
parent
commit
c4817383c8
2 changed files with 277 additions and 86 deletions
  1. 269 0
      code/elisp/org-player.el
  2. 8 86
      org-hacks.org

+ 269 - 0
code/elisp/org-player.el

@@ -0,0 +1,269 @@
+;;; -*- coding: utf-8-unix -*-
+;;; org-player.el - Play audio and video files in org-mode hyperlinks
+;;;
+;;; Author: Paul Sexton <eeeickythump@gmail.com>
+;;; Version: 1.0.0
+;;; Repository at http://bitbucket.org/eeeickythump/org-player/
+;;;
+;;;
+;;; Synopsis
+;;; ========
+;;;
+;;; I was learning a language, and wanted to include hyperlinks to audio files
+;;; within my org document, and to be able to play each file by clicking on its
+;;; link. I wrote the code in this file, which makes org mode use an Emacs
+;;; media player library, Bongo, to play audio and video files contained in
+;;; 'file:' hyperlinks.
+;;;
+;;; Installation
+;;; ============
+;;;
+;;; First you will need to install Bongo, which you can download from:
+;;; https://github.com/dbrock/bongo
+;;;
+;;; You will also need to ensure you have an actual media player program
+;;; installed, and you need to tell Bongo which program to use to play files. I
+;;; have tested org-player with MPlayer, an open source media player which
+;;; works well on Windows and Linux. VLC does not work with Bongo on Windows at
+;;; the time of writing.
+;;;
+;;; You can obtain MPlayer from:
+;;; http://www.mplayerhq.hu/design7/news.html
+;;;
+;;; Ensure you have Bongo set up and working. Once this is achieved, installing
+;;; org-player is easy - just put the file somewhere in your Emacs load-path
+;;; and put (require 'org-player) in your .emacs file.
+;;;
+;;; Usage
+;;; =====
+;;;
+;;; Clicking on a link such as
+;;;
+;;;   [[file:/path/to/song.mp3]]
+;;;
+;;; adds it to the active Bongo playlist and immediately starts playing
+;;; it. Playback can be paused, fast-forwarded etc using Bongo commands (see
+;;; below).
+;;;
+;;; Links can also specify track positions. When a link contains a track
+;;; position, playback will start at that position in the track. For example:
+;;;
+;;; [[file:/path/to/song.mp3::2:43]]      Starts playback at 2 min 43 sec.
+;;; [[file:/path/to/song.mp3::1:10:45]]   Starts playback at 1 hr 10 min 45 sec.
+;;; [[file:/path/to/song.mp3::3m15s]]     Starts playback at 3 min 15 sec.
+;;; [[file:/path/to/song.mp3::49s]]       Starts playback at 0 min 49 sec.
+;;; [[file:/path/to/song.mp3::1h21m10s]]  Starts playback at 1 hr 21 min 10 sec.
+;;;
+;;; Controlling playback
+;;; ====================
+;;;
+;;; When Bongo plays a file it puts some icons in the modeline that resemble the
+;;; well-known symbols for 'play', 'stop', 'rewind' and so on, and which can be
+;;; used to control playback using the mouse. I found these worked erratically when
+;;; outside the actual Bongo playlist buffer, so I have instead bound some org-mode
+;;; keys (ctrl + numpad keys) to the relevant functions.  These are:
+;;;
+;;; C-<keypad 0>     pause/resume
+;;; C-<keypad .>     stop, or restart playback from beginning
+;;; C-<keypad />     show track info
+;;; C-<keypad 4>     skip back 10 seconds
+;;; C-<keypad 6>     skip forward 10 seconds
+;;; C-<keypad 9>     increase volume (requires separate 'volume' library
+;;;                  at https://github.com/dbrock/volume-el )
+;;; C-<keypad 3>     decrease volume
+;;;
+;;; Note that these keys (and the modeline icons, if they work) act on the
+;;; track that is active in the Bongo playlist. This will always be the last
+;;; track that you added by clicking on a link in org-mode, unless you alter
+;;; the Bongo playlist outside org mode.
+;;;
+;;; Also note that these keys are only bound within org mode buffers.
+;;;
+;;; Notes
+;;; =====
+;;;
+;;; I have only tested this with MP3 files, but it ought to work with
+;;; video as well, as both MPlayer and Bongo claim to work with video files.
+;;;
+;;; Future plans
+;;; ============
+;;;
+;;; If anyone wants to integrate this code with EMMS, another popular Emacs
+;;; media player library, contributions would be welcome.
+
+
+(require 'org)
+(require 'bongo)
+
+(defvar org-player-file-extensions-regexp
+  (concat "\\." (regexp-opt
+                 (append bongo-audio-file-name-extensions
+                         bongo-video-file-name-extensions))))
+
+(add-to-list 'org-file-apps
+             (cons (concat org-player-file-extensions-regexp "$")
+                   '(org-player-play-file file)))
+
+(add-to-list 'org-file-apps
+             (cons (concat org-player-file-extensions-regexp
+                           "::[0-9]+:[0-9]+\\(:[0-9]+\\|\\)")
+                   '(org-player-play-file file search)))
+
+(add-to-list 'org-file-apps
+             (cons (concat org-player-file-extensions-regexp
+                           "::[0-9]+[HhMmSs]\\([0-9]+[MmSs]\\|\\)\\([0-9]+[MmSs]\\|\\)")
+                   '(org-player-play-file file search)))
+
+
+(defun org-player-string-to-time (str)
+  "Understands any of the following formats: MM:SS, HH:MM:SS, NNhNNmNNs,
+NNhNNm, NNmNNs, NNh, NNm, NNs."
+  (let ((str (substring-no-properties str))
+        hours mins secs)
+    (cond
+     ((string-match "\\([0-9]+\\):\\([0-9]+\\)\\(:[0-9.]+\\|\\)" str)
+      (if (string= "" (match-string 3 str)) ; XX:YY
+          (setq mins (match-string 1 str)
+                secs (match-string 2 str))
+        (setq hours (match-string 1 str) ; XX:YY:ZZ
+              mins (match-string 2 str)
+              secs (subseq (match-string 3 str) 1))))
+     ((eq (string-match "[0-9]+[HhMmSs]" str)
+          (string-match "\\([0-9]+[Hh]\\|\\)\\([0-9]+[Mm]\\|\\)\\([0-9.]+[Ss]\\|\\)"
+                        str))
+      (setq hours (match-string 1 str)
+            mins (match-string 2 str)
+            secs (match-string 3 str))
+      (unless (string= "" hours)
+        (setq hours (subseq hours 0 (1- (length hours)))))
+      (unless (string= "" mins)
+        (setq mins (subseq mins 0 (1- (length mins)))))
+      (unless (string= "" secs)
+        (setq secs (subseq secs 0 (1- (length secs))))))
+     (t
+      (error "Unrecognised track position specifier: %S" str)))
+    (setq hours (if (or (null hours) (string= "" hours))
+                    0 (read hours)))
+    (setq mins (if (or (null mins) (string= "" mins))
+                    0 (read mins)))
+    (setq secs (if (or (null secs) (string= "" secs))
+                    0 (read secs)))
+    (list hours mins secs)))
+
+
+(defun org-player-play-file (filename &optional pos)
+  "POS, if given, is a string that specifies a track position where
+playback should begin. This string is taken from the portion of the
+hyperlink that follows a double colon. For example:
+
+  [[file:song.mp3::03:22]]
+  [[file:song.mp3::1h24m3s]]
+
+See `org-player-string-to-time' for a description of the format of
+the POS string."
+  (let ((seek (if pos
+                  (destructuring-bind (hours mins secs)
+                      (org-player-string-to-time pos)
+                    (+ secs
+                       (* mins 60)
+                       (* hours 60 60))))))
+    (with-bongo-buffer
+      (bongo-insert-file filename)
+      (backward-char)
+      (org-player-start/stop 'play seek)
+      (when seek
+        (bongo-seek-to seek)))))
+
+
+(defun org-player-get-track-info ()
+  "Returns a list: (TRACKTITLE ALBUMNAME ARTISTNAME ELAPSEDSECS LENGTHSECS)"
+  (with-bongo-buffer
+    (let* ((info (bongo-line-infoset))
+           (track (cdr (assoc 'track info))))
+      (message "%s" info)
+      (list (cdr (assoc 'title track))
+            (cdr (assoc 'album info))
+            (cdr (assoc 'artist info))
+            (or (bongo-elapsed-time) 0)
+            (or (bongo-total-time) 0)))))
+
+
+(defun org-player-print-track-info ()
+  "Print out some info about the active track, in the minibuffer."
+  (interactive)
+  (destructuring-bind (title album artist elapsed total)
+      (org-player-get-track-info)
+    (message "%s" (list title album artist elapsed total))
+    (cond
+     ((null title)
+      (message "No active track."))
+     (t
+      (message "Track: %s
+Album: %s / %s
+%s / %s (%d%%) %s"
+               title album artist
+               (format "%02d:%02d" (floor elapsed 60) (mod elapsed 60))
+               (format "%02d:%02d" (floor total 60) (mod total 60))
+               (/ (* elapsed 100) total)
+               (cond
+                ((bongo-paused-p) "(paused)")
+                ((bongo-playing-p) "(playing)")
+                (t "(stopped)"))
+               )))))
+
+
+(defun org-player-pause/resume ()
+  (interactive)
+  (destructuring-bind (title album artist elapsed total)
+      (org-player-get-track-info)
+    (when title
+      (message "%02d:%02d/%02d:%02d %s %s (%s / %s)"
+               (floor elapsed 60) (mod elapsed 60)
+               (floor total 60) (mod total 60)
+               (if (bongo-paused-p) "Unpaused:" "Paused:")
+               title album artist)))
+    (bongo-pause/resume))
+
+
+(defun org-player-start/stop (&optional force starting-pos)
+  (interactive)
+  (destructuring-bind (title album artist elapsed total)
+      (org-player-get-track-info)
+    (when title
+      (cond
+       ((eq force 'play)
+        (bongo-play))
+       ((eq force 'stop)
+        (bongo-stop))
+       (t
+        (bongo-start/stop)))
+      (let ((msg (format "%02d:%02d/%02d:%02d %s %s (%s / %s)"
+                         (floor (or starting-pos 0) 60)
+                         (mod (or starting-pos 0) 60)
+                         (floor total 60) (mod total 60)
+                         (if (bongo-playing-p) "Playing:" "Stopped:")
+                         title album artist)))
+        (message msg)))))
+
+
+;; Numpad Ctrl-0: pause/resume
+(define-key org-mode-map (kbd "<C-kp-0>") 'org-player-pause/resume)
+(define-key org-mode-map (kbd "<C-kp-insert>") 'org-player-pause/resume)
+;; Numpad Ctrl-.: stop current track, or restart from beginning if stopped
+(define-key org-mode-map (kbd "<C-kp-decimal>") 'org-player-start/stop)
+(define-key org-mode-map (kbd "<C-kp-delete>") 'org-player-start/stop)
+;; Numpad Ctrl-PgUp, Ctrl-PgDn: raise/lower volume
+(define-key org-mode-map (kbd "<C-kp-prior>") 'volume-raise)
+(define-key org-mode-map (kbd "<C-kp-next>") 'volume-lower)
+(define-key org-mode-map (kbd "<C-kp-9>") 'volume-raise)
+(define-key org-mode-map (kbd "<C-kp-3>") 'volume-lower)
+;; Numpad Ctrl-left, Ctrl-right: skip back/forward 10 seconds
+(define-key org-mode-map (kbd "<C-kp-left>") 'bongo-seek-backward-10)
+(define-key org-mode-map (kbd "<C-kp-right>") 'bongo-seek-forward-10)
+(define-key org-mode-map (kbd "<C-kp-4>") 'bongo-seek-backward-10)
+(define-key org-mode-map (kbd "<C-kp-6>") 'bongo-seek-forward-10)
+;; Ctrl-/: show track info
+(define-key org-mode-map (kbd "<C-kp-divide>") 'org-player-print-track-info)
+
+
+(provide 'org-player)

+ 8 - 86
org-hacks.org

@@ -2195,90 +2195,12 @@ Dirk-Jan C.Binnema [[http://article.gmane.org/gmane.emacs.orgmode/27684/][provid
 
 ** Audio/video file playback within org mode
 
-From Paul Sexton, 2011-06-10
-
-While learning a language, I wanted to include hyperlinks to audio files within
-my org document, and to be able to play each file by clicking on its link.
-
-I eventually discovered the variable =org-file-apps= which allows you to
-associate particular applications with particular file types.
-
-I use [[https://github.com/dbrock/bongo][Bongo]] as the Emacs interface to the media player.  EMMS is another
-actively developed media player, but setup looked too complicated at first
-glance.
-
-I use [[http://www.mplayerhq.hu/design7/news.html][MPlayer]] as the actual media player. This supports almost all audio and
-video file formats. Most importantly, it works on Windows as well as on Linux
-(VLC has a Windows port but it doesn't work with Bongo as the 'fake-tty'
-interface is not implemented on Windows.)
-
-My current setup means that clicking on a link such as
-
-: [[file:song.mp3]]
-
-adds it to the active Bongo playlist (in another buffer) and immediately
-starts playing it. Playback can be paused, fast-forwarded etc., using
-Bongo commands.
-
-When Bongo plays a file it puts some icons in the modeline that resemble the
-well-known symbols for 'play', 'stop', 'rewind' and so on, and which can be
-used to control playback using the mouse. I found these worked erratically when
-outside the actual Bongo playlist buffer, so I have instead bound some org-mode
-keys (ctrl + numpad keys) to the relevant functions.  This part of the setup is
-optional of course.
-
-I have only tested this with =.mp3= files, but it ought to work with
-video as well, as both MPlayer and Bongo claim to work with video files.
-
-The setup follows:
-
-#+BEGIN_SRC emacs-lisp
-;;; Part 1. Bongo setup
-
-(add-to-list 'load-path "/path/to/bongo"))
-(require 'bongo)
-
-(setq bongo-mplayer-program-name
-      (case system-type
-        ((windows-nt cygwin) "c:\\Program Files\\MPlayer for Windows\\MPlayer.exe")
-        (t "mplayer")))
-
-(setq bongo-enabled-backends '(mplayer))
-
-;;; Part 2. Org setup
-
-(defvar av-file-regex
-  (concat "\\." (regexp-opt
-                 (append bongo-audio-file-name-extensions
-                         bongo-video-file-name-extensions)) "$"))
-
-(add-to-list 'org-file-apps
-             (cons av-file-regex '(org-play-media-file file)))
-
-(defun org-play-media-file (filename)
-  (with-bongo-buffer
-    (bongo-insert-file filename)
-    (backward-char)
-    (bongo-play)))
-
-;;; Part 3. Keybindings to allow control of playback within Org buffer
-;;; (optional)
-
-;; Numpad Ctrl-0: pause/resume
-(define-key org-mode-map (kbd "<C-kp-0>") 'bongo-pause/resume)
-(define-key org-mode-map (kbd "<C-kp-insert>") 'bongo-pause/resume)
-;; Numpad Ctrl-.: stop current track, or restart from beginning if stopped
-(define-key org-mode-map (kbd "<C-kp-decimal>") 'bongo-start/stop)
-(define-key org-mode-map (kbd "<C-kp-delete>") 'bongo-start/stop)
-;; Numpad Ctrl-PgUp, Ctrl-PgDn: raise/lower volume
-(define-key org-mode-map (kbd "<C-kp-prior>") 'bongo-volume-raise)
-(define-key org-mode-map (kbd "<C-kp-next>") 'bongo-volume-lower)
-(define-key org-mode-map (kbd "<C-kp-9>") 'bongo-volume-raise)
-(define-key org-mode-map (kbd "<C-kp-3>") 'bongo-volume-lower)
-;; Numpad Ctrl-left, Ctrl-right: skip back/forward 10 seconds
-(define-key org-mode-map (kbd "<C-kp-left>") 'bongo-seek-backward-10)
-(define-key org-mode-map (kbd "<C-kp-right>") 'bongo-seek-forward-10)
-(define-key org-mode-map (kbd "<C-kp-4>") 'bongo-seek-backward-10)
-(define-key org-mode-map (kbd "<C-kp-6>") 'bongo-seek-forward-10)
-#+END_SRC
+Paul Sexton provided code that makes =file:= links to audio or video files
+(MP3, WAV, OGG, AVI, MPG, et cetera) play those files using the [[https://github.com/dbrock/bongo][Bongo]] Emacs
+media player library. The user can pause, skip forward and backward in the
+track, and so on from without leaving Emacs. Links can also contain a time
+after a double colon -- when this is present, playback will begin at that
+position in the track.
+
+See the file [[file:code/elisp/org-player.el][org-player.el]]