From 4c17fed4d674659349b4bf2e57750e99ee41e7e3 Mon Sep 17 00:00:00 2001 From: Tim Vaughan Date: Sun, 8 Dec 2019 20:56:01 +0100 Subject: [PATCH] Decoupled browser from playback. --- emus.el | 250 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 153 insertions(+), 97 deletions(-) diff --git a/emus.el b/emus.el index e145fd4..ad7a5bd 100644 --- a/emus.el +++ b/emus.el @@ -1,17 +1,53 @@ -;;; emus.el --- Simple music player for Emacs. -*- lexical-binding:t -*- +;;; emus.el --- Simple mp3 player -*- lexical-binding:t -*- + +;; Copyright (C) 2019 Tim Vaughan ;; Author: Tim Vaughan +;; Created: 12 December 2019 ;; Version: 1.0 ;; Keywords: multimedia -;; URL: https://thelambdalab.xyz/emus +;; Homepage: http://thelambdalab.xy/emus +;; Package-Requires: ((emacs "26")) + +;; This file is not part of GNU Emacs. + +;; This program 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 of the License, or +;; (at your option) any later version. + +;; This program 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 this file. If not, see . ;;; Commentary: -;; This is a simple package for playing audio from a local library -;; of audio files. +;; This is a simple package for playing audio from a local directory +;; tree of mp3 files. It uses the program mpg123 as its back-end. +;; Currently the library is loaded completely every time emus starts. ;;; Code: +(provide 'emus) + + +;;; Dependencies +;; + +(require 'seq) + + +;;; Global constants +;; + +(defconst emus-version "1.0.0" + "Current version of emus.") + + ;;; Customizations ;; @@ -57,19 +93,8 @@ (defvar emus-records nil "Emus audio library.") -(defun emus-make-record (filename tagstr) - (let ((artist "") - (album "") - (title "")) - (dolist (line (split-string tagstr "\n")) - (let ((found-artist (elt (split-string line "@I ID3v2.artist:") 1)) - (found-album (elt (split-string line "@I ID3v2.album:") 1)) - (found-title (elt (split-string line "@I ID3v2.title:") 1))) - (cond - (found-artist (setq artist found-artist)) - (found-album (setq album found-album)) - (found-title (setq title found-title))))) - (vector artist album title filename nil))) +(defun emus-make-record (artist album title filename &optional pos) + (vector artist album title filename pos)) (defun emus-record-artist (record) (elt record 0)) @@ -89,9 +114,8 @@ (defun emus-set-record-browser-pos (record pos) (aset record 4 pos)) -(defun emus-update-records () - (interactive) - (emus-suspend-cp) +(defun emus--load-library (then) + (emus--suspend-cp) (setq emus-state 'stopped) (let ((proc (emus-get-process)) (tagstr "") @@ -101,20 +125,37 @@ (setq tagstr (concat tagstr string)) (when (string-suffix-p "@P 1\n" string) (add-to-list 'emus-records - (emus-make-record (car filenames) - tagstr)) + (emus--make-record-from-tagstr (car filenames) + tagstr)) (setq tagstr "") (setq filenames (cdr filenames)) (if filenames (emus-send-cmd "lp" (car filenames)) (set-process-filter proc nil) (setq emus-records (reverse emus-records)) - (emus-sort-records) - (emus-render-records) - (emus-resume-cp))))) + (emus--sort-records) + (unless emus-current-record + (setq emus-current-record (car emus-records))) + (funcall then) + ;; (emus-render-records) + (emus--resume-cp))))) (emus-send-cmd "lp" (car filenames)))) -(defun emus-sort-records () +(defun emus--make-record-from-tagstr (filename tagstr) + (let ((artist "") + (album "") + (title "")) + (dolist (line (split-string tagstr "\n")) + (let ((found-artist (elt (split-string line "@I ID3v2.artist:") 1)) + (found-album (elt (split-string line "@I ID3v2.album:") 1)) + (found-title (elt (split-string line "@I ID3v2.title:") 1))) + (cond + (found-artist (setq artist found-artist)) + (found-album (setq album found-album)) + (found-title (setq title found-title))))) + (emus-make-record artist album title filename nil))) + +(defun emus--sort-records () (sort emus-records (lambda (r1 r2) (let ((artist1 (emus-record-artist r1)) @@ -123,7 +164,13 @@ (let ((album1 (emus-record-album r1)) (album2 (emus-record-album r2))) (string< album1 album2)) - (string< artist1 artist2)))))) + (string< artist1 artist2)))))) + +(defmacro emus--with-library (&rest args) + `(if emus-records + (progn ,@args) + (emus--load-library + (lambda () ,@args)))) ;;; mpg123 process ;; @@ -172,10 +219,10 @@ (defvar emus-state 'stopped) (defvar emus-continuous-playback t) -(defun emus-suspend-cp () +(defun emus--suspend-cp () (setq emus-continuous-playback nil)) -(defun emus-resume-cp () +(defun emus--resume-cp () (setq emus-continuous-playback t) (set-process-filter (emus-get-process) (lambda (proc string) @@ -186,59 +233,64 @@ (defun emus-play-record (record) "Set RECORD as current and start playing." - (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))) + (emus--with-library + (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-select-record (record) "Set RECORD as current, but do not start playing." - (let ((old-record emus-current-record)) - (setq emus-state 'stopped) - (setq emus-current-record record) - (emus-update-record old-record) - (emus-update-record record) - (emus-send-cmd "o") - (emus-resume-cp))) + (emus--with-library + (let ((old-record emus-current-record)) + (setq emus-state 'stopped) + (setq emus-current-record record) + (emus--update-record old-record) + (emus--update-record record) + (emus-send-cmd "o") + (emus--resume-cp)))) (defun emus-stop () "Stop playback of the current record." (interactive) - (setq emus-state 'stopped) - (emus-update-record emus-current-record) - (emus-send-cmd "s")) + (emus--with-library + (setq emus-state 'stopped) + (emus--update-record emus-current-record) + (emus-send-cmd "s"))) (defun emus-playpause () (interactive) - (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))) + (emus--with-library + (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)))) (defvar emus-current-volume 100) -(defun emus-volume-delta (delta) - (setq emus-current-volume (max 0 (min 100 (+ emus-current-volume delta)))) - (emus-set-volume emus-current-volume)) +(defun emus-set-volume (pct) + (emus--with-library + (setq emus-current-volume pct) + (emus-send-cmd "v" (number-to-string pct)))) + +(defun emus-volume-increase-by (delta) + (emus-set-volume (max 0 (min 100 (+ emus-current-volume delta))))) (defun emus-volume-up () (interactive) - (emus-volume-delta 10)) + (emus-volume-increase-by 10)) (defun emus-volume-down () (interactive) - (emus-volume-delta -10)) + (emus-volume-increase-by -10)) (defun emus-play-nearby (offset) (let ((idx (seq-position emus-records emus-current-record))) @@ -249,7 +301,7 @@ (emus-play-record next-record) (emus-select-record next-record)) (error "Track does not exist"))) - (error "No track is currently selected.")))) + (error "No track is currently selected")))) (defun emus-play-next () (interactive) @@ -261,25 +313,26 @@ (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)) + (emus--with-library + (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-insert-record (record &optional prev-record first) +(defun emus--insert-record (record &optional prev-record first) (let* ((artist (emus-record-artist record)) (album (emus-record-album record)) (title (emus-record-title record)) @@ -288,7 +341,7 @@ (unless (equal (emus-record-artist prev-record) artist) (insert-text-button (propertize artist 'face 'emus-artist) - 'action #'emus-click-record + 'action #'emus--click-record 'follow-link t 'help-echo help-str 'emus-record record) @@ -296,7 +349,7 @@ (unless (equal (emus-record-album prev-record) album) (insert-text-button (propertize (concat " " album) 'face 'emus-album) - 'action #'emus-click-record + 'action #'emus--click-record 'follow-link t 'help-echo help-str 'emus-record record) @@ -317,7 +370,7 @@ 'face (if is-current 'emus-track-current 'emus-track))) - 'action #'emus-click-record + 'action #'emus--click-record 'follow-link t 'help-echo help-str 'emus-record record) @@ -326,7 +379,7 @@ 'emus-track-current 'emus-track)))))) -(defun emus-update-record (record) +(defun emus--update-record (record) (let ((record-pos (emus-record-browser-pos record))) (when (and (get-buffer "*emus*") (emus-record-browser-pos record)) @@ -337,10 +390,10 @@ (search-forward "\n") (delete-region record-pos (point)) (goto-char record-pos) - (emus-insert-record record) + (emus--insert-record record) (goto-char old-point)))))) -(defun emus-render-records () +(defun emus--render-records () (with-current-buffer "*emus*" (let ((inhibit-read-only t) (old-pos (point))) @@ -348,11 +401,11 @@ (goto-char (point-min)) (let ((prev-record nil)) (dolist (record emus-records) - (emus-insert-record record prev-record (not prev-record)) + (emus--insert-record record prev-record (not prev-record)) (setq prev-record record))) (goto-char old-pos)))) -(defun emus-click-record (button) +(defun emus--click-record (button) (emus-play-record (button-get button 'emus-record)) (emus-display-status)) @@ -367,18 +420,23 @@ (defun emus-browse () "Switch to *emus* audio library browser." (interactive) - (switch-to-buffer "*emus*") - (emus-browser-mode) - (emus-volume emus-current-volume) - (if emus-records - (emus-render-records) - (emus-update-records))) + (emus--with-library + (switch-to-buffer "*emus*") + (emus-browser-mode) + (emus--render-records) + (emus-centre-current))) + +(defun emus-refresh () + (interactive) + (emus-stop) + (setq emus-records nil) + (emus-browse)) (defun emus-playpause-status () (interactive) (emus-playpause) (emus-display-status)) (defun emus-stop-status () (interactive) (emus-stop) (emus-display-status)) (defun emus-volume-up-status () (interactive) (emus-volume-up) (emus-display-status)) (defun emus-volume-down-status () (interactive) (emus-volume-down) (emus-display-status)) -(defun emus-update-records-status () (interactive) (emus-update-records) (emus-display-status)) +(defun emus-refresh-status () (interactive) (emus-refresh) (emus-display-status)) (defun emus-play-next-status () (interactive) (emus-play-next) (emus-display-status)) (defun emus-play-prev-status () (interactive) (emus-play-prev) (emus-display-status)) (defun emus-centre-current-status () (interactive) (emus-centre-current) (emus-display-status)) @@ -390,7 +448,7 @@ (define-key map (kbd "+") 'emus-volume-up-status) (define-key map (kbd "=") 'emus-volume-up-status) (define-key map (kbd "-") 'emus-volume-down-status) - (define-key map (kbd "R") 'emus-update-records-status) + (define-key map (kbd "R") 'emus-refresh-status) (define-key map (kbd "n") 'emus-play-next-status) (define-key map (kbd "p") 'emus-play-prev-status) (define-key map (kbd "c") 'emus-centre-current-status) @@ -401,7 +459,7 @@ (kbd "+") 'emus-volume-up-status (kbd "=") 'emus-volume-up-status (kbd "-") 'emus-volume-down-status - (kbd "R") 'emus-update-records-status + (kbd "R") 'emus-refresh-status (kbd "n") 'emus-play-next-status (kbd "p") 'emus-play-prev-status (kbd "c") 'emus-centre-current-status)) @@ -414,6 +472,4 @@ (when (fboundp 'evil-set-initial-state) (evil-set-initial-state 'emus-browser-mode 'motion)) -;;; Debugging - ;;; emus.el ends here -- 2.20.1