X-Git-Url: https://thelambdalab.xyz/gitweb/index.cgi?p=emus.git;a=blobdiff_plain;f=emus.el;h=eb3d56645536179ce2aa3a00d21cb84ef3941ca7;hp=d53f4309bcd0e3ea3960099bce27fa1308c7ca34;hb=6efc84e4c57ecfddcc3d8fcb0327c55af0e1536b;hpb=d37e9f92dc92c6fe471691e1214db0615cc70199 diff --git a/emus.el b/emus.el index d53f430..eb3d566 100644 --- a/emus.el +++ b/emus.el @@ -1,18 +1,213 @@ -;;; emus.el --- Simple music player for Emacs +;;; emus.el --- Simple music player for Emacs. -*- lexical-binding:t -*- -;; Author: T. G. Vaughan +;; Author: Tim Vaughan ;; Version: 1.0 ;; Keywords: multimedia -;; URL: http://github.com/tgvaughan/emus +;; URL: https://thelambdalab.xyz/emus -;; Commentary: +;;; Commentary: ;; This is a simple package for playing audio from a local library ;; of audio files. -;;;###autoload -(defun emus () - "Switch to *emus* audio library buffer." +;;; Code: + +;;; Customizations +;; + +(defgroup emus nil + "Simple music player for Emacs." + :group 'multimedia) + +(defcustom emus-directory "~/Music/" + "Directory containing audio files for emus." + :type '(string)) + +(defcustom emus-mpg123-program "mpg123" + "Name of (and, optionally, path to) mpg123 binary." + :type '(string)) + +;;; 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) + +(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))))) + (list artist album title filename))) + +(defun emus-record-artist (record) + (elt record 0)) + +(defun emus-record-album (record) + (elt record 1)) + +(defun emus-record-title (record) + (elt record 2)) + +(defun emus-record-file (record) + (elt record 3)) + +(defun emus-update-records (then) + (let ((proc (emus-get-process)) + (tagstr "") + (filenames (emus-get-audio-files))) + (setq emus-records nil) + (set-process-filter proc (lambda (proc string) + (setq tagstr (concat tagstr string)) + (when (string-suffix-p "@P 1\n" string) + (add-to-list 'emus-records + (emus-make-record (car filenames) + tagstr)) + (setq tagstr "") + (setq filenames (cdr filenames)) + (if filenames + (emus-send-cmd "lp" (car filenames)) + (set-process-filter proc nil) + (funcall then))))) + (emus-send-cmd "lp" (car filenames)))) + +;;; mpg123 process +;; + +(defvar emus-proc-in-use nil) + +(defun emus-get-process () + "Return current or new mpg123 process." + (let* ((emus-process-raw (get-process "emus-process")) + (emus-process (if emus-process-raw + (if (process-live-p emus-process-raw) + emus-process-raw + (kill-process emus-process-raw) + nil)))) + (if emus-process + emus-process + (make-process :name "emus-process" + ;; :buffer (get-buffer-create "*emus-process*") + :command `(,emus-mpg123-program "-R"))))) + +(defun emus-send-cmd (cmd &rest args) + (process-send-string (emus-get-process) + (concat + (seq-reduce (lambda (s1 s2) (concat s1 " " s2)) args cmd) + "\n"))) + +;;; Playback +;; + +(defvar emus-currently-playing nil) + +(defun emus-play-record (record) + (setq emus-currently-playing record) + (emus-send-cmd "l" (emus-record-file record))) + +(defun emus-stop () + (interactive) + (emus-send-cmd "s") + (setq emus-currently-playing nil)) + +(defun emus-playpause () + (interactive) + (emus-send-cmd "p")) + + +(defun emus-volume (pct) + (emus-send-cmd "v" (number-to-string pct))) + +(defvar emus-current-volume 10) + +(defun emus-volume-delta (delta) + (setq emus-current-volume (max 0 (min 100 (+ emus-current-volume delta)))) + (emus-volume emus-current-volume)) + +(defun emus-volume-up () + (interactive) + (emus-volume-delta 10)) + +(defun emus-volume-down () + (interactive) + (emus-volume-delta -10)) + + +;;; Browser +;; + +(defun emus-render-record (record) + (insert-text-button + (concat + (propertize (format "%-20.20s" (emus-record-artist record)) + 'face 'font-lock-keyword-face) + (propertize (format "% -20.20s" (emus-record-album record)) + 'face 'font-lock-function-name-face) + (propertize (format " %s" (emus-record-title record)) + 'face 'font-lock-string-face)) + 'action #'emus-click-record + 'follow-link t + 'emus-record record) + (insert "\n")) + +(defun emus-render-records () + (with-current-buffer "*emus*" + (let ((inhibit-read-only t)) + (save-excursion + (erase-buffer) + (goto-char (point-min)) + (dolist (record emus-records) + (emus-render-record record)))))) + +(defun emus-click-record (button) + (emus-play-record (button-get button 'emus-record))) + +(defun emus-browse () + "Switch to *emus* audio library browser." (interactive) (switch-to-buffer "*emus*") - (emus-mode)) + (emus-mode) + (emus-volume emus-current-volume) + (if emus-records + (emus-render-records) + (emus-update-records #'emus-render-records))) + +(defvar emus-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) + (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)) + map) + "Keymap for emus.") + +(define-derived-mode emus-mode special-mode "Emus" + "Major mode for EMUS music player.") + +(when (fboundp 'evil-set-initial-state) + (evil-set-initial-state 'emus-mode 'motion)) + +;;; Debugging + +;;; emus.el ends here