From a3e5f8f22a5729e791e0a8d497e62d8ddd1ed8c2 Mon Sep 17 00:00:00 2001 From: Tim Vaughan Date: Sat, 7 Dec 2019 22:00:12 +0100 Subject: [PATCH] A lot more robust, status display working. --- emus.el | 208 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 172 insertions(+), 36 deletions(-) diff --git a/emus.el b/emus.el index eb3d566..b03c09d 100644 --- a/emus.el +++ b/emus.el @@ -27,17 +27,27 @@ "Name of (and, optionally, path to) mpg123 binary." :type '(string)) +(defface emus-artist + '((t :inherit font-lock-keyword-face)) + "Face used for artist names in browser.") + +(defface emus-album + '((t :inherit font-lock-function-name-face)) + "Face used for album names in browser.") + +(defface emus-title + '((t :inherit font-lock-string-face)) + "Face used for track titles in browser.") + ;;; Library ;; -(defvar emus-library nil - "Emus audio library.") - (defun emus-get-audio-files () "Get all mp3 files in main emus directory." (directory-files-recursively emus-directory ".*\\.mp3")) -(defvar emus-records nil) +(defvar emus-records nil + "Emus audio library.") (defun emus-make-record (filename tagstr) (let ((artist "") @@ -51,7 +61,7 @@ (found-artist (setq artist found-artist)) (found-album (setq album found-album)) (found-title (setq title found-title))))) - (list artist album title filename))) + (vector artist album title filename nil))) (defun emus-record-artist (record) (elt record 0)) @@ -65,7 +75,16 @@ (defun emus-record-file (record) (elt record 3)) -(defun emus-update-records (then) +(defun emus-record-browser-pos (record) + (elt record 4)) + +(defun emus-set-record-browser-pos (record pos) + (aset record 4 pos)) + +(defun emus-update-records () + (interactive) + (emus--suspend-cp) + (setq emus-state 'stopped) (let ((proc (emus-get-process)) (tagstr "") (filenames (emus-get-audio-files))) @@ -81,9 +100,23 @@ (if filenames (emus-send-cmd "lp" (car filenames)) (set-process-filter proc nil) - (funcall then))))) + (setq emus-records (reverse emus-records)) + (emus-sort-records) + (emus-render-records) + (emus--resume-cp))))) (emus-send-cmd "lp" (car filenames)))) +(defun emus-sort-records () + (sort emus-records + (lambda (r1 r2) + (let ((artist1 (emus-record-artist r1)) + (artist2 (emus-record-artist r2))) + (if (string= artist1 artist2) + (let ((album1 (emus-record-album r1)) + (album2 (emus-record-album r2))) + (string< album1 album2)) + (string< artist1 artist2)))))) + ;;; mpg123 process ;; @@ -99,9 +132,13 @@ nil)))) (if emus-process emus-process - (make-process :name "emus-process" - ;; :buffer (get-buffer-create "*emus-process*") - :command `(,emus-mpg123-program "-R"))))) + (let ((proc + (make-process :name "emus-process" + ;; :buffer (get-buffer-create "*emus-process*") + :command `(,emus-mpg123-program "-R")))) + (process-send-string proc "silence\n") + proc)))) + (defun emus-send-cmd (cmd &rest args) (process-send-string (emus-get-process) @@ -109,33 +146,71 @@ (seq-reduce (lambda (s1 s2) (concat s1 " " s2)) args cmd) "\n"))) +(defun emus-send-and-process (respfun predfun cmd &rest args) + (let ((respstr "")) + (set-process-filter (emus-get-process) + (lambda (proc string) + (setq respstr (concat respstr string)) + (when (funcall predfun respstr) + (set-process-filter proc nil) + (funcall respfun respstr)))) + (apply #'emus-send-cmd cmd args))) + + ;;; Playback ;; -(defvar emus-currently-playing nil) +(defvar emus-current-record nil) +(defvar emus-state 'stopped) +(defvar emus-continuous-playback t) + +(defun emus--suspend-cp () + (setq emus-continuous-playback nil)) + +(defun emus--resume-cp () + (setq emus-continuous-playback t) + (set-process-filter (emus-get-process) + (lambda (proc string) + (and emus-continuous-playback + (eq emus-state 'playing) + (string-suffix-p "@P 0\n" string) + (emus-play-next))))) (defun emus-play-record (record) - (setq emus-currently-playing record) - (emus-send-cmd "l" (emus-record-file record))) + (let ((old-record emus-current-record)) + (emus-send-cmd "l" (emus-record-file record)) + (setq emus-state 'playing) + (setq emus-current-record record) + (emus-update-record old-record) + (emus-update-record record) + (emus--resume-cp))) (defun emus-stop () (interactive) - (emus-send-cmd "s") - (setq emus-currently-playing nil)) + (setq emus-state 'stopped) + (emus-update-record emus-current-record) + (emus-send-cmd "s")) (defun emus-playpause () (interactive) - (emus-send-cmd "p")) - - -(defun emus-volume (pct) + (when emus-current-record + (if (eq emus-state 'stopped) + (emus-play-record emus-current-record) + (emus-send-cmd "p") + (pcase emus-state + ((or 'paused 'stopped) (setq emus-state 'playing)) + ('playing (setq emus-state 'paused))) + (unless (eq emus-state 'paused))) + (emus-update-record emus-current-record))) + +(defun emus-set-volume (pct) (emus-send-cmd "v" (number-to-string pct))) -(defvar emus-current-volume 10) +(defvar emus-current-volume 100) (defun emus-volume-delta (delta) (setq emus-current-volume (max 0 (min 100 (+ emus-current-volume delta)))) - (emus-volume emus-current-volume)) + (emus-set-volume emus-current-volume)) (defun emus-volume-up () (interactive) @@ -145,32 +220,87 @@ (interactive) (emus-volume-delta -10)) +(defun emus--play-nearby (offset) + (let ((idx (seq-position emus-records emus-current-record))) + (if idx + (let ((next-record (elt emus-records (+ idx offset)))) + (if next-record + (emus-play-record next-record) + (error "Track does not exist"))) + (error "No track is currently selected.")))) + +(defun emus-play-next () + (interactive) + (emus--play-nearby 1)) + +(defun emus-play-prev () + (interactive) + (emus--play-nearby -1)) + +(defun emus-display-status () + (interactive) + (message + (concat "Emus: Volume %d%%" + (pcase emus-state + ('stopped " [Stopped]") + ('paused " [Paused]") + ('playing " [Playing]") + (_ "")) + (if emus-current-record + (format " - %.30s (%.20s)" + (emus-record-title emus-current-record) + (emus-record-artist emus-current-record)) + "")) + emus-current-volume)) + ;;; Browser ;; -(defun emus-render-record (record) +(defun emus-insert-record (record &optional pref-record) + (emus-set-record-browser-pos record (point)) + (let ((current (equal record emus-current-record))) (insert-text-button (concat + (if current + (pcase emus-state + ('playing (propertize ">" 'face 'bold)) + ('paused (propertize ")" 'face 'bold)) + ('stopped (propertize "]" 'face 'bold))) + (propertize " " 'face 'default)) (propertize (format "%-20.20s" (emus-record-artist record)) - 'face 'font-lock-keyword-face) + 'face 'emus-artist) (propertize (format "% -20.20s" (emus-record-album record)) - 'face 'font-lock-function-name-face) + 'face 'emus-album) (propertize (format " %s" (emus-record-title record)) - 'face 'font-lock-string-face)) + 'face 'emus-title)) 'action #'emus-click-record 'follow-link t - 'emus-record record) + 'emus-record record)) (insert "\n")) +(defun emus-update-record (record) + (let ((record-pos (emus-record-browser-pos record))) + (when (and (get-buffer "*emus*") + (emus-record-browser-pos record)) + (with-current-buffer "*emus*" + (let ((inhibit-read-only t) + (old-point (point))) + (goto-char record-pos) + (search-forward "\n") + (delete-region record-pos (point)) + (goto-char record-pos) + (emus-insert-record record) + (goto-char old-point)))))) + (defun emus-render-records () - (with-current-buffer "*emus*" - (let ((inhibit-read-only t)) - (save-excursion + (save-mark-and-excursion + (with-current-buffer "*emus*" + (let ((inhibit-read-only t)) (erase-buffer) (goto-char (point-min)) (dolist (record emus-records) - (emus-render-record record)))))) + (emus-insert-record record)))))) (defun emus-click-record (button) (emus-play-record (button-get button 'emus-record))) @@ -179,34 +309,40 @@ "Switch to *emus* audio library browser." (interactive) (switch-to-buffer "*emus*") - (emus-mode) + (emus-browser-mode) (emus-volume emus-current-volume) (if emus-records (emus-render-records) - (emus-update-records #'emus-render-records))) + (emus-update-records))) -(defvar emus-mode-map +(defvar emus-browser-mode-map (let ((map (make-sparse-keymap))) (define-key map (kbd "SPC") 'emus-playpause) (define-key map (kbd "o") 'emus-stop) (define-key map (kbd "+") 'emus-volume-up) (define-key map (kbd "=") 'emus-volume-up) (define-key map (kbd "-") 'emus-volume-down) + (define-key map (kbd "R") 'emus-update-records) + (define-key map (kbd "n") 'emus-play-next) + (define-key map (kbd "p") 'emus-play-prev) (when (fboundp 'evil-define-key*) (evil-define-key* 'motion map (kbd "SPC") 'emus-playpause (kbd "o") 'emus-stop (kbd "+") 'emus-volume-up (kbd "=") 'emus-volume-up - (kbd "-") 'emus-volume-down)) + (kbd "-") 'emus-volume-down + (kbd "R") 'emus-update-records + (kbd "n") 'emus-play-next + (kbd "p") 'emus-play-prev)) map) "Keymap for emus.") -(define-derived-mode emus-mode special-mode "Emus" +(define-derived-mode emus-browser-mode special-mode "emus-browser" "Major mode for EMUS music player.") (when (fboundp 'evil-set-initial-state) - (evil-set-initial-state 'emus-mode 'motion)) + (evil-set-initial-state 'emus-browser-mode 'motion)) ;;; Debugging -- 2.20.1