Added function to restart the emus process.
[emus.git] / emus.el
1 ;;; emus.el --- Simple mp3 player  -*- lexical-binding:t -*-
2
3 ;; Copyright (C) 2019 Tim Vaughan
4
5 ;; Author: Tim Vaughan <timv@ughan.xyz>
6 ;; Created: 8 December 2019
7 ;; Version: 1.0
8 ;; Keywords: multimedia
9 ;; Homepage: http://thelambdalab.xy/emus
10 ;; Package-Requires: ((emacs "26"))
11
12 ;; This file is not part of GNU Emacs.
13
14 ;; This program is free software: you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation, either version 3 of the License, or
17 ;; (at your option) any later version.
18
19 ;; This program is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 ;; GNU General Public License for more details.
23
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with this file.  If not, see <http://www.gnu.org/licenses/>.
26
27 ;;; Commentary:
28
29 ;; This is a simple package for playing audio from a local directory
30 ;; tree of mp3 files.  It uses the program mpg123 as its back-end.
31 ;; Currently the library is loaded completely every time emus starts.
32
33 ;;; Code:
34
35 (provide 'emus)
36
37
38 ;;; Dependencies
39 ;;
40
41 (require 'seq)
42
43
44 ;;; Customizations
45 ;;
46
47 (defgroup emus nil
48   "Simple music player for Emacs."
49   :group 'multimedia)
50
51 (defcustom emus-directory "~/Music/"
52   "Directory containing audio files for emus."
53   :type '(string))
54
55 (defcustom emus-mpg123-program "mpg123"
56   "Name of (and, optionally, path to) mpg123 binary."
57   :type '(string))
58
59 (defface emus-artist
60   '((((background dark)) :inherit font-lock-string-face :background "#222" :extend t)
61     (t :inherit font-lock-string-face :background "#ddd" :extend t))
62   "Face used for artist names in browser.")
63
64 (defface emus-album
65   '((((background dark)) :inherit font-lock-constant-face :background "#111" :extend t)
66     (t :inherit font-lock-constant-face :background "#eee" :extend t))
67   "Face used for album names in browser.")
68
69 (defface emus-track
70   '((t :inherit font-lock-keyword-face))
71   "Face used for track titles in browser.")
72
73 (defface emus-track-current
74   '((t :inherit font-lock-keyword-face :inverse-video t :extend t))
75   "Face used for track titles in browser.")
76
77 (defface emus-cursor
78   '((t :inherit bold))
79   "Face used for current track cursor")
80
81
82 ;;; Global variables
83
84 (defvar emus--proc-in-use nil
85   "If non-nil, disables `emus-send-cmd'.
86 Used to prevent commands from interfering with library construction.")
87
88 (defvar emus-tracks nil
89   "Emus audio library.")
90
91 (defvar emus-current-track nil
92   "Currently-selected emus track.")
93
94 (defvar emus-state 'stopped
95   "Current playback state.")
96
97 (defvar emus-continuous-playback t
98   "If non-nil, emus will automatically play the next track when the current track is finished.")
99
100 (defvar emus-current-volume 100
101   "The current playback volume.")
102
103
104 ;;; mpg123 process
105 ;;
106
107
108 (defun emus-get-process ()
109   "Return current or new mpg123 process."
110   (let* ((emus-process-raw (get-process "emus-process"))
111          (emus-process (if emus-process-raw
112                            (if (process-live-p emus-process-raw)
113                                emus-process-raw
114                              (kill-process emus-process-raw)
115                              nil))))
116     (if emus-process
117         emus-process
118       (let ((proc
119              (make-process :name "emus-process"
120                            :command `(,emus-mpg123-program "-R"))))
121         (set-process-query-on-exit-flag proc nil)
122         (process-send-string proc "silence\n")
123         proc))))
124
125 (defun emus--send-cmd-raw (cmd &rest args)
126   "Send a command CMD with args ARGS to the mpg123 process.
127 This procedure does not respect `emus--proc-in-use' and thus should only
128 be used by `emus--load-library'."
129     (process-send-string (emus-get-process)
130                          (concat
131                           (seq-reduce (lambda (s1 s2) (concat s1 " " s2)) args cmd)
132                           "\n")))
133
134 (defun emus-send-cmd (cmd &rest args)
135   "Send a command CMD with args ARGS to the mpg123 process."
136   (unless emus--proc-in-use
137     (apply #'emus--send-cmd-raw cmd args)))
138
139 (defun emus-kill-process ()
140   "Kill any existing mpg123 process."
141   (let ((emus-process (get-process "emus-process")))
142     (if emus-process
143         (kill-process emus-process))))
144
145 (defun emus-restart ()
146   "Restart emus, regardless of current state."
147   (interactive)
148   (emus-kill-process)
149   (setq emus-state 'stopped
150         emus--proc-in-use nil
151         emus-tracks nil))
152
153 ;;; Library
154 ;;
155
156 (defun emus-get-audio-files ()
157   "Get all mp3 files in main emus directory."
158   (mapcar
159    #'expand-file-name
160    (directory-files-recursively emus-directory ".*\\.mp3")))
161
162 (defun emus-get-playlist-files ()
163   "Get all m3u files in the main emus music directory."
164   (mapcar
165    #'expand-file-name
166    (directory-files-recursively emus-directory ".*\\.m3u")))
167
168 (defun emus-make-track (artist album title filename &optional pos)
169   "Create an object representing an emus track.
170 ARTIST, ALBUM and TITLE are used to describe the track, FILENAME
171 refers to the mp3 file containing the track.  If non-nil, POS
172 specifies the position of the record representing this track in the
173 emus browser buffer."
174   (list artist album title filename pos))
175
176 (defun emus-track-artist (track)
177   "The artist corresponding to TRACK."
178   (elt track 0))
179
180 (defun emus-track-album (track)
181   "The album corresponding to TRACK."
182   (elt track 1))
183
184 (defun emus-track-title (track)
185   "The title of TRACK."
186   (elt track 2))
187
188 (defun emus-track-file (track)
189   "The mp3 file corresponding to TRACK."
190   (elt track 3))
191
192 (defun emus-track-browser-pos (track)
193   "The location of the browser buffer record corresponding to TRACK."
194   (elt track 4))
195
196 (defun emus-set-track-browser-pos (track pos)
197   "Set the location of the browser buffer record corresponding to TRACK to POS."
198   (setf (seq-elt track 4) pos))
199
200 (defun emus--load-library (then)
201   "Initialize the emus track library.
202 Once the library is initialized, the function THEN is called."
203   (unless emus--proc-in-use
204     (setq emus--proc-in-use t)
205     (emus--suspend-cp)
206     (setq emus-state 'stopped)
207     (setq emus-tracks nil)
208     (let ((proc (emus-get-process))
209           (tagstr "")
210           (filenames (emus-get-audio-files)))
211       (set-process-filter proc (lambda (proc string)
212                                  (setq tagstr (concat tagstr string))
213                                  (when (string-suffix-p "@P 1\n" string)
214                                    (add-to-list 'emus-tracks
215                                                 (emus--make-track-from-tagstr (car filenames)
216                                                                               tagstr))
217                                    (setq tagstr "")
218                                    (setq filenames (cdr filenames))
219                                    (if filenames
220                                        (emus--send-cmd-raw "lp" (car filenames))
221                                      (set-process-filter proc nil)
222                                      (setq emus-tracks (reverse emus-tracks))
223                                      (emus--sort-tracks)
224                                      (emus--add-tracks-from-playlist-files)
225                                      (unless emus-current-track
226                                        (setq emus-current-track (car emus-tracks)))
227                                      (funcall then)
228                                      (emus--resume-cp)
229                                      (setq emus--proc-in-use nil)))))
230       (emus--send-cmd-raw "lp" (car filenames)))))
231
232 (defun emus--dump-tracks ()
233     emus-tracks)
234
235 (defun emus--make-track-from-tagstr (filename tagstr)
236   "Parse TAGSTR to populate the fields of a track corresponding to FILENAME."
237   (let ((artist "")
238         (album "")
239         (title ""))
240     (dolist (line (split-string tagstr "\n"))
241       (let ((found-artist (elt (split-string line "@I ID3v2.artist:") 1))
242             (found-album (elt (split-string line "@I ID3v2.album:") 1))
243             (found-title (elt (split-string line "@I ID3v2.title:") 1)))
244         (cond
245          (found-artist (setq artist found-artist))
246          (found-album (setq album found-album))
247          (found-title (setq title found-title)))))
248     (emus-make-track artist album title filename)))
249
250 (defun emus--add-tracks-from-playlist-files ()
251   (let ((tracks nil))
252     (dolist (filename (emus-get-playlist-files))
253       (let ((artist "Playlists")
254             (album (file-name-base filename))
255             (title nil)
256             (lines (split-string (with-temp-buffer
257                                    (insert-file-contents filename)
258                                    (buffer-string))
259                                  "\n")))
260         (dolist (line lines)
261           (pcase (string-trim line)
262             ((rx (: string-start
263                     (* space)
264                     "#extinf:"
265                     (* (not ",")) ","
266                     (let display-title (* any))
267                     string-end))
268              (setq title display-title))
269             ((rx (: string-start (* space) "#"))) ;skip other comments
270             ((rx (let filename (+ any)))
271              (setq tracks (cons (emus-make-track artist album (or title filename) filename)
272                                 tracks))
273              (setq title nil))))))
274     (setq emus-tracks (append emus-tracks (reverse tracks)))))
275
276 (defun emus--sort-tracks ()
277   "Sort the library tracks according to artist and album.
278 Leaves the track titles unsorted, so they will appear in the order specified
279 by the filesystem."
280   (setq emus-tracks
281         (sort emus-tracks
282               (lambda (r1 r2)
283                 (let ((artist1 (emus-track-artist r1))
284                       (artist2 (emus-track-artist r2)))
285                   (if (string= artist1 artist2)
286                       (let ((album1 (emus-track-album r1))
287                             (album2 (emus-track-album r2)))
288                         (string< album1 album2))
289                     (string< artist1 artist2)))))))
290
291 (defmacro emus--with-library (&rest body)
292   "Evaluate BODY with the library initialized."
293   `(if emus-tracks
294        (unless emus--proc-in-use ,@body)
295      (emus--load-library
296       (lambda () ,@body))))
297
298
299 ;;; Playback
300 ;;
301
302 (defun emus--suspend-cp ()
303   "Suspend continuous playback."
304   (setq emus-continuous-playback nil))
305
306 (defun emus--resume-cp ()
307   "Resume continuous playback."
308   (setq emus-continuous-playback t)
309   (set-process-filter (emus-get-process)
310                       (lambda (_proc string)
311                         (and emus-continuous-playback
312                              (eq emus-state 'playing)
313                              (string-suffix-p "@P 0\n" string)
314                              (emus-play-next)))))
315
316 (defun emus-play-track (track)
317   "Set TRACK as current and start playing."
318   (emus--with-library
319    (let ((old-track emus-current-track))
320      (emus-send-cmd "l" (emus-track-file track))
321      (setq emus-state 'playing)
322      (setq emus-current-track track)
323      (emus--update-track old-track)
324      (emus--update-track track)
325      (emus--resume-cp)
326      (emus-goto-current))))
327
328 (defun emus-select-track (track)
329   "Set TRACK as current, but do not start playing."
330   (emus--with-library
331    (let ((old-track emus-current-track))
332      (setq emus-state 'stopped)
333      (setq emus-current-track track)
334      (emus--update-track old-track)
335      (emus--update-track track)
336      (emus-send-cmd "o")
337      (emus--resume-cp)
338      (emus-goto-current))))
339
340 (defun emus-stop ()
341   "Stop playback of the current track."
342   (interactive)
343   (emus--with-library
344    (setq emus-state 'stopped)
345    (emus--update-track emus-current-track)
346    (emus-send-cmd "s")))
347
348 (defun emus-playpause ()
349   "Begin playback of the current track.
350 If the track is already playing, pause playback.
351 If the track is currently paused, resume playback."
352   (interactive)
353   (emus--with-library
354    (when emus-current-track
355      (if (eq emus-state 'stopped)
356          (emus-play-track emus-current-track)
357        (emus-send-cmd "p")
358        (pcase emus-state
359          ((or 'paused 'stopped) (setq emus-state 'playing))
360          ('playing (setq emus-state 'paused)))
361        (unless (eq emus-state 'paused)))
362      (emus--update-track emus-current-track))))
363
364 (defun emus-set-volume (pct)
365   "Set the playback volume to PCT %."
366   (emus--with-library
367    (setq emus-current-volume pct)
368    (emus-send-cmd "v" (number-to-string pct))))
369
370 (defun emus-volume-increase-by (delta)
371   "Increase the playback volume by DELTA %."
372   (emus-set-volume (max 0 (min 100 (+ emus-current-volume delta)))))
373
374 (defun emus-volume-up ()
375   "Increase the playback volume by 10%."
376   (interactive)
377   (emus-volume-increase-by 10))
378
379 (defun emus-volume-down ()
380   "Decrease the playback volume by 10%."
381   (interactive)
382   (emus-volume-increase-by -10))
383
384 (defun emus--play-adjacent-track (&optional prev)
385   "Play the next track in the library, or the previous if PREV is non-nil."
386   (emus--with-library
387    (let ((idx (seq-position emus-tracks emus-current-track))
388          (offset (if prev -1 +1)))
389      (if idx
390          (let ((next-track (elt emus-tracks (+ idx offset))))
391            (if next-track
392                (if (eq emus-state 'playing)
393                    (emus-play-track next-track)
394                  (emus-select-track next-track))
395              (error "Track does not exist")))
396        (error "No track selected")))))
397
398 (defun emus--play-adjacent-album (&optional prev)
399   "Play the first track of the next album in the library.
400 If PREV is non-nil, plays the last track of the previous album."
401   (emus--with-library
402    (let ((idx (seq-position emus-tracks emus-current-track)))
403      (if idx
404          (let* ((search-list (if prev
405                                  (reverse (seq-subseq emus-tracks 0 idx))
406                                (seq-subseq emus-tracks (+ idx 1))))
407                 (current-album (emus-track-album emus-current-track))
408                 (next-track (seq-some (lambda (r)
409                                         (if (string= (emus-track-album r)
410                                                      current-album)
411                                             nil
412                                           r))
413                                       search-list)))
414            (if next-track
415                (if (eq emus-state 'playing)
416                    (emus-play-track next-track)
417                  (emus-select-track next-track))
418              (error "Track does not exist")))
419        (error "No track selected")))))
420
421 (defun emus-play-next ()
422   "Play the next track in the library."
423   (interactive)
424   (emus--play-adjacent-track))
425
426 (defun emus-play-prev ()
427   "Play the previous track in the library."
428   (interactive)
429   (emus--play-adjacent-track t))
430
431 (defun emus-play-next-album ()
432   "Play the first track of the next album in the library."
433   (interactive)
434   (emus--play-adjacent-album))
435
436 (defun emus-play-prev-album ()
437   "Play the last track of the previous album in the library."
438   (interactive)
439   (emus--play-adjacent-album t))
440
441 (defun emus-jump (seconds)
442   "Jump forward in current track by SECONDS seconds."
443   (emus--with-library
444    (emus-send-cmd "jump" (format "%+ds" seconds))))
445
446 (defun emus-jump-10s-forward ()
447   "Jump 10 seconds forward in current track."
448   (interactive)
449   (emus-jump 10))
450
451 (defun emus-jump-10s-backward ()
452   "Jump 10 seconds backward in current track."
453   (interactive)
454   (emus-jump -10))
455
456 (defun emus-display-status ()
457   "Display the current playback status in the minibuffer."
458   (interactive)
459   (emus--with-library
460    (message
461     (concat "Emus: Volume %d%%"
462             (pcase emus-state
463               ('stopped " [Stopped]")
464               ('paused " [Paused]")
465               ('playing " [Playing]")
466               (_ ""))
467             " %s")
468     emus-current-volume
469     (if emus-current-track
470         (format "- %.30s (%.20s)"
471                 (emus-track-title emus-current-track)
472                 (emus-track-artist emus-current-track))
473       ""))))
474
475
476 ;;; Browser
477 ;;
478
479 (defun emus--insert-track (track &optional prev-track first)
480   "Insert a button representing TRACK into the current buffer.
481
482 When provided, PREV-TRACK is used to determine whether to insert additional
483 headers representing the artist or the album title.
484
485 If non-nil, FIRST indicates that the track is the first in the library
486 and thus requires both artist and album headers."
487   (let* ((artist (emus-track-artist track))
488          (album (emus-track-album track))
489          (title (emus-track-title track))
490          (album-symb (intern (concat artist album)))
491          (help-str (format "mouse-1, RET: Play '%.30s' (%.20s)" title artist))
492          (field (intern album))) ;Allows easy jumping between albums with cursor.
493     (when (or prev-track first)
494       (unless (equal (emus-track-artist prev-track) artist)
495         (insert-text-button
496          (propertize artist 'face 'emus-artist)
497          'action #'emus--click-track
498          'follow-link t
499          'help-echo help-str
500          'emus-track track
501          'field field)
502         (insert (propertize "\n"
503                             'face 'emus-artist
504                             'field field)))
505       (unless (equal (emus-track-album prev-track) album)
506         (insert-text-button
507          (propertize (concat "  " album) 'face 'emus-album)
508          'action #'emus--click-track
509          'follow-link t
510          'help-echo help-str
511          'emus-track track
512          'field field)
513         (insert (propertize "\n"
514                             'face 'emus-album
515                             'field field))))
516     (emus-set-track-browser-pos track (point))
517     (let ((is-current (equal track emus-current-track)))
518       (insert-text-button
519        (concat
520         (if is-current
521             (propertize
522              (pcase emus-state
523                ('playing "->")
524                ('paused "-)")
525                ('stopped "-]"))
526              'face 'emus-cursor)
527           (propertize "  " 'face 'default))
528         (propertize (format "   %s" title)
529                     'face (if is-current
530                               'emus-track-current
531                             'emus-track)))
532        'action #'emus--click-track
533        'follow-link t
534        'help-echo help-str
535        'emus-track track
536        'invisible album-symb
537        'field field)
538       (insert (propertize "\n"
539                           'face (if is-current
540                                     'emus-track-current
541                                   'emus-track)
542                           'field field
543                           'invisible album-symb)))))
544
545 (defun emus--update-track (track)
546   "Rerender entry for TRACK in emus browser buffer.
547 Used to update browser display when `emus-current-track' and/or `emus-state' changes."
548   (let ((track-pos (emus-track-browser-pos track)))
549     (when (and (get-buffer "*emus*")
550                (emus-track-browser-pos track))
551       (with-current-buffer "*emus*"
552         (let ((inhibit-read-only t)
553               (old-point (point)))
554           (goto-char track-pos)
555           (search-forward "\n")
556           (delete-region track-pos (point))
557           (goto-char track-pos)
558           (emus--insert-track track)
559           (goto-char old-point))))))
560
561 (defun emus--render-tracks ()
562   "Render all library tracks in emus browser buffer."
563   (with-current-buffer "*emus*"
564     (let ((inhibit-read-only t)
565           (old-pos (point)))
566       (erase-buffer)
567       (goto-char (point-min))
568       (let ((prev-track nil))
569         (dolist (track emus-tracks)
570           (emus--insert-track track prev-track (not prev-track))
571           (setq prev-track track)))
572       (goto-char old-pos))))
573
574 (defun emus--click-track (button)
575   "Begin playback of track indicated by BUTTON."
576   (emus-play-track (button-get button 'emus-track))
577   (emus-display-status))
578
579 (defun emus-goto-current ()
580   "Move point to the current track in the browser buffer, if available."
581   (interactive)
582   (when (and (get-buffer "*emus*")
583              emus-current-track)
584     (with-current-buffer "*emus*"
585         (goto-char (emus-track-browser-pos emus-current-track)))))
586
587 (defun emus-browse ()
588   "Switch to *emus* audio library browser."
589   (interactive)
590   (emus--with-library
591    (pop-to-buffer-same-window "*emus*")
592    (emus-browser-mode)
593    (emus--render-tracks)
594    (emus-goto-current)))
595
596 (defun emus-refresh ()
597   "Refresh the emus library."
598   (interactive)
599   (emus-stop)
600   (setq emus-tracks nil)
601   (emus-browse))
602
603
604 ;;; Playback + status display commands
605 ;;
606
607 (defun emus-playpause-status ()
608   "Start, pause or resume playback, then display the emus status in the minibuffer."
609   (interactive)
610   (emus-playpause)
611   (emus-display-status))
612
613 (defun emus-stop-status ()
614   "Stop playback, then display the emus status in the minibuffer."
615   (interactive)
616   (emus-stop)
617   (emus-display-status))
618
619 (defun emus-volume-up-status ()
620   "Increase volume by 10%, then display the emus status in the minibuffer."
621   (interactive)
622   (emus-volume-up)
623   (emus-display-status))
624
625 (defun emus-volume-down-status ()
626   "Decrease volume by 10%, then display the emus status in the minibuffer."
627   (interactive)
628   (emus-volume-down)
629   (emus-display-status))
630
631 (defun emus-play-next-status ()
632   "Play next track, then display the emus status in the minibuffer."
633   (interactive)
634   (emus-play-next)
635   (emus-display-status))
636
637 (defun emus-play-prev-status ()
638   "Play previous track, then display the emus status in the minibuffer."
639   (interactive)
640   (emus-play-prev)
641   (emus-display-status))
642
643 (defun emus-play-next-album-status ()
644   "Play first track of next album, then display the emus status in the minibuffer."
645   (interactive)
646   (emus-play-next-album)
647   (emus-display-status))
648
649 (defun emus-play-prev-album-status ()
650   "Play last track of previous album, then display the emus status in the minibuffer."
651   (interactive)
652   (emus-play-prev-album)
653   (emus-display-status))
654
655 (defun emus-jump-10s-forward-status ()
656   "Jump 10s forward in current track, then display the emus status in the minibuffer."
657   (interactive)
658   (emus-jump-10s-forward)
659   (emus-display-status))
660
661 (defun emus-jump-10s-backward-status ()
662   "Jump 10s backward in current track, then display the emus status in the minibuffer."
663   (interactive)
664   (emus-jump-10s-backward)
665   (emus-display-status))
666
667 (defun emus-goto-current-status ()
668   "Move point to the current track, then display the emus status in the minibuffer."
669   (interactive)
670   (emus-goto-current)
671   (emus-display-status))
672
673 (defun emus-refresh-status ()
674   "Refresh the emus library, then display the emus status in the minibuffer."
675   (interactive)
676   (emus-stop)
677   (setq emus-tracks nil)
678   (emus--with-library
679    (emus-browse)
680    (emus-display-status)))
681
682 (defvar emus-browser-mode-map
683   (let ((map (make-sparse-keymap)))
684     (define-key map (kbd "SPC") 'emus-playpause-status)
685     (define-key map (kbd "o") 'emus-stop-status)
686     (define-key map (kbd "+") 'emus-volume-up-status)
687     (define-key map (kbd "=") 'emus-volume-up-status)
688     (define-key map (kbd "-") 'emus-volume-down-status)
689     (define-key map (kbd "R") 'emus-refresh-status)
690     (define-key map (kbd "n") 'emus-play-next-status)
691     (define-key map (kbd "p") 'emus-play-prev-status)
692     (define-key map (kbd "N") 'emus-play-next-album-status)
693     (define-key map (kbd "P") 'emus-play-prev-album-status)
694     (define-key map (kbd ",") 'emus-jump-10s-backward-status)
695     (define-key map (kbd ".") 'emus-jump-10s-forward-status)
696     (define-key map (kbd "c") 'emus-goto-current-status)
697     (when (fboundp 'evil-define-key*)
698       (evil-define-key* 'motion map
699                         (kbd "SPC") 'emus-playpause-status
700                         (kbd "o") 'emus-stop-status
701                         (kbd "+") 'emus-volume-up-status
702                         (kbd "=") 'emus-volume-up-status
703                         (kbd "-") 'emus-volume-down-status
704                         (kbd "R") 'emus-refresh-status
705                         (kbd "n") 'emus-play-next-status
706                         (kbd "p") 'emus-play-prev-status
707                         (kbd "N") 'emus-play-next-album-status
708                         (kbd "P") 'emus-play-prev-album-status
709                         (kbd ",") 'emus-jump-10s-backward-status
710                         (kbd ".") 'emus-jump-10s-forward-status
711                         (kbd "c") 'emus-goto-current-status))
712     map)
713   "Keymap for emus browser.")
714
715 (defun emus-restart ()
716   "Restart emus."
717   (interactive)
718   (emus-kill-process))
719
720 (define-derived-mode emus-browser-mode special-mode "emus-browser"
721   "Major mode for EMUS music player file browser."
722   (setq-local buffer-invisibility-spec nil))
723
724 (when (fboundp 'evil-set-initial-state)
725   (evil-set-initial-state 'emus-browser-mode 'motion))
726
727 ;;; emus.el ends here