From 395d69cb965b6905b7b87b6f8ae062dfe3423c56 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Thu, 15 Jun 2006 06:30:00 +0000 Subject: add player-mpg321-remote - allows seeking in files - handles files with errors in them (the mpg321 simple version skips over the files) darcs-hash:20060615063027-4e3e3-55f9b446189c86894879167259604cda28fe5215.gz --- NEWS | 7 +- emms-player-mpg321-remote.el | 201 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 emms-player-mpg321-remote.el diff --git a/NEWS b/NEWS index 19b9a4a..d2e5e10 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,8 @@ News since version 2: + - A new player that uses mpg321's remote mode is now available + (emms-player-mpg321-remote) - this allows seeking and copes with + errors in files. - A metadata browser has been added in emms-browser.el. - Recording of the time a track was last played is now supported. - emms-play-* and emms-add-* functions now toggle their play/add @@ -10,5 +13,7 @@ News since version 2: - When playing a playlist from emms-playlist-mode, EMMS now optionally opens a new buffer for this playlist. - User-visible bug fixes: +User-visible bug fixes: + - The emms-playlist-mode is now much faster + - MP3s with errors are now playable when using the remote player. diff --git a/emms-player-mpg321-remote.el b/emms-player-mpg321-remote.el new file mode 100644 index 0000000..6b7790f --- /dev/null +++ b/emms-player-mpg321-remote.el @@ -0,0 +1,201 @@ +;;; emms-player-mpg321-remote.el --- play files with mpg321 -R + +;; Copyright (C) 2006 Damien Elmes + +;; Author: Damien Elmes +;; Keywords: emms, mp3, mpeg, multimedia + +;; This file 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 2, or (at your option) +;; any later version. + +;; This file 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., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;; This file provides an emms-player which uses mpg321's remote mode +;; to play files. This is a persistent process which isn't killed each +;; time a new file is played. + +;; The remote process copes graciously with errors in music files, and +;; allows you to seek in files. + +;; To enable this code, add the following to your emacs configuration: + +;; (require 'emms-player-mpg321-remote) +;; (push 'emms-player-mpg321-remote emms-player-list) + +;;; Code: + +(require 'emms) +(require 'emms-player-simple) + +;; -------------------------------------------------- +;; Variables and configuration +;; -------------------------------------------------- + +(defgroup emms-player-mpg321-remote nil + "*EMMS player using mpg321's remote mode." + :group 'emms-player + :prefix "emms-player-mpg321-remote") + +(defcustom emms-player-mpg321-remote-command "mpg321" + "*The command name of mpg321." + :type 'string + :group 'emms-player-mpg321-remote) + +(defcustom emms-player-mpg321-remote-parameters nil + "*Extra arguments to pass to mpg321 when using remote mode +For example: (list \"-o\" \"alsa\")" + :type '(repeat string) + :group 'emms-player-mpg321-remote) + +(defcustom emms-player-mpg321-remote + (emms-player 'emms-player-mpg321-remote-start-playing + 'emms-player-mpg321-remote-stop-playing + 'emms-player-mpg321-remote-playable-p) + "*A player for EMMS." + :type '(cons symbol alist) + :group 'emms-player-mpg321-remote) + +(defvar emms-player-mpg321-remote-initial-args + (list "--skip-printing-frames=10" "-R" "-") + "Initial args to pass to the mpg321 process.") + +(defvar emms-player-mpg321-remote-process-name "emms-player-mpg321-remote-proc" + "The name of the mpg321 remote player process") + +(defmacro emms-player-mpg321-remote-add (cmd func) + `(emms-player-set 'emms-player-mpg321-remote + ,cmd ,func)) + +(emms-player-mpg321-remote-add + 'regex (emms-player-simple-regexp "mp3" "mp2")) +(emms-player-mpg321-remote-add + 'pause 'emms-player-mpg321-remote-pause) +(emms-player-mpg321-remote-add + 'resume 'emms-player-mpg321-remote-pause) +(emms-player-mpg321-remote-add + 'seek 'emms-player-mpg321-remote-seek) + +;; -------------------------------------------------- +;; Process maintenence +;; -------------------------------------------------- + +(defun emms-player-mpg321-remote-start-process () + "Start a new remote process, and return the process." + (let ((process (apply 'start-process + emms-player-mpg321-remote-process-name + nil + emms-player-mpg321-remote-command + (append emms-player-mpg321-remote-initial-args + emms-player-mpg321-remote-parameters)))) + (set-process-sentinel process 'emms-player-mpg321-remote-sentinel) + (set-process-filter process 'emms-player-mpg321-remote-filter) + process)) + +(defun emms-player-mpg321-remote-stop () + "Stop the currently playing process, if indeed there is one" + (let ((process (emms-player-mpg321-remote-process))) + (when process + (kill-process process) + (delete-process process)))) + +(defun emms-player-mpg321-remote-process () + "Return the remote process, if it exists." + (get-process emms-player-mpg321-remote-process-name)) + +(defun emms-player-mpg321-remote-running-p () + "True if the remote process exists and is running." + (let ((proc (emms-player-mpg321-remote-process))) + (and proc + (eq (process-status proc) 'run)))) + +(defun emms-player-mpg321-remote-sentinel (proc str) + "Sentinel for determining the end of process" + (when (or (eq (process-status proc) 'exit) + (eq (process-status proc) 'signal)) + (emms-player-stopped))) + +(defun emms-player-mpg321-remote-send (text) + "Send TEXT to the mpg321 remote process, and add a newline." + (let ((proc (emms-player-mpg321-remote-process))) + ;; we shouldn't be trying to send to a dead process + (assert proc) + (process-send-string proc (concat text "\n")))) + +;; -------------------------------------------------- +;; Interfacing with emms +;; -------------------------------------------------- + +(defun emms-player-mpg321-remote-filter (proc str) + (let* ((data (split-string str)) + (cmd (car data))) + (cond + ;; stop notice + ((and (string= cmd "@P") + (string= (cadr data) "0")) + (if emms-player-mpg321-remote-mask-stop-message + (setq emms-player-mpg321-remote-mask-stop-message + nil) + (emms-player-stopped))) + ;; frame notice + ((string= cmd "@F") + ;; even though a timer is constantly updating this variable, + ;; updating it here will cause it to stay pretty much in sync. + (setq emms-playing-time + (truncate (string-to-number (nth 3 data)))))))) + +(defun emms-player-mpg321-remote-start-playing (track) + "Start playing a song by telling the remote process to play it. +If the remote process is not running, launch it." + (unless (emms-player-mpg321-remote-running-p) + (emms-player-mpg321-remote-start-process)) + (emms-player-mpg321-remote-play-track track)) + +(defun emms-player-mpg321-remote-stop-playing () + "Stop the current song playing." + ;; we don't want to tell emms we've stopped playing, or it'll start + ;; playing the next track + (setq emms-player-mpg321-remote-mask-stop-message t) + (emms-player-mpg321-remote-send "stop")) + +(defun emms-player-mpg321-remote-play-track (track) + "Send a play command to the remote, based on TRACK." + (emms-player-mpg321-remote-send + (concat "load " (emms-track-get track 'name))) + (emms-player-started 'emms-player-mpg321-remote)) + +(defun emms-player-mpg321-remote-playable-p (track) + ;; use the simple definition. + (emms-player-mpg321-playable-p track)) + +(defun emms-player-mpg321-remote-pause () + "Pause the player." + (emms-player-mpg321-remote-send "pause")) + +(defun emms-player-mpg321-remote-resume () + "Resume the player." + (emms-player-mpg321-remote-send "pause")) + +(defun emms-player-mpg321-remote-seek (seconds) + "Seek forward or backward in the file." + ;; since mpg321 only supports seeking by frames, not seconds, we + ;; make a very rough guess as to how much a second constitutes + (let ((frame-string (number-to-string (* 35 seconds)))) + ;; if we're not going backwards, we need to add a '+' + (unless (eq ?- (string-to-char frame-string)) + (setq frame-string (concat "+" frame-string))) + (emms-player-mpg321-remote-send (concat "jump " frame-string)))) + +(provide 'emms-player-mpg321-remote) +;;; emms-player-mpg321-remote.el ends here -- cgit v1.2.3