aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile41
-rw-r--r--doc/developer-release.txt2
-rw-r--r--emms-auto.el562
-rw-r--r--emms-auto.in (renamed from lisp/emms-auto.in)0
-rw-r--r--emms-bookmarks.el (renamed from lisp/emms-bookmarks.el)0
-rw-r--r--emms-browser.el (renamed from lisp/emms-browser.el)0
-rw-r--r--emms-cache.el (renamed from lisp/emms-cache.el)0
-rw-r--r--emms-compat.el (renamed from lisp/emms-compat.el)0
-rw-r--r--emms-cue.el (renamed from lisp/emms-cue.el)0
-rw-r--r--emms-history.el (renamed from lisp/emms-history.el)0
-rw-r--r--emms-i18n.el (renamed from lisp/emms-i18n.el)0
-rw-r--r--emms-info-libtag.el (renamed from lisp/emms-info-libtag.el)0
-rw-r--r--emms-info-metaflac.el (renamed from lisp/emms-info-metaflac.el)0
-rw-r--r--emms-info-mp3info.el (renamed from lisp/emms-info-mp3info.el)0
-rw-r--r--emms-info-ogginfo.el (renamed from lisp/emms-info-ogginfo.el)0
-rw-r--r--emms-info-opusinfo.el (renamed from lisp/emms-info-opusinfo.el)0
-rw-r--r--emms-info-tinytag.el (renamed from lisp/emms-info-tinytag.el)0
-rw-r--r--emms-info.el (renamed from lisp/emms-info.el)0
-rw-r--r--emms-last-played.el (renamed from lisp/emms-last-played.el)0
-rw-r--r--emms-librefm-scrobbler.el (renamed from lisp/emms-librefm-scrobbler.el)0
-rw-r--r--emms-librefm-stream.el (renamed from lisp/emms-librefm-stream.el)0
-rw-r--r--emms-lyrics.el (renamed from lisp/emms-lyrics.el)0
-rw-r--r--emms-maint.el (renamed from lisp/emms-maint.el)0
-rw-r--r--emms-mark.el (renamed from lisp/emms-mark.el)0
-rw-r--r--emms-metaplaylist-mode.el (renamed from lisp/emms-metaplaylist-mode.el)0
-rw-r--r--emms-mode-line-icon.el (renamed from lisp/emms-mode-line-icon.el)0
-rw-r--r--emms-mode-line.el (renamed from lisp/emms-mode-line.el)0
-rw-r--r--emms-player-mpd.el (renamed from lisp/emms-player-mpd.el)0
-rw-r--r--emms-player-mpg321-remote.el (renamed from lisp/emms-player-mpg321-remote.el)0
-rw-r--r--emms-player-mplayer.el (renamed from lisp/emms-player-mplayer.el)0
-rw-r--r--emms-player-mpv.el (renamed from lisp/emms-player-mpv.el)0
-rw-r--r--emms-player-simple.el (renamed from lisp/emms-player-simple.el)0
-rw-r--r--emms-player-vlc.el (renamed from lisp/emms-player-vlc.el)0
-rw-r--r--emms-player-xine.el (renamed from lisp/emms-player-xine.el)0
-rw-r--r--emms-playing-time.el (renamed from lisp/emms-playing-time.el)0
-rw-r--r--emms-playlist-limit.el (renamed from lisp/emms-playlist-limit.el)0
-rw-r--r--emms-playlist-mode.el (renamed from lisp/emms-playlist-mode.el)0
-rw-r--r--emms-playlist-sort.el (renamed from lisp/emms-playlist-sort.el)0
-rw-r--r--emms-score.el (renamed from lisp/emms-score.el)0
-rw-r--r--emms-setup.el (renamed from lisp/emms-setup.el)0
-rw-r--r--emms-show-all.el (renamed from lisp/emms-show-all.el)0
-rw-r--r--emms-source-file.el (renamed from lisp/emms-source-file.el)0
-rw-r--r--emms-source-playlist.el (renamed from lisp/emms-source-playlist.el)0
-rw-r--r--emms-stream-info.el (renamed from lisp/emms-stream-info.el)0
-rw-r--r--emms-streams.el (renamed from lisp/emms-streams.el)0
-rw-r--r--emms-tag-editor.el (renamed from lisp/emms-tag-editor.el)0
-rw-r--r--emms-url.el (renamed from lisp/emms-url.el)0
-rw-r--r--emms-volume-amixer.el (renamed from lisp/emms-volume-amixer.el)0
-rw-r--r--emms-volume-mixerctl.el (renamed from lisp/emms-volume-mixerctl.el)0
-rw-r--r--emms-volume-pulse.el (renamed from lisp/emms-volume-pulse.el)0
-rw-r--r--emms-volume.el (renamed from lisp/emms-volume.el)0
-rw-r--r--emms.el1528
-rw-r--r--jack.el (renamed from lisp/jack.el)0
-rw-r--r--later-do.el (renamed from lisp/later-do.el)0
-rw-r--r--lisp/Makefile28
-rw-r--r--lisp/emms.el1539
56 files changed, 2107 insertions, 1593 deletions
diff --git a/Makefile b/Makefile
index 5e5dfc6..698d3b5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,11 +1,14 @@
GZIP=gzip
MAN1PAGES=emms-print-metadata.1
DOCDIR=doc/
-LISPDIR=lisp
SRCDIR=src
+SITEFLAG=--no-site-file
+EMACS=emacs
-ALLSOURCE=$(wildcard $(LISPDIR)/*.el)
-ALLCOMPILED=$(wildcard $(LISPDIR)/*.elc)
+ALLSOURCE=$(wildcard *.el)
+SOURCE=$(filter-out $(SPECIAL),$(ALLSOURCE))
+TARGET=$(patsubst %.el,%.elc,$(SOURCE))
+ALLCOMPILED=$(wildcard *.elc)
DESTDIR=
PREFIX=$(DESTDIR)/usr/local
@@ -20,17 +23,25 @@ INSTALLINFO = /usr/bin/install-info --info-dir=$(INFODIR)
CHANGELOG_CMD = git log --pretty=medium --no-merges
# The currently released version of EMMS
-VERSION=5.4
+VERSION=5.41
-.PHONY: all install lisp docs deb-install clean
+.PHONY: all install docs clean
.PRECIOUS: %.elc
-all: lisp docs
-
-autoloads:
- $(MAKE) -C $(LISPDIR) emms-auto.el
-
-lisp:
- $(MAKE) -C $(LISPDIR)
+all: emms-auto.el $(TARGET) docs
+
+emms-auto.el: emms-auto.in $(SOURCE)
+ cp emms-auto.in emms-auto.el
+ -rm -f emms-auto.elc
+ @$(EMACS) -q $(SITEFLAG) -batch \
+ -l emms-maint.el \
+ -l emms-auto.el \
+ -f emms-generate-autoloads \
+ $(shell pwd)/emms-auto.el .
+
+%.elc: %.el
+ @$(EMACS) -q $(SITEFLAG) -batch \
+ -l emms-maint.el \
+ -f batch-byte-compile $<
docs:
$(MAKE) -C $(DOCDIR)
@@ -70,13 +81,13 @@ ChangeLog:
clean:
-rm -f *~ $(DOCDIR)emms.info $(DOCDIR)emms.html $(SRCDIR)/emms-print-metadata
- $(MAKE) -C $(LISPDIR) clean
+ -rm -f *~ *.elc emms-auto.el
-dist: clean autoloads
+dist: clean emms-auto.el
git archive --format=tar --prefix=emms-$(VERSION)/ HEAD | \
(cd .. && tar xf -)
rm -f ../emms-$(VERSION)/.gitignore
- cp lisp/emms-auto.el ../emms-$(VERSION)/lisp
+ cp emms-auto.el ../emms-$(VERSION)
$(CHANGELOG_CMD) > ../emms-$(VERSION)/ChangeLog
release: dist
diff --git a/doc/developer-release.txt b/doc/developer-release.txt
index 4453a6a..65fc8ab 100644
--- a/doc/developer-release.txt
+++ b/doc/developer-release.txt
@@ -1,7 +1,7 @@
This file tries to list the things people have to do before they do a
release.
-* Increase the version number in emms.el (variable, elpa header), Makefile, and emms-elpa.el
+* Increase the version number in emms.el (variable, elpa header), Makefile
* Update NEWS
diff --git a/emms-auto.el b/emms-auto.el
new file mode 100644
index 0000000..2ee5831
--- /dev/null
+++ b/emms-auto.el
@@ -0,0 +1,562 @@
+;;; -*-emacs-lisp-*-
+
+(defvar generated-autoload-file)
+(defvar command-line-args-left)
+(defun emms-generate-autoloads ()
+ (interactive)
+ (require 'autoload)
+ (setq generated-autoload-file (car command-line-args-left))
+ (setq command-line-args-left (cdr command-line-args-left))
+ (batch-update-autoloads))
+
+(add-to-list 'load-path (directory-file-name
+ (or (file-name-directory load-file-name)
+ (car load-path))))
+
+(provide 'emms-auto)
+;;; Generated autoloads follow (made by autoload.el).
+
+;;;### (autoloads nil "emms" "emms.el" (0 0 0 0))
+;;; Generated autoloads from emms.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms" '("define-emms-" "emms-" "with-current-emms-playlist")))
+
+;;;***
+
+;;;### (autoloads nil "emms-browser" "emms-browser.el" (0 0 0 0))
+;;; Generated autoloads from emms-browser.el
+
+(autoload 'emms-browser "emms-browser" "\
+Launch or switch to the EMMS Browser." t nil)
+
+(autoload 'emms-smart-browse "emms-browser" "\
+Display browser and playlist.
+Toggle between selecting browser, playlist or hiding both. Tries
+to behave sanely if the user has manually changed the window
+configuration." t nil)
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-browser" '("case-fold-string" "emms-")))
+
+;;;***
+
+;;;### (autoloads nil "emms-cache" "emms-cache.el" (0 0 0 0))
+;;; Generated autoloads from emms-cache.el
+
+(autoload 'emms-cache-enable "emms-cache" "\
+Enable caching of Emms track data." t nil)
+
+(autoload 'emms-cache-disable "emms-cache" "\
+Disable caching of Emms track data." t nil)
+
+(autoload 'emms-cache-toggle "emms-cache" "\
+Toggle caching of Emms track data." t nil)
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-cache" '("emms-cache")))
+
+;;;***
+
+;;;### (autoloads nil "emms-compat" "emms-compat.el" (0 0 0 0))
+;;; Generated autoloads from emms-compat.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-compat" '("emms-")))
+
+;;;***
+
+;;;### (autoloads nil "emms-cue" "emms-cue.el" (0 0 0 0))
+;;; Generated autoloads from emms-cue.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-cue" '("emms-")))
+
+;;;***
+
+;;;### (autoloads nil "emms-history" "emms-history.el" (0 0 0 0))
+;;; Generated autoloads from emms-history.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-history" '("emms-history-")))
+
+;;;***
+
+;;;### (autoloads nil "emms-i18n" "emms-i18n.el" (0 0 0 0))
+;;; Generated autoloads from emms-i18n.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-i18n" '("emms-i18n-")))
+
+;;;***
+
+;;;### (autoloads nil "emms-info" "emms-info.el" (0 0 0 0))
+;;; Generated autoloads from emms-info.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-info" '("emms-info-")))
+
+;;;***
+
+;;;### (autoloads nil "emms-info-libtag" "emms-info-libtag.el" (0
+;;;;;; 0 0 0))
+;;; Generated autoloads from emms-info-libtag.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-info-libtag" '("emms-info-libtag")))
+
+;;;***
+
+;;;### (autoloads nil "emms-info-metaflac" "emms-info-metaflac.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-info-metaflac.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-info-metaflac" '("emms-info-metaflac")))
+
+;;;***
+
+;;;### (autoloads nil "emms-info-mp3info" "emms-info-mp3info.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-info-mp3info.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-info-mp3info" '("emms-info-mp3")))
+
+;;;***
+
+;;;### (autoloads nil "emms-info-ogginfo" "emms-info-ogginfo.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-info-ogginfo.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-info-ogginfo" '("emms-info-ogginfo")))
+
+;;;***
+
+;;;### (autoloads nil "emms-info-opusinfo" "emms-info-opusinfo.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-info-opusinfo.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-info-opusinfo" '("emms-info-opusinfo")))
+
+;;;***
+
+;;;### (autoloads nil "emms-info-tinytag" "emms-info-tinytag.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-info-tinytag.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-info-tinytag" '("emms-info-tinytag")))
+
+;;;***
+
+;;;### (autoloads nil "emms-last-played" "emms-last-played.el" (0
+;;;;;; 0 0 0))
+;;; Generated autoloads from emms-last-played.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-last-played" '("emms-last-played-")))
+
+;;;***
+
+;;;### (autoloads nil "emms-librefm-scrobbler" "emms-librefm-scrobbler.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-librefm-scrobbler.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-librefm-scrobbler" '("emms-librefm-scrobbler-")))
+
+;;;***
+
+;;;### (autoloads nil "emms-librefm-stream" "emms-librefm-stream.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-librefm-stream.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-librefm-stream" '("emms-librefm-stream")))
+
+;;;***
+
+;;;### (autoloads nil "emms-lyrics" "emms-lyrics.el" (0 0 0 0))
+;;; Generated autoloads from emms-lyrics.el
+
+(autoload 'emms-lyrics-enable "emms-lyrics" "\
+Enable displaying emms lyrics." t nil)
+
+(autoload 'emms-lyrics-disable "emms-lyrics" "\
+Disable displaying emms lyrics." t nil)
+
+(autoload 'emms-lyrics-toggle "emms-lyrics" "\
+Toggle displaying emms lyrics." t nil)
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-lyrics" '("emms-lyrics")))
+
+;;;***
+
+;;;### (autoloads nil "emms-mark" "emms-mark.el" (0 0 0 0))
+;;; Generated autoloads from emms-mark.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-mark" '("emms-mark-")))
+
+;;;***
+
+;;;### (autoloads nil "emms-metaplaylist-mode" "emms-metaplaylist-mode.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-metaplaylist-mode.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-metaplaylist-mode" '("emms-metaplaylist-mode")))
+
+;;;***
+
+;;;### (autoloads nil "emms-mode-line" "emms-mode-line.el" (0 0 0
+;;;;;; 0))
+;;; Generated autoloads from emms-mode-line.el
+
+(autoload 'emms-mode-line-enable "emms-mode-line" "\
+Turn on `emms-mode-line'." t nil)
+
+(autoload 'emms-mode-line-disable "emms-mode-line" "\
+Turn off `emms-mode-line'." t nil)
+
+(autoload 'emms-mode-line-toggle "emms-mode-line" "\
+Toggle `emms-mode-line'." t nil)
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-mode-line" '("emms-mode-line")))
+
+;;;***
+
+;;;### (autoloads nil "emms-mode-line-icon" "emms-mode-line-icon.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-mode-line-icon.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-mode-line-icon" '("emms-mode-line-icon-")))
+
+;;;***
+
+;;;### (autoloads nil "emms-player-mpd" "emms-player-mpd.el" (0 0
+;;;;;; 0 0))
+;;; Generated autoloads from emms-player-mpd.el
+
+(autoload 'emms-player-mpd-clear "emms-player-mpd" "\
+Clear the MusicPD playlist." t nil)
+
+(autoload 'emms-player-mpd-connect "emms-player-mpd" "\
+Connect to MusicPD and retrieve its current playlist.
+
+Afterward, the status of MusicPD will be tracked.
+
+This also has the effect of changing the current EMMS playlist to
+be the same as the current MusicPD playlist. Thus, this
+function is useful to call if the contents of the EMMS playlist
+buffer get out-of-sync for some reason." t nil)
+
+(autoload 'emms-player-mpd-show "emms-player-mpd" "\
+Describe the current EMMS track in the minibuffer.
+
+If INSERTP is non-nil, insert the description into the current
+buffer instead.
+
+If CALLBACK is a function, call it with the current buffer and
+description as arguments instead of displaying the description or
+inserting it.
+
+This function uses `emms-show-format' to format the current track.
+It differs from `emms-show' in that it asks MusicPD for the current track,
+rather than EMMS.
+
+\(fn &optional INSERTP CALLBACK)" t nil)
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-player-mpd" '("emms-")))
+
+;;;***
+
+;;;### (autoloads nil "emms-player-mpg321-remote" "emms-player-mpg321-remote.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-player-mpg321-remote.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-player-mpg321-remote" '("emms-player-mpg321-remote")))
+
+;;;***
+
+;;;### (autoloads nil "emms-player-mplayer" "emms-player-mplayer.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-player-mplayer.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-player-mplayer" '("emms-player-mplayer-" "mplayer")))
+
+;;;***
+
+;;;### (autoloads nil "emms-player-mpv" "emms-player-mpv.el" (0 0
+;;;;;; 0 0))
+;;; Generated autoloads from emms-player-mpv.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-player-mpv" '("emms-player-mpv")))
+
+;;;***
+
+;;;### (autoloads nil "emms-player-simple" "emms-player-simple.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-player-simple.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-player-simple" '("alsaplayer" "define-emms-simple-player" "emms-player-" "fluidsynth" "mikmod" "mpg321" "ogg123" "playsound" "speexdec" "timidity")))
+
+;;;***
+
+;;;### (autoloads nil "emms-player-vlc" "emms-player-vlc.el" (0 0
+;;;;;; 0 0))
+;;; Generated autoloads from emms-player-vlc.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-player-vlc" '("emms-player-vlc-" "vlc")))
+
+;;;***
+
+;;;### (autoloads nil "emms-player-xine" "emms-player-xine.el" (0
+;;;;;; 0 0 0))
+;;; Generated autoloads from emms-player-xine.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-player-xine" '("emms-" "xine")))
+
+;;;***
+
+;;;### (autoloads nil "emms-playing-time" "emms-playing-time.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-playing-time.el
+
+(autoload 'emms-playing-time-enable-display "emms-playing-time" "\
+Display playing time on mode line." t nil)
+
+(autoload 'emms-playing-time-disable-display "emms-playing-time" "\
+Remove playing time from mode line." t nil)
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-playing-time" '("emms-playing-time")))
+
+;;;***
+
+;;;### (autoloads nil "emms-playlist-limit" "emms-playlist-limit.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-playlist-limit.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-playlist-limit" '("define-emms-playlist-limit" "emms-playlist-limit-")))
+
+;;;***
+
+;;;### (autoloads nil "emms-playlist-mode" "emms-playlist-mode.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-playlist-mode.el
+
+(autoload 'emms-playlist-mode "emms-playlist-mode" "\
+A major mode for Emms playlists.
+\\{emms-playlist-mode-map}" t nil)
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-playlist-mode" '("emms")))
+
+;;;***
+
+;;;### (autoloads nil "emms-playlist-sort" "emms-playlist-sort.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-playlist-sort.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-playlist-sort" '("define-emms-playlist-sort" "emms-")))
+
+;;;***
+
+;;;### (autoloads nil "emms-score" "emms-score.el" (0 0 0 0))
+;;; Generated autoloads from emms-score.el
+
+(autoload 'emms-score-enable "emms-score" "\
+Turn on emms-score." t nil)
+
+(autoload 'emms-score-disable "emms-score" "\
+Turn off emms-score." t nil)
+
+(autoload 'emms-score-toggle "emms-score" "\
+Toggle emms-score." t nil)
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-score" '("emms-score")))
+
+;;;***
+
+;;;### (autoloads nil "emms-setup" "emms-setup.el" (0 0 0 0))
+;;; Generated autoloads from emms-setup.el
+
+(autoload 'emms-minimalistic "emms-setup" "\
+An Emms setup script.
+Invisible playlists and all the basics for playing media." nil nil)
+
+(autoload 'emms-all "emms-setup" "\
+An Emms setup script.
+Everything included in the `emms-minimalistic' setup and adds all
+the stable features which come with the Emms distribution." nil nil)
+
+(autoload 'emms-default-players "emms-setup" "\
+Set `emms-player-list' to `emms-setup-default-player-list'." nil nil)
+
+(autoload 'emms-devel "emms-setup" nil nil nil)
+
+(autoload 'emms-standard "emms-setup" nil nil nil)
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-setup" '("emms-setup-default-player-list")))
+
+;;;***
+
+;;;### (autoloads nil "emms-show-all" "emms-show-all.el" (0 0 0 0))
+;;; Generated autoloads from emms-show-all.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-show-all" '("emms-show-all")))
+
+;;;***
+
+;;;### (autoloads nil "emms-source-file" "emms-source-file.el" (0
+;;;;;; 0 0 0))
+;;; Generated autoloads from emms-source-file.el
+ (autoload 'emms-play-file "emms-source-file" nil t)
+ (autoload 'emms-add-file "emms-source-file" nil t)
+ (autoload 'emms-play-directory "emms-source-file" nil t)
+ (autoload 'emms-add-directory "emms-source-file" nil t)
+ (autoload 'emms-play-directory-tree "emms-source-file" nil t)
+ (autoload 'emms-add-directory-tree "emms-source-file" nil t)
+ (autoload 'emms-play-find "emms-source-file" nil t)
+ (autoload 'emms-add-find "emms-source-file" nil t)
+ (autoload 'emms-play-dired "emms-source-file" nil t)
+ (autoload 'emms-add-dired "emms-source-file" nil t)
+
+(autoload 'emms-source-file-directory-tree "emms-source-file" "\
+Return a list of all files under DIR that match REGEX.
+This function uses `emms-source-file-directory-tree-function'.
+
+\(fn DIR REGEX)" nil nil)
+
+(autoload 'emms-source-file-regex "emms-source-file" "\
+Return a regexp that matches everything any player (that supports
+files) can play." nil nil)
+
+(autoload 'emms-locate "emms-source-file" "\
+Search for REGEXP and display the results in a locate buffer
+
+\(fn REGEXP)" t nil)
+ (autoload 'emms-play-url "emms-source-file" nil t)
+ (autoload 'emms-add-url "emms-source-file" nil t)
+ (autoload 'emms-play-streamlist "emms-source-file" nil t)
+ (autoload 'emms-add-streamlist "emms-source-file" nil t)
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-source-file" '("dire" "emms-" "file" "find" "streamlist" "url")))
+
+;;;***
+
+;;;### (autoloads nil "emms-source-playlist" "emms-source-playlist.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-source-playlist.el
+ (autoload 'emms-play-playlist "emms-source-playlist" nil t)
+ (autoload 'emms-add-playlist "emms-source-playlist" nil t)
+ (autoload 'emms-play-native-playlist "emms-source-playlist" nil t)
+ (autoload 'emms-add-native-playlist "emms-source-playlist" nil t)
+ (autoload 'emms-play-m3u-playlist "emms-source-playlist" nil t)
+ (autoload 'emms-add-m3u-playlist "emms-source-playlist" nil t)
+ (autoload 'emms-play-pls-playlist "emms-source-playlist" nil t)
+ (autoload 'emms-add-pls-playlist "emms-source-playlist" nil t)
+ (autoload 'emms-play-playlist-file "emms-source-playlist" nil t)
+ (autoload 'emms-add-playlist-file "emms-source-playlist" nil t)
+ (autoload 'emms-play-playlist-directory
+ "emms-source-playlist" nil t)
+ (autoload 'emms-add-playlist-directory
+ "emms-source-playlist" nil t)
+ (autoload 'emms-play-playlist-directory-tree
+ "emms-source-playlist" nil t)
+ (autoload 'emms-add-playlist-directory-tree
+ "emms-source-file" nil t)
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-source-playlist" '("emms-" "m3u-playlist" "native-playlist" "playlist" "pls-playlist")))
+
+;;;***
+
+;;;### (autoloads nil "emms-streams" "emms-streams.el" (0 0 0 0))
+;;; Generated autoloads from emms-streams.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-streams" '("emms-streams")))
+
+;;;***
+
+;;;### (autoloads nil "emms-tag-editor" "emms-tag-editor.el" (0 0
+;;;;;; 0 0))
+;;; Generated autoloads from emms-tag-editor.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-tag-editor" '("emms-tag-editor-")))
+
+;;;***
+
+;;;### (autoloads nil "emms-url" "emms-url.el" (0 0 0 0))
+;;; Generated autoloads from emms-url.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-url" '("emms-")))
+
+;;;***
+
+;;;### (autoloads nil "emms-volume" "emms-volume.el" (0 0 0 0))
+;;; Generated autoloads from emms-volume.el
+
+(autoload 'emms-volume-raise "emms-volume" "\
+Raise the speaker volume." t nil)
+
+(autoload 'emms-volume-lower "emms-volume" "\
+Lower the speaker volume." t nil)
+
+(autoload 'emms-volume-mode-plus "emms-volume" "\
+Raise volume and enable or extend the `emms-volume-minor-mode' timeout." t nil)
+
+(autoload 'emms-volume-mode-minus "emms-volume" "\
+Lower volume and enable or extend the `emms-volume-minor-mode' timeout." t nil)
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-volume" '("emms-volume-")))
+
+;;;***
+
+;;;### (autoloads nil "emms-volume-amixer" "emms-volume-amixer.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-volume-amixer.el
+
+(autoload 'emms-volume-amixer-change "emms-volume-amixer" "\
+Change amixer master volume by AMOUNT.
+
+\(fn AMOUNT)" nil nil)
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-volume-amixer" '("emms-volume-amixer-c")))
+
+;;;***
+
+;;;### (autoloads nil "emms-volume-mixerctl" "emms-volume-mixerctl.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-volume-mixerctl.el
+
+(autoload 'emms-volume-mixerctl-change "emms-volume-mixerctl" "\
+Change mixerctl master volume by AMOUNT.
+
+\(fn AMOUNT)" nil nil)
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-volume-mixerctl" '("emms-volume-mixerctl-c")))
+
+;;;***
+
+;;;### (autoloads nil "emms-volume-pulse" "emms-volume-pulse.el"
+;;;;;; (0 0 0 0))
+;;; Generated autoloads from emms-volume-pulse.el
+
+(autoload 'emms-volume-pulse-change "emms-volume-pulse" "\
+Change PulseAudio volume by AMOUNT.
+
+\(fn AMOUNT)" nil nil)
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-volume-pulse" '("emms-volume-")))
+
+;;;***
+
+;;;### (autoloads nil "jack" "jack.el" (0 0 0 0))
+;;; Generated autoloads from jack.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "jack" '("jack-")))
+
+;;;***
+
+;;;### (autoloads nil "later-do" "later-do.el" (0 0 0 0))
+;;; Generated autoloads from later-do.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "later-do" '("later-do")))
+
+;;;***
+
+;;;### (autoloads nil nil ("emms-maint.el" "emms-stream-info.el")
+;;;;;; (0 0 0 0))
+
+;;;***
+
+;;;### (autoloads nil "emms-bookmarks" "emms-bookmarks.el" (0 0 0
+;;;;;; 0))
+;;; Generated autoloads from emms-bookmarks.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "emms-bookmarks" '("emms-bookmarks-")))
+
+;;;***
diff --git a/lisp/emms-auto.in b/emms-auto.in
index d799e4a..d799e4a 100644
--- a/lisp/emms-auto.in
+++ b/emms-auto.in
diff --git a/lisp/emms-bookmarks.el b/emms-bookmarks.el
index b579ca3..b579ca3 100644
--- a/lisp/emms-bookmarks.el
+++ b/emms-bookmarks.el
diff --git a/lisp/emms-browser.el b/emms-browser.el
index f805b3b..f805b3b 100644
--- a/lisp/emms-browser.el
+++ b/emms-browser.el
diff --git a/lisp/emms-cache.el b/emms-cache.el
index 792b1b8..792b1b8 100644
--- a/lisp/emms-cache.el
+++ b/emms-cache.el
diff --git a/lisp/emms-compat.el b/emms-compat.el
index 94f9b45..94f9b45 100644
--- a/lisp/emms-compat.el
+++ b/emms-compat.el
diff --git a/lisp/emms-cue.el b/emms-cue.el
index aefcafd..aefcafd 100644
--- a/lisp/emms-cue.el
+++ b/emms-cue.el
diff --git a/lisp/emms-history.el b/emms-history.el
index 2f75571..2f75571 100644
--- a/lisp/emms-history.el
+++ b/emms-history.el
diff --git a/lisp/emms-i18n.el b/emms-i18n.el
index 66a5eeb..66a5eeb 100644
--- a/lisp/emms-i18n.el
+++ b/emms-i18n.el
diff --git a/lisp/emms-info-libtag.el b/emms-info-libtag.el
index 7954ab2..7954ab2 100644
--- a/lisp/emms-info-libtag.el
+++ b/emms-info-libtag.el
diff --git a/lisp/emms-info-metaflac.el b/emms-info-metaflac.el
index 5a9f517..5a9f517 100644
--- a/lisp/emms-info-metaflac.el
+++ b/emms-info-metaflac.el
diff --git a/lisp/emms-info-mp3info.el b/emms-info-mp3info.el
index b01217e..b01217e 100644
--- a/lisp/emms-info-mp3info.el
+++ b/emms-info-mp3info.el
diff --git a/lisp/emms-info-ogginfo.el b/emms-info-ogginfo.el
index 2d8412f..2d8412f 100644
--- a/lisp/emms-info-ogginfo.el
+++ b/emms-info-ogginfo.el
diff --git a/lisp/emms-info-opusinfo.el b/emms-info-opusinfo.el
index d636f7b..d636f7b 100644
--- a/lisp/emms-info-opusinfo.el
+++ b/emms-info-opusinfo.el
diff --git a/lisp/emms-info-tinytag.el b/emms-info-tinytag.el
index fe21653..fe21653 100644
--- a/lisp/emms-info-tinytag.el
+++ b/emms-info-tinytag.el
diff --git a/lisp/emms-info.el b/emms-info.el
index 5e8fae2..5e8fae2 100644
--- a/lisp/emms-info.el
+++ b/emms-info.el
diff --git a/lisp/emms-last-played.el b/emms-last-played.el
index 529869a..529869a 100644
--- a/lisp/emms-last-played.el
+++ b/emms-last-played.el
diff --git a/lisp/emms-librefm-scrobbler.el b/emms-librefm-scrobbler.el
index a49a458..a49a458 100644
--- a/lisp/emms-librefm-scrobbler.el
+++ b/emms-librefm-scrobbler.el
diff --git a/lisp/emms-librefm-stream.el b/emms-librefm-stream.el
index 7df67cb..7df67cb 100644
--- a/lisp/emms-librefm-stream.el
+++ b/emms-librefm-stream.el
diff --git a/lisp/emms-lyrics.el b/emms-lyrics.el
index 95b8e29..95b8e29 100644
--- a/lisp/emms-lyrics.el
+++ b/emms-lyrics.el
diff --git a/lisp/emms-maint.el b/emms-maint.el
index fd5c4a4..fd5c4a4 100644
--- a/lisp/emms-maint.el
+++ b/emms-maint.el
diff --git a/lisp/emms-mark.el b/emms-mark.el
index b7d2558..b7d2558 100644
--- a/lisp/emms-mark.el
+++ b/emms-mark.el
diff --git a/lisp/emms-metaplaylist-mode.el b/emms-metaplaylist-mode.el
index d4616f0..d4616f0 100644
--- a/lisp/emms-metaplaylist-mode.el
+++ b/emms-metaplaylist-mode.el
diff --git a/lisp/emms-mode-line-icon.el b/emms-mode-line-icon.el
index 82cf815..82cf815 100644
--- a/lisp/emms-mode-line-icon.el
+++ b/emms-mode-line-icon.el
diff --git a/lisp/emms-mode-line.el b/emms-mode-line.el
index caac8b1..caac8b1 100644
--- a/lisp/emms-mode-line.el
+++ b/emms-mode-line.el
diff --git a/lisp/emms-player-mpd.el b/emms-player-mpd.el
index 327938d..327938d 100644
--- a/lisp/emms-player-mpd.el
+++ b/emms-player-mpd.el
diff --git a/lisp/emms-player-mpg321-remote.el b/emms-player-mpg321-remote.el
index de242ea..de242ea 100644
--- a/lisp/emms-player-mpg321-remote.el
+++ b/emms-player-mpg321-remote.el
diff --git a/lisp/emms-player-mplayer.el b/emms-player-mplayer.el
index aa1f5c7..aa1f5c7 100644
--- a/lisp/emms-player-mplayer.el
+++ b/emms-player-mplayer.el
diff --git a/lisp/emms-player-mpv.el b/emms-player-mpv.el
index c4fc541..c4fc541 100644
--- a/lisp/emms-player-mpv.el
+++ b/emms-player-mpv.el
diff --git a/lisp/emms-player-simple.el b/emms-player-simple.el
index fd93137..fd93137 100644
--- a/lisp/emms-player-simple.el
+++ b/emms-player-simple.el
diff --git a/lisp/emms-player-vlc.el b/emms-player-vlc.el
index 7c2130d..7c2130d 100644
--- a/lisp/emms-player-vlc.el
+++ b/emms-player-vlc.el
diff --git a/lisp/emms-player-xine.el b/emms-player-xine.el
index 2fc60e7..2fc60e7 100644
--- a/lisp/emms-player-xine.el
+++ b/emms-player-xine.el
diff --git a/lisp/emms-playing-time.el b/emms-playing-time.el
index 8b14e16..8b14e16 100644
--- a/lisp/emms-playing-time.el
+++ b/emms-playing-time.el
diff --git a/lisp/emms-playlist-limit.el b/emms-playlist-limit.el
index 91eff1e..91eff1e 100644
--- a/lisp/emms-playlist-limit.el
+++ b/emms-playlist-limit.el
diff --git a/lisp/emms-playlist-mode.el b/emms-playlist-mode.el
index 6979b79..6979b79 100644
--- a/lisp/emms-playlist-mode.el
+++ b/emms-playlist-mode.el
diff --git a/lisp/emms-playlist-sort.el b/emms-playlist-sort.el
index e23604b..e23604b 100644
--- a/lisp/emms-playlist-sort.el
+++ b/emms-playlist-sort.el
diff --git a/lisp/emms-score.el b/emms-score.el
index 8c783c5..8c783c5 100644
--- a/lisp/emms-score.el
+++ b/emms-score.el
diff --git a/lisp/emms-setup.el b/emms-setup.el
index fed2a36..fed2a36 100644
--- a/lisp/emms-setup.el
+++ b/emms-setup.el
diff --git a/lisp/emms-show-all.el b/emms-show-all.el
index f37ef5e..f37ef5e 100644
--- a/lisp/emms-show-all.el
+++ b/emms-show-all.el
diff --git a/lisp/emms-source-file.el b/emms-source-file.el
index 2be6538..2be6538 100644
--- a/lisp/emms-source-file.el
+++ b/emms-source-file.el
diff --git a/lisp/emms-source-playlist.el b/emms-source-playlist.el
index 327be8c..327be8c 100644
--- a/lisp/emms-source-playlist.el
+++ b/emms-source-playlist.el
diff --git a/lisp/emms-stream-info.el b/emms-stream-info.el
index 2bc7a41..2bc7a41 100644
--- a/lisp/emms-stream-info.el
+++ b/emms-stream-info.el
diff --git a/lisp/emms-streams.el b/emms-streams.el
index c3b2d11..c3b2d11 100644
--- a/lisp/emms-streams.el
+++ b/emms-streams.el
diff --git a/lisp/emms-tag-editor.el b/emms-tag-editor.el
index ca7941a..ca7941a 100644
--- a/lisp/emms-tag-editor.el
+++ b/emms-tag-editor.el
diff --git a/lisp/emms-url.el b/emms-url.el
index 59d5cfa..59d5cfa 100644
--- a/lisp/emms-url.el
+++ b/emms-url.el
diff --git a/lisp/emms-volume-amixer.el b/emms-volume-amixer.el
index 190f6b0..190f6b0 100644
--- a/lisp/emms-volume-amixer.el
+++ b/emms-volume-amixer.el
diff --git a/lisp/emms-volume-mixerctl.el b/emms-volume-mixerctl.el
index 4ca7820..4ca7820 100644
--- a/lisp/emms-volume-mixerctl.el
+++ b/emms-volume-mixerctl.el
diff --git a/lisp/emms-volume-pulse.el b/emms-volume-pulse.el
index 440ab7f..440ab7f 100644
--- a/lisp/emms-volume-pulse.el
+++ b/emms-volume-pulse.el
diff --git a/lisp/emms-volume.el b/emms-volume.el
index 7a6ff5f..7a6ff5f 100644
--- a/lisp/emms-volume.el
+++ b/emms-volume.el
diff --git a/emms.el b/emms.el
index e43457e..1f49e73 100644
--- a/emms.el
+++ b/emms.el
@@ -5,7 +5,7 @@
;; Author: Jorgen Schäfer <forcer@forcix.cx>, the Emms developers (see AUTHORS file)
;; Maintainer: Yoni Rabkin <yrk@gnu.org>
-;; Version: 5.4
+;; Version: 5.41
;; Keywords: emms, mp3, ogg, flac, music, mpeg, video, multimedia
;; Package-Type: multi
;; Package-Requires: ((cl-lib "0.5"))
@@ -13,20 +13,1528 @@
;; This file is part of EMMS.
-;; EMMS 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.
+;; EMMS 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, or (at your option)
+;; any later version.
-;; EMMS 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.
+;; EMMS 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 program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
+;; This is the very core of EMMS. It provides ways to play a track
+;; using `emms-start', to go through the playlist using the commands
+;; `emms-next' and `emms-previous', to stop the playback using
+;; `emms-stop', and to see what's currently playing using `emms-show'.
-;;; emms.el ends here.
+;; But in itself, this core is useless, because it doesn't know how to
+;; play any tracks --- you need players for this. In fact, it doesn't
+;; even know how to find any tracks to consider playing --- for this,
+;; you need sources.
+
+;; A sample configuration is offered in emms-setup.el, and the
+;; Friendly Manual in the doc/ directory is both detailed, and kept up
+;; to date.
+
+
+;;; Code:
+
+(defvar emms-version "5.41"
+ "EMMS version string.")
+
+
+;;; User Customization
+
+(defgroup emms nil
+ "*The Emacs Multimedia System."
+ :prefix "emms-"
+ :group 'multimedia
+ :group 'applications)
+
+(defgroup emms-player nil
+ "*Track players for EMMS."
+ :prefix "emms-player-"
+ :group 'emms)
+
+(defgroup emms-source nil
+ "*Track sources for EMMS."
+ :prefix "emms-source-"
+ :group 'emms)
+
+(defcustom emms-player-list nil
+ "*List of players that EMMS can use. You need to set this!"
+ :group 'emms
+ :type '(repeat (symbol :tag "Player")))
+
+(defcustom emms-show-format "Currently playing: %s"
+ "*The format to use for `emms-show'.
+Any \"%s\" is replaced by what `emms-track-description-function' returns
+for the currently playing track."
+ :group 'emms
+ :type 'string)
+
+(defcustom emms-repeat-playlist nil
+ "*Non-nil if the EMMS playlist should automatically repeat.
+If nil, playback will stop when the last track finishes playing.
+If non-nil, EMMS will wrap back to the first track when that happens."
+ :group 'emms
+ :type 'boolean)
+
+(defcustom emms-random-playlist nil
+ "*Non-nil means that tracks are played randomly. If nil, tracks
+are played sequentially."
+ :group 'emms
+ :type 'boolean)
+
+(defcustom emms-repeat-track nil
+ "Non-nil, playback will repeat current track. If nil, EMMS will play
+track by track normally."
+ :group 'emms
+ :type 'boolean)
+
+(defvar-local emms-single-track nil
+ "Non-nil, play the current track and then stop.")
+
+(defcustom emms-completing-read-function
+ (if (and (boundp 'ido-mode)
+ ido-mode)
+ 'ido-completing-read
+ 'completing-read)
+ "Function to call when prompting user to choose between a list of options.
+This should take the same arguments as `completing-read'. Some
+possible values are `completing-read' and `ido-completing-read'.
+Note that you must set `ido-mode' if using
+`ido-completing-read'."
+ :group 'emms
+ :type 'function)
+
+(defcustom emms-track-description-function 'emms-track-simple-description
+ "*Function for describing an EMMS track in a user-friendly way."
+ :group 'emms
+ :type 'function)
+
+(defcustom emms-player-delay 0
+ "The delay to pause after a player finished.
+This is a floating-point number of seconds. This is necessary
+for some platforms where it takes a bit to free the audio device
+after a player has finished. If EMMS is skipping songs, increase
+this number."
+ :type 'number
+ :group 'emms)
+
+(defcustom emms-playlist-shuffle-function 'emms-playlist-simple-shuffle
+ "*The function to use for shuffling the playlist."
+ :type 'function
+ :group 'emms)
+
+(defcustom emms-playlist-sort-function 'emms-playlist-simple-sort
+ "*The function to use for sorting the playlist."
+ :type 'function
+ :group 'emms)
+
+(defcustom emms-playlist-uniq-function 'emms-playlist-simple-uniq
+ "*The function to use for removing duplicate tracks in the playlist."
+ :type 'function
+ :group 'emms)
+
+(defcustom emms-sort-lessp-function 'emms-sort-track-name-less-p
+ "*Function for comparing two EMMS tracks.
+The function should return non-nil if and only if the first track
+sorts before the second (see `sort')."
+ :group 'emms
+ :type 'function)
+
+(defcustom emms-playlist-buffer-name " *EMMS Playlist*"
+ "*The default name of the EMMS playlist buffer."
+ :type 'string
+ :group 'emms)
+
+(defcustom emms-playlist-default-major-mode 'emms-playlist-mode
+ "*The default major mode for EMMS playlist."
+ :type 'function
+ :group 'emms)
+
+(defcustom emms-playlist-insert-track-function 'emms-playlist-simple-insert-track
+ "*A function to insert a track into the playlist buffer."
+ :group 'emms
+ :type 'function)
+(make-variable-buffer-local 'emms-playlist-insert-track-function)
+
+(defcustom emms-playlist-update-track-function 'emms-playlist-simple-update-track
+ "*A function to update the track at point.
+This is called when the track information changed. This also
+shouldn't assume that the track has been inserted before."
+ :group 'emms
+ :type 'function)
+(make-variable-buffer-local 'emms-playlist-insert-track-function)
+
+(defcustom emms-playlist-delete-track-function 'emms-playlist-simple-delete-track
+ "*A function to delete the track at point in the playlist buffer."
+ :group 'emms
+ :type 'function)
+(make-variable-buffer-local 'emms-playlist-delete-track-function)
+
+(defcustom emms-ok-track-function 'emms-default-ok-track-function
+ "*Function returns true if we shouldn't skip this track."
+ :group 'emms
+ :type 'function)
+
+(defcustom emms-playlist-source-inserted-hook nil
+ "*Hook run when a source got inserted into the playlist.
+The buffer is narrowed to the new tracks."
+ :type 'hook
+ :group 'emms)
+
+(defcustom emms-playlist-selection-changed-hook nil
+ "*Hook run after another track is selected in the EMMS playlist."
+ :group 'emms
+ :type 'hook)
+
+(defcustom emms-playlist-cleared-hook nil
+ "*Hook run after the current EMMS playlist is cleared.
+This happens both when the playlist is cleared and when a new
+buffer is created for it."
+ :group 'emms
+ :type 'hook)
+
+(defcustom emms-track-initialize-functions nil
+ "*List of functions to call for each new EMMS track.
+This can be used to initialize tracks with various info."
+ :group 'emms
+ :type 'hook)
+
+(defcustom emms-track-info-filters nil
+ "*List of functions to call when a track changes data, before updating
+the display.
+These functions are passed the track as an argument."
+ :group 'emms
+ :type 'hook)
+
+(defcustom emms-track-updated-functions nil
+ "*List of functions to call when a track changes data, after updating
+the display.
+These functions are passed the track as an argument."
+ :group 'emms
+ :type 'hook)
+
+(defcustom emms-player-started-hook nil
+ "*Hook run when an EMMS player starts playing."
+ :group 'emms
+ :type 'hook
+ :options '(emms-show))
+
+(defcustom emms-player-stopped-hook nil
+ "*Hook run when an EMMS player is stopped by the user.
+See `emms-player-finished-hook'."
+ :group 'emms
+ :type 'hook)
+
+(defcustom emms-player-finished-hook nil
+ "*Hook run when an EMMS player finishes playing a track.
+Please pay attention to the differences between
+`emms-player-finished-hook' and `emms-player-stopped-hook'. The
+former is called only when the player actually finishes playing a
+track; the latter, only when the player is stopped
+interactively."
+ :group 'emms
+ :type 'hook)
+
+(defcustom emms-player-next-function 'emms-next-noerror
+ "*A function run when EMMS thinks the next song should be played."
+ :group 'emms
+ :type 'function
+ :options '(emms-next-noerror
+ emms-random))
+
+(defcustom emms-player-paused-hook nil
+ "*Hook run when a player is paused or resumed.
+Use `emms-player-paused-p' to find the current state."
+ :group 'emms
+ :type 'hook)
+
+(defcustom emms-seek-seconds 10
+ "The number of seconds to seek forward or backward when seeking."
+ :group 'emms
+ :type 'number)
+
+(defcustom emms-player-seeked-functions nil
+ "*Functions called when a player is seeking.
+The functions are called with a single argument, the amount of
+seconds the player did seek."
+ :group 'emms
+ :type 'hook)
+
+(defcustom emms-player-time-set-functions nil
+ "*Functions called when a player is setting the elapsed time of a track.
+The functions are called with a single argument, the time elapsed
+since the beginning of the current track."
+ :group 'emms
+ :type 'hook)
+
+(defcustom emms-cache-get-function nil
+ "A function to retrieve a track entry from the cache.
+This is called with two arguments, the type and the name."
+ :group 'emms
+ :type 'function)
+
+(defcustom emms-cache-set-function nil
+ "A function to add/set a track entry from the cache.
+This is called with three arguments: the type of the track, the
+name of the track, and the track itself."
+ :group 'emms
+ :type 'function)
+
+(defcustom emms-cache-modified-function nil
+ "A function to be called when a track is modified.
+The modified track is passed as the argument to this function."
+ :group 'emms
+ :type 'function)
+
+(defcustom emms-directory (expand-file-name "emms" user-emacs-directory)
+ "*Directory variable from which all other emms file variables are derived."
+ :group 'emms
+ :type 'string)
+
+(defvar emms-player-playing-p nil
+ "The currently playing EMMS player, or nil.")
+
+(defvar emms-player-paused-p nil
+ "Whether the current player is paused or not.")
+
+(defvar emms-source-old-buffer nil
+ "The active buffer before a source was invoked.
+This can be used if the source depends on the current buffer not
+being the playlist buffer.")
+
+(defvar emms-playlist-buffer nil
+ "The current playlist buffer, if any.")
+
+
+;;; Macros
+
+;;; These need to be at the top of the file so that compilation works.
+
+(defmacro with-current-emms-playlist (&rest body)
+ "Run BODY with the current buffer being the current playlist buffer.
+This also disables any read-onliness of the current buffer."
+ `(progn
+ (when (or (not emms-playlist-buffer)
+ (not (buffer-live-p emms-playlist-buffer)))
+ (emms-playlist-current-clear))
+ (let ((emms-source-old-buffer (or emms-source-old-buffer
+ (current-buffer))))
+ (with-current-buffer emms-playlist-buffer
+ (let ((inhibit-read-only t))
+ ,@body)))))
+(put 'with-current-emms-playlist 'lisp-indent-function 0)
+(put 'with-current-emms-playlist 'edebug-form-spec '(body))
+
+(defmacro emms-with-inhibit-read-only-t (&rest body)
+ "Simple wrapper around `inhibit-read-only'."
+ `(let ((inhibit-read-only t))
+ ,@body))
+(put 'emms-with-inhibit-read-only-t 'edebug-form-spec '(body))
+
+(defmacro emms-with-widened-buffer (&rest body)
+ `(save-restriction
+ (widen)
+ ,@body))
+(put 'emms-with-widened-buffer 'edebug-form-spec '(body))
+
+(defmacro emms-walk-tracks (&rest body)
+ "Execute BODY for each track in the current buffer, starting at point.
+Point will be placed at the beginning of the track before
+executing BODY.
+
+Point will not be restored afterward."
+ (let ((donep (make-symbol "donep")))
+ `(let ((,donep nil))
+ ;; skip to first track if not on one
+ (unless (emms-playlist-track-at (point))
+ (condition-case nil
+ (emms-playlist-next)
+ (error
+ (setq ,donep t))))
+ ;; walk tracks
+ (while (not ,donep)
+ ,@body
+ (condition-case nil
+ (emms-playlist-next)
+ (error
+ (setq ,donep t)))))))
+(put 'emms-walk-tracks 'lisp-indent-function 0)
+(put 'emms-walk-tracks 'edebug-form-spec '(body))
+
+(defvar emms-player-base-format-list
+ '("ogg" "mp3" "wav" "mpg" "mpeg" "wmv" "wma"
+ "mov" "avi" "divx" "ogm" "ogv" "asf" "mkv"
+ "rm" "rmvb" "mp4" "flac" "vob" "m4a" "ape"
+ "flv" "webm" "aif")
+ "A list of common formats which player definitions can use.")
+
+
+;;; User Interface
+
+(defun emms-start ()
+ "Start playing the current track in the EMMS playlist."
+ (interactive)
+ (unless emms-player-playing-p
+ (emms-player-start (emms-playlist-current-selected-track))))
+
+(defun emms-stop ()
+ "Stop any current EMMS playback."
+ (interactive)
+ (when emms-player-playing-p
+ (emms-player-stop)))
+
+(defun emms-next ()
+ "Start playing the next track in the EMMS playlist.
+This might behave funny if called from `emms-player-next-function',
+so use `emms-next-noerror' in that case."
+ (interactive)
+ (when emms-player-playing-p
+ (emms-stop))
+ (emms-playlist-current-select-next)
+ (emms-start))
+
+(defun emms-next-noerror ()
+ "Start playing the next track in the EMMS playlist.
+Unlike `emms-next', this function doesn't signal an error when called
+at the end of the playlist.
+This function should only be called when no player is playing.
+This is a good function to put in `emms-player-next-function'."
+ (interactive)
+ (when emms-player-playing-p
+ (error "A track is already being played"))
+ (cond (emms-repeat-track
+ (emms-start))
+ (emms-single-track ; buffer local
+ (emms-stop))
+ ;; attempt to play the next track but ignore errors
+ ((condition-case nil
+ (progn
+ (emms-playlist-current-select-next)
+ t)
+ (error nil))
+ (if (funcall emms-ok-track-function
+ (emms-playlist-current-selected-track))
+ (emms-start)
+ (emms-next-noerror)))
+ (t
+ (message "No next track in playlist"))))
+
+(defun emms-previous ()
+ "Start playing the previous track in the EMMS playlist."
+ (interactive)
+ (when emms-player-playing-p
+ (emms-stop))
+ (emms-playlist-current-select-previous)
+ (emms-start))
+
+(defun emms-random ()
+ "Jump to a random track."
+ (interactive)
+ (when emms-player-playing-p
+ (emms-stop))
+ (emms-playlist-current-select-random)
+ (emms-start))
+
+(defun emms-pause ()
+ "Pause the current player.
+If player hasn't started, then start it now."
+ (interactive)
+ (if emms-player-playing-p
+ (emms-player-pause)
+ (emms-start)))
+
+(defun emms-seek (seconds)
+ "Seek the current player SECONDS seconds.
+This can be a floating point number for sub-second fractions.
+It can also be negative to seek backwards."
+ (interactive "nSeconds to seek: ")
+ (emms-ensure-player-playing-p)
+ (emms-player-seek seconds))
+
+(defun emms-seek-to (seconds)
+ "Seek the current player to SECONDS seconds.
+This can be a floating point number for sub-second fractions.
+It can also be negative to seek backwards."
+ (interactive "nSeconds to seek to: ")
+ (emms-ensure-player-playing-p)
+ (emms-player-seek-to seconds))
+
+(defun emms-seek-forward ()
+ "Seek ten seconds forward."
+ (interactive)
+ (when emms-player-playing-p
+ (emms-player-seek emms-seek-seconds)))
+
+(defun emms-seek-backward ()
+ "Seek ten seconds backward."
+ (interactive)
+ (when emms-player-playing-p
+ (emms-player-seek (- emms-seek-seconds))))
+
+(defun emms-show (&optional insertp)
+ "Describe the current EMMS track in the minibuffer.
+If INSERTP is non-nil, insert the description into the current buffer instead.
+This function uses `emms-show-format' to format the current track."
+ (interactive "P")
+ (let ((string (if emms-player-playing-p
+ (format emms-show-format
+ (emms-track-description
+ (emms-playlist-current-selected-track)))
+ "Nothing playing right now")))
+ (if insertp
+ (insert string)
+ (message "%s" string))))
+
+(defun emms-shuffle ()
+ "Shuffle the current playlist.
+This uses `emms-playlist-shuffle-function'."
+ (interactive)
+ (with-current-emms-playlist
+ (save-excursion
+ (funcall emms-playlist-shuffle-function))))
+
+(defun emms-sort ()
+ "Sort the current playlist.
+This uses `emms-playlist-sort-function'."
+ (interactive)
+ (with-current-emms-playlist
+ (save-excursion
+ (funcall emms-playlist-sort-function))))
+
+(defun emms-uniq ()
+ "Remove duplicates from the current playlist.
+This uses `emms-playlist-uniq-function'."
+ (interactive)
+ (with-current-emms-playlist
+ (save-excursion
+ (funcall emms-playlist-uniq-function))))
+
+(defun emms-toggle-single-track ()
+ "Toggle if Emms plays a single track and stops."
+ (interactive)
+ (with-current-emms-playlist
+ (cond (emms-single-track
+ (setq emms-single-track nil)
+ (message "single track mode disabled for %s"
+ (buffer-name)))
+ (t (setq emms-single-track t)
+ (message "single track mode enabled for %s"
+ (buffer-name))))))
+
+(defun emms-toggle-random-playlist ()
+ "Toggle whether emms plays the tracks randomly or sequentially.
+See `emms-random-playlist'."
+ (interactive)
+ (setq emms-random-playlist (not emms-random-playlist))
+ (if emms-random-playlist
+ (progn (setq emms-player-next-function 'emms-random)
+ (message "Will play the tracks randomly."))
+ (setq emms-player-next-function 'emms-next-noerror)
+ (message "Will play the tracks sequentially.")))
+
+(defun emms-toggle-repeat-playlist ()
+ "Toggle whether emms repeats the playlist after it is done.
+See `emms-repeat-playlist'."
+ (interactive)
+ (setq emms-repeat-playlist (not emms-repeat-playlist))
+ (if emms-repeat-playlist
+ (message "Will repeat the playlist after it is done.")
+ (message "Will stop after the playlist is over.")))
+
+(defun emms-toggle-repeat-track ()
+ "Toggle whether emms repeats the current track.
+See `emms-repeat-track'."
+ (interactive)
+ (setq emms-repeat-track (not emms-repeat-track))
+ (if emms-repeat-track
+ (message "Will repeat the current track.")
+ (message "Will advance to the next track after this one.")))
+
+(defun emms-sort-track-name-less-p (a b)
+ "Return non-nil if the track name of A sorts before B."
+ (string< (emms-track-name a)
+ (emms-track-name b)))
+
+(defun emms-ensure-player-playing-p ()
+ "Raise an error if no player is playing right now."
+ (when (not emms-player-playing-p)
+ (error "No EMMS player playing right now")))
+
+(defun emms-completing-read (&rest args)
+ "Read a string in the minibuffer, with completion.
+Set `emms-completing-read' to determine which function to use.
+
+See `completing-read' for a description of ARGS."
+ (apply emms-completing-read-function args))
+
+(defun emms-display-modes ()
+ "Display the current EMMS play modes."
+ (interactive)
+ (with-current-emms-playlist
+ (message
+ "repeat playlist: %s, repeat track: %s, random: %s, single %s"
+ (if emms-repeat-playlist "yes" "no")
+ (if emms-repeat-track "yes" "no")
+ (if emms-random-playlist "yes" "no")
+ (if emms-single-track "yes" "no"))))
+
+
+;;; Compatibility functions
+
+(require 'emms-compat)
+
+
+;;; Utility functions
+
+(defun emms-insert-file-contents (filename &optional visit)
+ "Insert the contents of file FILENAME after point.
+Do character code conversion and end-of-line conversion, but none
+of the other unnecessary things like format decoding or
+`find-file-hook'.
+
+If VISIT is non-nil, the buffer's visited filename
+and last save file modtime are set, and it is marked unmodified.
+If visiting and the file does not exist, visiting is completed
+before the error is signaled."
+ (let ((format-alist nil)
+ (after-insert-file-functions nil)
+ (inhibit-file-name-handlers
+ (append '(jka-compr-handler image-file-handler epa-file-handler)
+ inhibit-file-name-handlers))
+ (inhibit-file-name-operation 'insert-file-contents))
+ (insert-file-contents filename visit)))
+
+
+;;; Dictionaries
+
+;; This is a simple helper data structure, used by both players
+;; and tracks.
+
+(defsubst emms-dictionary (name)
+ "Create a new dictionary of type NAME."
+ (list name))
+
+(defsubst emms-dictionary-type (dict)
+ "Return the type of the dictionary DICT."
+ (car dict))
+
+(defun emms-dictionary-get (dict name &optional default)
+ "Return the value of NAME in DICT."
+ (let ((item (assq name (cdr dict))))
+ (if item
+ (cdr item)
+ default)))
+
+(defun emms-dictionary-set (dict name value)
+ "Set the value of NAME in DICT to VALUE."
+ (let ((item (assq name (cdr dict))))
+ (if item
+ (setcdr item value)
+ (setcdr dict (append (cdr dict)
+ (list (cons name value))))))
+ dict)
+
+
+;;; Tracks
+
+;; This is a simple datatype to store track information.
+;; Each track consists of a type (a symbol) and a name (a string).
+;; In addition, each track has an associated dictionary of information.
+
+(defun emms-track (type name)
+ "Create an EMMS track with type TYPE and name NAME."
+ (let ((track (when emms-cache-get-function
+ (funcall emms-cache-get-function type name))))
+ (when (not track)
+ (setq track (emms-dictionary '*track*))
+ ;; Prevent the cache from being called for these two sets
+ (let ((emms-cache-modified-function nil))
+ (emms-track-set track 'type type)
+ (emms-track-set track 'name name))
+ (when emms-cache-set-function
+ (funcall emms-cache-set-function type name track)))
+ ;; run any hooks regardless of a cache hit, as the entry may be
+ ;; old
+ (run-hook-with-args 'emms-track-initialize-functions track)
+ track))
+
+(defun emms-track-p (obj)
+ "True if OBJ is an emms track."
+ (and (listp obj)
+ (eq (car obj) '*track*)))
+
+(defun emms-track-type (track)
+ "Return the type of TRACK."
+ (emms-track-get track 'type))
+
+(defun emms-track-name (track)
+ "Return the name of TRACK."
+ (emms-track-get track 'name))
+
+(defun emms-track-get (track name &optional default)
+ "Return the value of NAME for TRACK.
+If there is no value, return DEFAULT (or nil, if not given)."
+ (emms-dictionary-get track name default))
+
+(defun emms-track-set (track name value)
+ "Set the value of NAME for TRACK to VALUE."
+ (emms-dictionary-set track name value)
+ (when emms-cache-modified-function
+ (funcall emms-cache-modified-function track)))
+
+(defun emms-track-description (track)
+ "Return a description of TRACK.
+This function uses the global value for
+`emms-track-description-function', rather than anything the
+current mode might have set.
+
+Use `emms-track-force-description' instead if you need to insert
+a description into a playlist buffer."
+ (funcall (default-value 'emms-track-description-function) track))
+
+(defun emms-track-updated (track)
+ "Information in TRACK got updated."
+ (run-hook-with-args 'emms-track-info-filters track)
+ (emms-playlist-track-updated track)
+ (run-hook-with-args 'emms-track-updated-functions track))
+
+(defun emms-track-simple-description (track)
+ "Simple function to give a user-readable description of a track.
+If it's a file track, just return the file name. Otherwise,
+return the type and the name with a colon in between.
+Hex-encoded characters in URLs are replaced by the decoded
+character."
+ (let ((type (emms-track-type track)))
+ (cond ((eq 'file type)
+ (emms-track-name track))
+ ((eq 'url type)
+ (emms-format-url-track-name (emms-track-name track)))
+ (t (concat (symbol-name type)
+ ": " (emms-track-name track))))))
+
+(defun emms-format-url-track-name (name)
+ "Format URL track name for better readability."
+ (url-unhex-string name))
+
+(defun emms-track-force-description (track)
+ "Always return text that describes TRACK.
+This is used when inserting a description into a buffer.
+
+The reason for this is that if no text was returned (i.e. the
+user defined a track function that returned nil or the empty
+string), a confusing error message would result."
+ (let ((desc (funcall emms-track-description-function track)))
+ (if (and (stringp desc) (not (string= desc "")))
+ desc
+ (emms-track-simple-description track))))
+
+(defun emms-track-get-year (track)
+ "Get year of TRACK for display.
+There is the separation between the 'release date' and the
+'original date'. This difference matters e.g. for
+re-releases (anniversaries and such) where the release date is
+more recent than the original release date. In such cases the
+user probably wants the original release date so this is what we
+show."
+ (or
+ (emms-format-date-to-year (emms-track-get track 'info-date))
+ (emms-format-date-to-year (emms-track-get track 'info-originaldate))
+ (emms-track-get track 'info-year)
+ (emms-track-get track 'info-originalyear)))
+
+(defun emms-format-date-to-year (date)
+ "Try to extract year part from DATE.
+Return nil if the year cannot be extracted."
+ (when date
+ (let ((year (nth 5 (parse-time-string date))))
+ (if year (number-to-string year)
+ (when (string-match "^[ \t]*\\([0-9]\\{4\\}\\)" date)
+ (match-string 1 date))))))
+
+
+;;; The Playlist
+
+;; Playlists are stored in buffers. The current playlist buffer is
+;; remembered in the `emms-playlist' variable. The buffer consists of
+;; any kind of data. Strings of text with a `emms-track' property are
+;; the tracks in the buffer.
+
+(defvar emms-playlist-buffers nil
+ "The list of EMMS playlist buffers.
+You should use the `emms-playlist-buffer-list' function to
+retrieve a current list of EMMS buffers. Never use this variable
+for that purpose.")
+
+(defvar emms-playlist-selected-marker nil
+ "The marker for the currently selected track.")
+(make-variable-buffer-local 'emms-playlist-selected-marker)
+
+(defvar emms-playlist-buffer-p nil
+ "Non-nil if the current buffer is an EMMS playlist.")
+(make-variable-buffer-local 'emms-playlist-buffer-p)
+
+(defun emms-playlist-ensure-playlist-buffer ()
+ "Throw an error if we're not in a playlist-buffer."
+ (when (not emms-playlist-buffer-p)
+ (error "Not an EMMS playlist buffer")))
+
+(defun emms-playlist-set-playlist-buffer (&optional buffer)
+ "Set the current playlist buffer."
+ (interactive
+ (list (let* ((buf-list (mapcar #'(lambda (buf)
+ (list (buffer-name buf)))
+ (emms-playlist-buffer-list)))
+ (sorted-buf-list (sort buf-list
+ #'(lambda (lbuf rbuf)
+ (< (length (car lbuf))
+ (length (car rbuf))))))
+ (default (or (and emms-playlist-buffer-p
+ ;; default to current buffer
+ (buffer-name))
+ ;; pick shortest buffer name, since it is
+ ;; likely to be a shared prefix
+ (car sorted-buf-list))))
+ (emms-completing-read "Playlist buffer to make current: "
+ sorted-buf-list nil t default))))
+ (let ((buf (if buffer
+ (get-buffer buffer)
+ (current-buffer))))
+ (with-current-buffer buf
+ (emms-playlist-ensure-playlist-buffer))
+ (setq emms-playlist-buffer buf)
+ (when (called-interactively-p 'interactive)
+ (message "Set current EMMS playlist buffer"))
+ buf))
+
+(defun emms-playlist-new (&optional name)
+ "Create a new playlist buffer.
+The buffer is named NAME, but made unique. NAME defaults to
+`emms-playlist-buffer-name'. If called interactively, the new
+buffer is also selected."
+ (interactive)
+ (let ((buf (generate-new-buffer (or name
+ emms-playlist-buffer-name))))
+ (with-current-buffer buf
+ (when (not (eq major-mode emms-playlist-default-major-mode))
+ (funcall emms-playlist-default-major-mode))
+ (setq emms-playlist-buffer-p t))
+ (add-to-list 'emms-playlist-buffers buf)
+ (when (called-interactively-p 'interactive)
+ (switch-to-buffer buf))
+ buf))
+
+(defun emms-playlist-buffer-list ()
+ "Return a list of EMMS playlist buffers.
+The first element is guaranteed to be the current EMMS playlist
+buffer, if it exists, otherwise the slot will be used for the
+other EMMS buffers. The list will be in newest-first order."
+ ;; prune dead buffers
+ (setq emms-playlist-buffers (emms-delete-if (lambda (buf)
+ (not (buffer-live-p buf)))
+ emms-playlist-buffers))
+ ;; add new buffers
+ (mapc (lambda (buf)
+ (when (buffer-live-p buf)
+ (with-current-buffer buf
+ (when (and emms-playlist-buffer-p
+ (not (memq buf emms-playlist-buffers)))
+ (setq emms-playlist-buffers
+ (cons buf emms-playlist-buffers))))))
+ (buffer-list))
+ ;; force current playlist buffer to head position
+ (when (and (buffer-live-p emms-playlist-buffer)
+ (not (eq (car emms-playlist-buffers) emms-playlist-buffer)))
+ (setq emms-playlist-buffers (cons emms-playlist-buffer
+ (delete emms-playlist-buffer
+ emms-playlist-buffers))))
+ emms-playlist-buffers)
+
+(defun emms-playlist-current-kill ()
+ "Kill the current EMMS playlist buffer and switch to the next one."
+ (interactive)
+ (when (buffer-live-p emms-playlist-buffer)
+ (let ((new (cadr (emms-playlist-buffer-list))))
+ (if new
+ (let ((old emms-playlist-buffer))
+ (setq emms-playlist-buffer new
+ emms-playlist-buffers (cdr emms-playlist-buffers))
+ (kill-buffer old)
+ (switch-to-buffer emms-playlist-buffer))
+ (with-current-buffer emms-playlist-buffer
+ (bury-buffer))))))
+
+(defun emms-playlist-current-clear ()
+ "Clear the current playlist.
+If no current playlist exists, a new one is generated."
+ (interactive)
+ (if (or (not emms-playlist-buffer)
+ (not (buffer-live-p emms-playlist-buffer)))
+ (setq emms-playlist-buffer (emms-playlist-new))
+ (with-current-buffer emms-playlist-buffer
+ (emms-playlist-clear))))
+
+(defun emms-playlist-clear ()
+ "Clear the current buffer."
+ (interactive)
+ (emms-playlist-ensure-playlist-buffer)
+ (let ((inhibit-read-only t))
+ (widen)
+ (delete-region (point-min)
+ (point-max)))
+ (run-hooks 'emms-playlist-cleared-hook))
+
+;;; Point movement within the playlist buffer.
+(defun emms-playlist-track-at (&optional pos)
+ "Return the track at POS (point if not given), or nil if none."
+ (emms-playlist-ensure-playlist-buffer)
+ (emms-with-widened-buffer
+ (get-text-property (or pos (point))
+ 'emms-track)))
+
+(defun emms-playlist-next ()
+ "Move to the next track in the current buffer."
+ (emms-playlist-ensure-playlist-buffer)
+ (let ((next (next-single-property-change (point)
+ 'emms-track)))
+ (when (not next)
+ (error "No next track"))
+ (when (not (emms-playlist-track-at next))
+ (setq next (next-single-property-change next 'emms-track)))
+ (when (or (not next)
+ (= next (point-max)))
+ (error "No next track"))
+ (goto-char next)))
+
+(defun emms-playlist-previous ()
+ "Move to the previous track in the current buffer."
+ (emms-playlist-ensure-playlist-buffer)
+ (let ((prev (previous-single-property-change (point)
+ 'emms-track)))
+ (when (not prev)
+ (error "No previous track"))
+ (when (not (get-text-property prev 'emms-track))
+ (setq prev (or (previous-single-property-change prev 'emms-track)
+ (point-min))))
+ (when (or (not prev)
+ (not (get-text-property prev 'emms-track)))
+ (error "No previous track"))
+ (goto-char prev)))
+
+(defun emms-playlist-first ()
+ "Move to the first track in the current buffer."
+ (emms-playlist-ensure-playlist-buffer)
+ (let ((first (condition-case nil
+ (save-excursion
+ (goto-char (point-min))
+ (when (not (emms-playlist-track-at (point)))
+ (emms-playlist-next))
+ (point))
+ (error
+ nil))))
+ (if first
+ (goto-char first)
+ (error "No first track"))))
+
+(defun emms-playlist-last ()
+ "Move to the last track in the current buffer."
+ (emms-playlist-ensure-playlist-buffer)
+ (let ((last (condition-case nil
+ (save-excursion
+ (goto-char (point-max))
+ (emms-playlist-previous)
+ (point))
+ (error
+ nil))))
+ (if last
+ (goto-char last)
+ (error "No last track"))))
+
+(defun emms-playlist-delete-track ()
+ "Delete the track at point."
+ (emms-playlist-ensure-playlist-buffer)
+ (funcall emms-playlist-delete-track-function))
+
+;;; Track selection
+(defun emms-playlist-selected-track ()
+ "Return the currently selected track."
+ (emms-playlist-ensure-playlist-buffer)
+ (when emms-playlist-selected-marker
+ (emms-playlist-track-at emms-playlist-selected-marker)))
+
+(defun emms-playlist-current-selected-track ()
+ "Return the currently selected track in the current playlist."
+ (with-current-emms-playlist
+ (emms-playlist-selected-track)))
+
+(defun emms-playlist-selected-track-at-p (&optional point)
+ "Return non-nil if POINT (defaulting to point) is on the selected track."
+ (when emms-playlist-selected-marker
+ (or (= emms-playlist-selected-marker
+ (or point (point)))
+ (let ((p (previous-single-property-change (or point (point))
+ 'emms-track)))
+ (when p
+ (= emms-playlist-selected-marker
+ p))))))
+
+(defun emms-playlist-select (pos)
+ "Select the track at POS."
+ (emms-playlist-ensure-playlist-buffer)
+ (when (not (emms-playlist-track-at pos))
+ (error "No track at position %s" pos))
+ (when (not emms-playlist-selected-marker)
+ (setq emms-playlist-selected-marker (make-marker)))
+ (set-marker-insertion-type emms-playlist-selected-marker t)
+ (set-marker emms-playlist-selected-marker pos)
+ (run-hooks 'emms-playlist-selection-changed-hook))
+
+(defun emms-playlist-select-next ()
+ "Select the next track in the current buffer."
+ (emms-playlist-ensure-playlist-buffer)
+ (save-excursion
+ (goto-char (if (and emms-playlist-selected-marker
+ (marker-position emms-playlist-selected-marker))
+ emms-playlist-selected-marker
+ (point-min)))
+ (condition-case nil
+ (progn
+ (if emms-repeat-playlist
+ (condition-case nil
+ (emms-playlist-next)
+ (error
+ (emms-playlist-first)))
+ (emms-playlist-next))
+ (emms-playlist-select (point)))
+ (error
+ (error "No next track in playlist")))))
+
+(defun emms-playlist-current-select-next ()
+ "Select the next track in the current playlist."
+ (with-current-emms-playlist
+ (emms-playlist-select-next)))
+
+(defun emms-playlist-select-previous ()
+ "Select the previous track in the current buffer."
+ (emms-playlist-ensure-playlist-buffer)
+ (save-excursion
+ (goto-char (if (and emms-playlist-selected-marker
+ (marker-position emms-playlist-selected-marker))
+ emms-playlist-selected-marker
+ (point-max)))
+ (condition-case nil
+ (progn
+ (if emms-repeat-playlist
+ (condition-case nil
+ (emms-playlist-previous)
+ (error
+ (emms-playlist-last)))
+ (emms-playlist-previous))
+ (emms-playlist-select (point)))
+ (error
+ (error "No previous track in playlist")))))
+
+(defun emms-playlist-current-select-previous ()
+ "Select the previous track in the current playlist."
+ (with-current-emms-playlist
+ (emms-playlist-select-previous)))
+
+(defun emms-playlist-select-random ()
+ "Select a random track in the current buffer."
+ (emms-playlist-ensure-playlist-buffer)
+ ;; FIXME: This is rather inefficient.
+ (save-excursion
+ (let ((track-indices nil))
+ (goto-char (point-min))
+ (emms-walk-tracks
+ (setq track-indices (cons (point)
+ track-indices)))
+ (setq track-indices (vconcat track-indices))
+ (emms-playlist-select (aref track-indices
+ (random (length track-indices)))))))
+
+(defun emms-playlist-current-select-random ()
+ "Select a random track in the current playlist."
+ (with-current-emms-playlist
+ (emms-playlist-select-random)))
+
+(defun emms-playlist-select-first ()
+ "Select the first track in the current buffer."
+ (emms-playlist-ensure-playlist-buffer)
+ (save-excursion
+ (emms-playlist-first)
+ (emms-playlist-select (point))))
+
+(defun emms-playlist-current-select-first ()
+ "Select the first track in the current playlist."
+ (with-current-emms-playlist
+ (emms-playlist-select-first)))
+
+(defun emms-playlist-select-last ()
+ "Select the last track in the current buffer."
+ (emms-playlist-ensure-playlist-buffer)
+ (save-excursion
+ (emms-playlist-last)
+ (emms-playlist-select (point))))
+
+(defun emms-playlist-current-select-last ()
+ "Select the last track in the current playlist."
+ (with-current-emms-playlist
+ (emms-playlist-select-last)))
+
+;;; Playlist manipulation
+(defun emms-playlist-insert-track (track)
+ "Insert TRACK at the current position into the playlist.
+This uses `emms-playlist-insert-track-function'."
+ (emms-playlist-ensure-playlist-buffer)
+ (funcall emms-playlist-insert-track-function track))
+
+(defun emms-playlist-update-track ()
+ "Update TRACK at point.
+This uses `emms-playlist-update-track-function'."
+ (emms-playlist-ensure-playlist-buffer)
+ (funcall emms-playlist-update-track-function))
+
+(defun emms-playlist-insert-source (source &rest args)
+ "Insert tracks from SOURCE, supplying ARGS as arguments."
+ (emms-playlist-ensure-playlist-buffer)
+ (save-restriction
+ (narrow-to-region (point)
+ (point))
+ (apply source args)
+ (run-hooks 'emms-playlist-source-inserted-hook)))
+
+(defun emms-playlist-current-insert-source (source &rest args)
+ "Insert tracks from SOURCE in the current playlist.
+This is supplying ARGS as arguments to the source."
+ (with-current-emms-playlist
+ (apply 'emms-playlist-insert-source source args)))
+
+(defun emms-playlist-tracks-in-region (beg end)
+ "Return all tracks between BEG and END."
+ (emms-playlist-ensure-playlist-buffer)
+ (let ((tracks nil))
+ (save-restriction
+ (narrow-to-region beg end)
+ (goto-char (point-min))
+ (emms-walk-tracks
+ (setq tracks (cons (emms-playlist-track-at (point))
+ tracks))))
+ tracks))
+
+(defun emms-playlist-track-updated (track)
+ "Update TRACK in all playlist buffers."
+ (mapc (lambda (buf)
+ (with-current-buffer buf
+ (when emms-playlist-buffer-p
+ (save-excursion
+ (let ((pos (text-property-any (point-min) (point-max)
+ 'emms-track track)))
+ (while pos
+ (goto-char pos)
+ (emms-playlist-update-track)
+ (setq pos (text-property-any
+ (next-single-property-change (point)
+ 'emms-track)
+ (point-max)
+ 'emms-track
+ track))))))))
+ (buffer-list))
+ t)
+
+;;; Simple playlist buffer
+(defun emms-playlist-simple-insert-track (track)
+ "Insert the description of TRACK at point."
+ (emms-playlist-ensure-playlist-buffer)
+ (let ((inhibit-read-only t))
+ (insert (emms-propertize (emms-track-force-description track)
+ 'emms-track track)
+ "\n")))
+
+(defun emms-playlist-simple-update-track ()
+ "Update the track at point.
+Since we don't do anything special with the track anyway, just
+ignore this."
+ nil)
+
+(defun emms-playlist-simple-delete-track ()
+ "Delete the track at point."
+ (emms-playlist-ensure-playlist-buffer)
+ (when (not (emms-playlist-track-at (point)))
+ (error "No track at point"))
+ (let ((inhibit-read-only t)
+ (region (emms-property-region (point) 'emms-track)))
+ (delete-region (car region)
+ (cdr region))))
+
+(defun emms-playlist-simple-shuffle ()
+ "Shuffle the whole playlist buffer."
+ (emms-playlist-ensure-playlist-buffer)
+ (let ((inhibit-read-only t)
+ (current nil))
+ (widen)
+ (when emms-player-playing-p
+ (setq current (emms-playlist-selected-track))
+ (goto-char emms-playlist-selected-marker)
+ (emms-playlist-delete-track))
+ (let* ((tracks (vconcat (emms-playlist-tracks-in-region (point-min)
+ (point-max))))
+ (len (length tracks))
+ (i 0))
+ (delete-region (point-min)
+ (point-max))
+ (run-hooks 'emms-playlist-cleared-hook)
+ (emms-shuffle-vector tracks)
+ (when current
+ (emms-playlist-insert-track current))
+ (while (< i len)
+ (emms-playlist-insert-track (aref tracks i))
+ (setq i (1+ i))))
+ (emms-playlist-select-first)
+ (goto-char (point-max))))
+
+(defun emms-playlist-simple-sort ()
+ "Sort the whole playlist buffer."
+ (emms-playlist-ensure-playlist-buffer)
+ (widen)
+ (let ((inhibit-read-only t)
+ (current (emms-playlist-selected-track))
+ (tracks (emms-playlist-tracks-in-region (point-min)
+ (point-max))))
+ (delete-region (point-min)
+ (point-max))
+ (run-hooks 'emms-playlist-cleared-hook)
+ (mapc 'emms-playlist-insert-track
+ (sort tracks emms-sort-lessp-function))
+ (let ((pos (text-property-any (point-min)
+ (point-max)
+ 'emms-track current)))
+ (if pos
+ (emms-playlist-select pos)
+ (emms-playlist-first)))))
+
+(defun emms-uniq-list (list stringify)
+ "Compare stringfied element of list, and remove duplicate elements."
+ ;; This uses a fast append list, keeping a pointer to the last cons
+ ;; cell of the list (TAIL). It might be worthwhile to provide an
+ ;; abstraction for this eventually.
+ (let* ((hash (make-hash-table :test 'equal))
+ (result (cons nil nil))
+ (tail result))
+ (dolist (element list)
+ (let ((str (funcall stringify element)))
+ (when (not (gethash str hash))
+ (setcdr tail (cons element nil))
+ (setq tail (cdr tail)))
+ (puthash str t hash)))
+ (cdr result)))
+
+(defun emms-playlist-simple-uniq ()
+ "Remove duplicate tracks."
+ ;; TODO: This seems unnecessarily destructive.
+ (emms-playlist-ensure-playlist-buffer)
+ (widen)
+ (let ((inhibit-read-only t)
+ (current (emms-playlist-selected-track))
+ (tracks (emms-playlist-tracks-in-region (point-min)
+ (point-max))))
+ (delete-region (point-min) (point-max))
+ (run-hooks 'emms-playlist-cleared-hook)
+ (mapc 'emms-playlist-insert-track
+ (nreverse
+ (emms-uniq-list tracks 'emms-track-name)))
+ (let ((pos (text-property-any (point-min)
+ (point-max)
+ 'emms-track current)))
+ (if pos
+ (emms-playlist-select pos)
+ (emms-playlist-first)))))
+
+(defun emms-default-ok-track-function (track)
+ "A function which OKs all tracks for playing by default."
+ t)
+
+;;; Helper functions
+(defun emms-property-region (pos prop)
+ "Return a pair of the beginning and end of the property PROP at POS.
+If POS does not contain PROP, try to find PROP just before POS."
+ (let (begin end)
+ (if (and (> pos (point-min))
+ (get-text-property (1- pos) prop))
+ (setq begin (previous-single-property-change (1- pos) prop))
+ (if (get-text-property pos prop)
+ (setq begin pos)
+ (error "Cannot find the %s property at the given position" prop)))
+ (if (get-text-property pos prop)
+ (setq end (next-single-property-change pos prop))
+ (if (and (> pos (point-min))
+ (get-text-property (1- pos) prop))
+ (setq end pos)
+ (error "Cannot find the %s property at the given position" prop)))
+ (cons (or begin (point-min))
+ (or end (point-max)))))
+
+(defun emms-shuffle-vector (vector)
+ "Shuffle VECTOR."
+ (let ((i (- (length vector) 1)))
+ (while (>= i 0)
+ (let* ((r (random (1+ i)))
+ (old (aref vector r)))
+ (aset vector r (aref vector i))
+ (aset vector i old))
+ (setq i (- i 1))))
+ vector)
+
+
+;;; Sources
+
+;; A source is just a function which is called in a playlist buffer.
+;; It should use `emms-playlist-insert-track' to insert the tracks it
+;; knows about.
+;;
+;; The define-emms-source macro also defines functions
+;; emms-play-SOURCE and emms-add-SOURCE. The former will replace the
+;; current playlist, while the latter will add to the end.
+
+(defmacro define-emms-source (name arglist &rest body)
+ "Define a new EMMS source called NAME.
+This macro defines three functions: `emms-source-NAME',
+`emms-play-NAME' and `emms-add-NAME'. BODY should use
+`emms-playlist-insert-track' to insert all tracks to be played,
+which is exactly what `emms-source-NAME' will do. The other two
+functions will be simple wrappers around `emms-source-NAME'; any
+`interactive' form that you specify in BODY will end up in these.
+See emms-source-file.el for some examples."
+ (let ((source-name (intern (format "emms-source-%s" name)))
+ (source-play (intern (format "emms-play-%s" name)))
+ (source-add (intern (format "emms-add-%s" name)))
+ (source-insert (intern (format "emms-insert-%s" name)))
+ (docstring "A source of tracks for EMMS.")
+ (interactive nil)
+ (call-args (delete '&rest
+ (delete '&optional
+ arglist))))
+ (when (stringp (car body))
+ (setq docstring (car body)
+ body (cdr body)))
+ (when (eq 'interactive (caar body))
+ (setq interactive (car body)
+ body (cdr body)))
+ `(progn
+ (defun ,source-name ,arglist
+ ,docstring
+ ,@body)
+ (defun ,source-play ,arglist
+ ,docstring
+ ,interactive
+ (if current-prefix-arg
+ (let ((current-prefix-arg nil))
+ (emms-source-add ',source-name ,@call-args))
+ (emms-source-play ',source-name ,@call-args)))
+ (defun ,source-add ,arglist
+ ,docstring
+ ,interactive
+ (if current-prefix-arg
+ (let ((current-prefix-arg nil))
+ (emms-source-play ',source-name ,@call-args))
+ (emms-source-add ',source-name ,@call-args)))
+ (defun ,source-insert ,arglist
+ ,docstring
+ ,interactive
+ (emms-source-insert ',source-name ,@call-args)))))
+
+(defun emms-source-play (source &rest args)
+ "Play the tracks of SOURCE, after first clearing the EMMS playlist."
+ (emms-stop)
+ (emms-playlist-current-clear)
+ (apply 'emms-playlist-current-insert-source source args)
+ (emms-playlist-current-select-first)
+ (emms-start))
+
+(defun emms-source-add (source &rest args)
+ "Add the tracks of SOURCE at the current position in the playlist."
+ (with-current-emms-playlist
+ (save-excursion
+ (goto-char (point-max))
+ (apply 'emms-playlist-current-insert-source source args))
+ (when (or (not emms-playlist-selected-marker)
+ (not (marker-position emms-playlist-selected-marker)))
+ (emms-playlist-select-first))))
+
+(defun emms-source-insert (source &rest args)
+ "Insert the tracks from SOURCE in the current buffer."
+ (if (not emms-playlist-buffer-p)
+ (error "Not in an EMMS playlist buffer")
+ (apply 'emms-playlist-insert-source source args)))
+
+;;; User-defined playlists
+;;; FIXME: Shuffle is bogus here! (because of narrowing)
+(defmacro define-emms-combined-source (name shufflep sources)
+ "Define a `emms-play-X' and `emms-add-X' function for SOURCES."
+ `(define-emms-source ,name ()
+ "An EMMS source for a tracklist."
+ (interactive)
+ (mapc (lambda (source)
+ (apply (car source)
+ (cdr source)))
+ ,sources)
+ ,(when shufflep
+ '(save-restriction
+ (widen)
+ (emms-shuffle)))))
+
+
+;;; Players
+
+;; A player is a data structure created by `emms-player'.
+;; See the docstring of that function for more information.
+
+(defvar emms-player-stopped-p nil
+ "Non-nil if the last EMMS player was stopped by the user.")
+
+(defun emms-player (start stop playablep)
+ "Create a new EMMS player.
+The start function will be START, and the stop function STOP.
+PLAYABLEP should return non-nil for tracks that this player can
+play.
+
+When trying to play a track, EMMS walks through
+`emms-player-list'. For each player, it calls the PLAYABLEP
+function. The player corresponding to the first PLAYABLEP
+function that returns non-nil is used to play the track. To
+actually play the track, EMMS calls the START function, passing
+the chosen track as a parameter.
+
+If the user tells EMMS to stop playing, the STOP function is
+called. Once the player has finished playing, it should call
+`emms-player-stopped' to let EMMS know."
+ (let ((p (emms-dictionary '*player*)))
+ (emms-player-set p 'start start)
+ (emms-player-set p 'stop stop)
+ (emms-player-set p 'playablep playablep)
+ p))
+
+(defun emms-player-get (player name &optional inexistent)
+ "Return the value of entry NAME in PLAYER."
+ (let ((p (if (symbolp player)
+ (symbol-value player)
+ player)))
+ (emms-dictionary-get p name inexistent)))
+
+(defun emms-player-set (player name value)
+ "Set the value of entry NAME in PLAYER to VALUE."
+ (let ((p (if (symbolp player)
+ (symbol-value player)
+ player)))
+ (emms-dictionary-set p name value)))
+
+(defun emms-player-for (track)
+ "Return an EMMS player capable of playing TRACK.
+This will be the first player whose PLAYABLEP function returns
+non-nil, or nil if no such player exists."
+ (let ((lis emms-player-list))
+ (while (and lis
+ (not (funcall (emms-player-get (car lis) 'playablep)
+ track)))
+ (setq lis (cdr lis)))
+ (if lis
+ (car lis)
+ nil)))
+
+(defun emms-player-start (track)
+ "Start playing TRACK."
+ (if emms-player-playing-p
+ (error "A player is already playing")
+ (let ((player (emms-player-for track)))
+ (if (not player)
+ (error "Don't know how to play track: %S" track)
+ ;; Change default-directory so we don't accidentally block any
+ ;; directories the current buffer was visiting.
+ (let ((default-directory "/"))
+ (funcall (emms-player-get player 'start)
+ track))))))
+
+(defun emms-player-started (player)
+ "Declare that the given EMMS PLAYER has started.
+This should only be done by the current player itself."
+ (setq emms-player-playing-p player
+ emms-player-paused-p nil)
+ (run-hooks 'emms-player-started-hook))
+
+(defun emms-player-stop ()
+ "Stop the current EMMS player."
+ (when emms-player-playing-p
+ (let ((emms-player-stopped-p t))
+ (funcall (emms-player-get emms-player-playing-p 'stop)))
+ (setq emms-player-playing-p nil)))
+
+(defun emms-player-stopped ()
+ "Declare that the current EMMS player is finished.
+This should only be done by the current player itself."
+ (setq emms-player-playing-p nil)
+ (if emms-player-stopped-p
+ (run-hooks 'emms-player-stopped-hook)
+ (sleep-for emms-player-delay)
+ (run-hooks 'emms-player-finished-hook)
+ (funcall emms-player-next-function)))
+
+(defun emms-player-pause ()
+ "Pause the current EMMS player."
+ (cond
+ ((not emms-player-playing-p)
+ (error "Can't pause player, nothing is playing"))
+ (emms-player-paused-p
+ (let ((resume (emms-player-get emms-player-playing-p 'resume))
+ (pause (emms-player-get emms-player-playing-p 'pause)))
+ (cond
+ (resume
+ (funcall resume))
+ (pause
+ (funcall pause))
+ (t
+ (error "Player does not know how to pause"))))
+ (setq emms-player-paused-p nil)
+ (run-hooks 'emms-player-paused-hook))
+ (t
+ (let ((pause (emms-player-get emms-player-playing-p 'pause)))
+ (if pause
+ (funcall pause)
+ (error "Player does not know how to pause")))
+ (setq emms-player-paused-p t)
+ (run-hooks 'emms-player-paused-hook))))
+
+(defun emms-player-seek (seconds)
+ "Seek the current player by SECONDS seconds.
+This can be a floating point number for fractions of a second, or
+negative to seek backwards."
+ (if (not emms-player-playing-p)
+ (error "Can't seek player, nothing playing right now")
+ (let ((seek (emms-player-get emms-player-playing-p 'seek)))
+ (if (not seek)
+ (error "Player does not know how to seek")
+ (funcall seek seconds)
+ (run-hook-with-args 'emms-player-seeked-functions seconds)))))
+
+(defun emms-player-seek-to (seconds)
+ "Seek the current player to SECONDS seconds.
+This can be a floating point number for fractions of a second, or
+negative to seek backwards."
+ (if (not emms-player-playing-p)
+ (error "Can't seek-to player, nothing playing right now")
+ (let ((seek (emms-player-get emms-player-playing-p 'seek-to)))
+ (if (not seek)
+ (error "Player does not know how to seek-to")
+ (funcall seek seconds)
+ (run-hook-with-args 'emms-player-time-set-functions seconds)))))
+
+(provide 'emms)
+;;; emms.el ends here
diff --git a/lisp/jack.el b/jack.el
index d248a12..d248a12 100644
--- a/lisp/jack.el
+++ b/jack.el
diff --git a/lisp/later-do.el b/later-do.el
index 5908ac5..5908ac5 100644
--- a/lisp/later-do.el
+++ b/later-do.el
diff --git a/lisp/Makefile b/lisp/Makefile
deleted file mode 100644
index 85ff3e1..0000000
--- a/lisp/Makefile
+++ /dev/null
@@ -1,28 +0,0 @@
-EMACS=emacs
-SITEFLAG=--no-site-file
-ALLSOURCE=$(wildcard *.el)
-ALLCOMPILED=$(wildcard *.elc)
-SPECIAL=emms-auto.el emms-maint.el
-SOURCE=$(filter-out $(SPECIAL),$(ALLSOURCE))
-TARGET=$(patsubst %.el,%.elc,$(SOURCE))
-
-.PHONY: all clean
-.PRECIOUS: %.elc
-all: emms-auto.el $(TARGET)
-
-emms-auto.el: emms-auto.in $(SOURCE)
- cp emms-auto.in emms-auto.el
- -rm -f emms-auto.elc
- @$(EMACS) -q $(SITEFLAG) -batch \
- -l emms-maint.el \
- -l emms-auto.el \
- -f emms-generate-autoloads \
- $(shell pwd)/emms-auto.el .
-
-%.elc: %.el
- @$(EMACS) -q $(SITEFLAG) -batch \
- -l emms-maint.el \
- -f batch-byte-compile $<
-
-clean:
- -rm -f *~ *.elc emms-auto.el
diff --git a/lisp/emms.el b/lisp/emms.el
deleted file mode 100644
index ff786b7..0000000
--- a/lisp/emms.el
+++ /dev/null
@@ -1,1539 +0,0 @@
-;;; emms.el --- The Emacs Multimedia System
-
-;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008,
-;; 2009, 2018, 2020 Free Software Foundation, Inc.
-
-;; Author: Jorgen Schäfer <forcer@forcix.cx>, the Emms developers (see AUTHORS file)
-;; Maintainer: Yoni Rabkin <yrk@gnu.org>
-;; Version: 5.4
-;; Keywords: emms, mp3, ogg, flac, music, mpeg, video, multimedia
-;; Package-Requires: ((cl-lib "0.5"))
-;; url: https://www.gnu.org/software/emms/
-
-;; This file is part of EMMS.
-
-;; EMMS 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, or (at your option)
-;; any later version.
-
-;; EMMS 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 program. If not, see <https://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This is the very core of EMMS. It provides ways to play a track
-;; using `emms-start', to go through the playlist using the commands
-;; `emms-next' and `emms-previous', to stop the playback using
-;; `emms-stop', and to see what's currently playing using `emms-show'.
-
-;; But in itself, this core is useless, because it doesn't know how to
-;; play any tracks --- you need players for this. In fact, it doesn't
-;; even know how to find any tracks to consider playing --- for this,
-;; you need sources.
-
-;; A sample configuration is offered in emms-setup.el, and the
-;; Friendly Manual in the doc/ directory is both detailed, and kept up
-;; to date.
-
-
-;;; Code:
-
-(defvar emms-version "5.4"
- "EMMS version string.")
-
-
-;;; User Customization
-
-(defgroup emms nil
- "*The Emacs Multimedia System."
- :prefix "emms-"
- :group 'multimedia
- :group 'applications)
-
-(defgroup emms-player nil
- "*Track players for EMMS."
- :prefix "emms-player-"
- :group 'emms)
-
-(defgroup emms-source nil
- "*Track sources for EMMS."
- :prefix "emms-source-"
- :group 'emms)
-
-(defcustom emms-player-list nil
- "*List of players that EMMS can use. You need to set this!"
- :group 'emms
- :type '(repeat (symbol :tag "Player")))
-
-(defcustom emms-show-format "Currently playing: %s"
- "*The format to use for `emms-show'.
-Any \"%s\" is replaced by what `emms-track-description-function' returns
-for the currently playing track."
- :group 'emms
- :type 'string)
-
-(defcustom emms-repeat-playlist nil
- "*Non-nil if the EMMS playlist should automatically repeat.
-If nil, playback will stop when the last track finishes playing.
-If non-nil, EMMS will wrap back to the first track when that happens."
- :group 'emms
- :type 'boolean)
-
-(defcustom emms-random-playlist nil
- "*Non-nil means that tracks are played randomly. If nil, tracks
-are played sequentially."
- :group 'emms
- :type 'boolean)
-
-(defcustom emms-repeat-track nil
- "Non-nil, playback will repeat current track. If nil, EMMS will play
-track by track normally."
- :group 'emms
- :type 'boolean)
-
-(defvar-local emms-single-track nil
- "Non-nil, play the current track and then stop.")
-
-(defcustom emms-completing-read-function
- (if (and (boundp 'ido-mode)
- ido-mode)
- 'ido-completing-read
- 'completing-read)
- "Function to call when prompting user to choose between a list of options.
-This should take the same arguments as `completing-read'. Some
-possible values are `completing-read' and `ido-completing-read'.
-Note that you must set `ido-mode' if using
-`ido-completing-read'."
- :group 'emms
- :type 'function)
-
-(defcustom emms-track-description-function 'emms-track-simple-description
- "*Function for describing an EMMS track in a user-friendly way."
- :group 'emms
- :type 'function)
-
-(defcustom emms-player-delay 0
- "The delay to pause after a player finished.
-This is a floating-point number of seconds. This is necessary
-for some platforms where it takes a bit to free the audio device
-after a player has finished. If EMMS is skipping songs, increase
-this number."
- :type 'number
- :group 'emms)
-
-(defcustom emms-playlist-shuffle-function 'emms-playlist-simple-shuffle
- "*The function to use for shuffling the playlist."
- :type 'function
- :group 'emms)
-
-(defcustom emms-playlist-sort-function 'emms-playlist-simple-sort
- "*The function to use for sorting the playlist."
- :type 'function
- :group 'emms)
-
-(defcustom emms-playlist-uniq-function 'emms-playlist-simple-uniq
- "*The function to use for removing duplicate tracks in the playlist."
- :type 'function
- :group 'emms)
-
-(defcustom emms-sort-lessp-function 'emms-sort-track-name-less-p
- "*Function for comparing two EMMS tracks.
-The function should return non-nil if and only if the first track
-sorts before the second (see `sort')."
- :group 'emms
- :type 'function)
-
-(defcustom emms-playlist-buffer-name " *EMMS Playlist*"
- "*The default name of the EMMS playlist buffer."
- :type 'string
- :group 'emms)
-
-(defcustom emms-playlist-default-major-mode 'emms-playlist-mode
- "*The default major mode for EMMS playlist."
- :type 'function
- :group 'emms)
-
-(defcustom emms-playlist-insert-track-function 'emms-playlist-simple-insert-track
- "*A function to insert a track into the playlist buffer."
- :group 'emms
- :type 'function)
-(make-variable-buffer-local 'emms-playlist-insert-track-function)
-
-(defcustom emms-playlist-update-track-function 'emms-playlist-simple-update-track
- "*A function to update the track at point.
-This is called when the track information changed. This also
-shouldn't assume that the track has been inserted before."
- :group 'emms
- :type 'function)
-(make-variable-buffer-local 'emms-playlist-insert-track-function)
-
-(defcustom emms-playlist-delete-track-function 'emms-playlist-simple-delete-track
- "*A function to delete the track at point in the playlist buffer."
- :group 'emms
- :type 'function)
-(make-variable-buffer-local 'emms-playlist-delete-track-function)
-
-(defcustom emms-ok-track-function 'emms-default-ok-track-function
- "*Function returns true if we shouldn't skip this track."
- :group 'emms
- :type 'function)
-
-(defcustom emms-playlist-source-inserted-hook nil
- "*Hook run when a source got inserted into the playlist.
-The buffer is narrowed to the new tracks."
- :type 'hook
- :group 'emms)
-
-(defcustom emms-playlist-selection-changed-hook nil
- "*Hook run after another track is selected in the EMMS playlist."
- :group 'emms
- :type 'hook)
-
-(defcustom emms-playlist-cleared-hook nil
- "*Hook run after the current EMMS playlist is cleared.
-This happens both when the playlist is cleared and when a new
-buffer is created for it."
- :group 'emms
- :type 'hook)
-
-(defcustom emms-track-initialize-functions nil
- "*List of functions to call for each new EMMS track.
-This can be used to initialize tracks with various info."
- :group 'emms
- :type 'hook)
-
-(defcustom emms-track-info-filters nil
- "*List of functions to call when a track changes data, before updating
-the display.
-These functions are passed the track as an argument."
- :group 'emms
- :type 'hook)
-
-(defcustom emms-track-updated-functions nil
- "*List of functions to call when a track changes data, after updating
-the display.
-These functions are passed the track as an argument."
- :group 'emms
- :type 'hook)
-
-(defcustom emms-player-started-hook nil
- "*Hook run when an EMMS player starts playing."
- :group 'emms
- :type 'hook
- :options '(emms-show))
-
-(defcustom emms-player-stopped-hook nil
- "*Hook run when an EMMS player is stopped by the user.
-See `emms-player-finished-hook'."
- :group 'emms
- :type 'hook)
-
-(defcustom emms-player-finished-hook nil
- "*Hook run when an EMMS player finishes playing a track.
-Please pay attention to the differences between
-`emms-player-finished-hook' and `emms-player-stopped-hook'. The
-former is called only when the player actually finishes playing a
-track; the latter, only when the player is stopped
-interactively."
- :group 'emms
- :type 'hook)
-
-(defcustom emms-player-next-function 'emms-next-noerror
- "*A function run when EMMS thinks the next song should be played."
- :group 'emms
- :type 'function
- :options '(emms-next-noerror
- emms-random))
-
-(defcustom emms-player-paused-hook nil
- "*Hook run when a player is paused or resumed.
-Use `emms-player-paused-p' to find the current state."
- :group 'emms
- :type 'hook)
-
-(defcustom emms-seek-seconds 10
- "The number of seconds to seek forward or backward when seeking."
- :group 'emms
- :type 'number)
-
-(defcustom emms-player-seeked-functions nil
- "*Functions called when a player is seeking.
-The functions are called with a single argument, the amount of
-seconds the player did seek."
- :group 'emms
- :type 'hook)
-
-(defcustom emms-player-time-set-functions nil
- "*Functions called when a player is setting the elapsed time of a track.
-The functions are called with a single argument, the time elapsed
-since the beginning of the current track."
- :group 'emms
- :type 'hook)
-
-(defcustom emms-cache-get-function nil
- "A function to retrieve a track entry from the cache.
-This is called with two arguments, the type and the name."
- :group 'emms
- :type 'function)
-
-(defcustom emms-cache-set-function nil
- "A function to add/set a track entry from the cache.
-This is called with three arguments: the type of the track, the
-name of the track, and the track itself."
- :group 'emms
- :type 'function)
-
-(defcustom emms-cache-modified-function nil
- "A function to be called when a track is modified.
-The modified track is passed as the argument to this function."
- :group 'emms
- :type 'function)
-
-(defcustom emms-directory (expand-file-name "emms" user-emacs-directory)
- "*Directory variable from which all other emms file variables are derived."
- :group 'emms
- :type 'string)
-
-(defvar emms-player-playing-p nil
- "The currently playing EMMS player, or nil.")
-
-(defvar emms-player-paused-p nil
- "Whether the current player is paused or not.")
-
-(defvar emms-source-old-buffer nil
- "The active buffer before a source was invoked.
-This can be used if the source depends on the current buffer not
-being the playlist buffer.")
-
-(defvar emms-playlist-buffer nil
- "The current playlist buffer, if any.")
-
-
-;;; Macros
-
-;;; These need to be at the top of the file so that compilation works.
-
-(defmacro with-current-emms-playlist (&rest body)
- "Run BODY with the current buffer being the current playlist buffer.
-This also disables any read-onliness of the current buffer."
- `(progn
- (when (or (not emms-playlist-buffer)
- (not (buffer-live-p emms-playlist-buffer)))
- (emms-playlist-current-clear))
- (let ((emms-source-old-buffer (or emms-source-old-buffer
- (current-buffer))))
- (with-current-buffer emms-playlist-buffer
- (let ((inhibit-read-only t))
- ,@body)))))
-(put 'with-current-emms-playlist 'lisp-indent-function 0)
-(put 'with-current-emms-playlist 'edebug-form-spec '(body))
-
-(defmacro emms-with-inhibit-read-only-t (&rest body)
- "Simple wrapper around `inhibit-read-only'."
- `(let ((inhibit-read-only t))
- ,@body))
-(put 'emms-with-inhibit-read-only-t 'edebug-form-spec '(body))
-
-(defmacro emms-with-widened-buffer (&rest body)
- `(save-restriction
- (widen)
- ,@body))
-(put 'emms-with-widened-buffer 'edebug-form-spec '(body))
-
-(defmacro emms-walk-tracks (&rest body)
- "Execute BODY for each track in the current buffer, starting at point.
-Point will be placed at the beginning of the track before
-executing BODY.
-
-Point will not be restored afterward."
- (let ((donep (make-symbol "donep")))
- `(let ((,donep nil))
- ;; skip to first track if not on one
- (unless (emms-playlist-track-at (point))
- (condition-case nil
- (emms-playlist-next)
- (error
- (setq ,donep t))))
- ;; walk tracks
- (while (not ,donep)
- ,@body
- (condition-case nil
- (emms-playlist-next)
- (error
- (setq ,donep t)))))))
-(put 'emms-walk-tracks 'lisp-indent-function 0)
-(put 'emms-walk-tracks 'edebug-form-spec '(body))
-
-(defvar emms-player-base-format-list
- '("ogg" "mp3" "wav" "mpg" "mpeg" "wmv" "wma"
- "mov" "avi" "divx" "ogm" "ogv" "asf" "mkv"
- "rm" "rmvb" "mp4" "flac" "vob" "m4a" "ape"
- "flv" "webm" "aif")
- "A list of common formats which player definitions can use.")
-
-
-;;; User Interface
-
-(defun emms-start ()
- "Start playing the current track in the EMMS playlist."
- (interactive)
- (unless emms-player-playing-p
- (emms-player-start (emms-playlist-current-selected-track))))
-
-(defun emms-stop ()
- "Stop any current EMMS playback."
- (interactive)
- (when emms-player-playing-p
- (emms-player-stop)))
-
-(defun emms-next ()
- "Start playing the next track in the EMMS playlist.
-This might behave funny if called from `emms-player-next-function',
-so use `emms-next-noerror' in that case."
- (interactive)
- (when emms-player-playing-p
- (emms-stop))
- (emms-playlist-current-select-next)
- (emms-start))
-
-(defun emms-next-noerror ()
- "Start playing the next track in the EMMS playlist.
-Unlike `emms-next', this function doesn't signal an error when called
-at the end of the playlist.
-This function should only be called when no player is playing.
-This is a good function to put in `emms-player-next-function'."
- (interactive)
- (when emms-player-playing-p
- (error "A track is already being played"))
- (cond (emms-repeat-track
- (emms-start))
- (emms-single-track ; buffer local
- (emms-stop))
- ;; attempt to play the next track but ignore errors
- ((condition-case nil
- (progn
- (emms-playlist-current-select-next)
- t)
- (error nil))
- (if (funcall emms-ok-track-function
- (emms-playlist-current-selected-track))
- (emms-start)
- (emms-next-noerror)))
- (t
- (message "No next track in playlist"))))
-
-(defun emms-previous ()
- "Start playing the previous track in the EMMS playlist."
- (interactive)
- (when emms-player-playing-p
- (emms-stop))
- (emms-playlist-current-select-previous)
- (emms-start))
-
-(defun emms-random ()
- "Jump to a random track."
- (interactive)
- (when emms-player-playing-p
- (emms-stop))
- (emms-playlist-current-select-random)
- (emms-start))
-
-(defun emms-pause ()
- "Pause the current player.
-If player hasn't started, then start it now."
- (interactive)
- (if emms-player-playing-p
- (emms-player-pause)
- (emms-start)))
-
-(defun emms-seek (seconds)
- "Seek the current player SECONDS seconds.
-This can be a floating point number for sub-second fractions.
-It can also be negative to seek backwards."
- (interactive "nSeconds to seek: ")
- (emms-ensure-player-playing-p)
- (emms-player-seek seconds))
-
-(defun emms-seek-to (seconds)
- "Seek the current player to SECONDS seconds.
-This can be a floating point number for sub-second fractions.
-It can also be negative to seek backwards."
- (interactive "nSeconds to seek to: ")
- (emms-ensure-player-playing-p)
- (emms-player-seek-to seconds))
-
-(defun emms-seek-forward ()
- "Seek ten seconds forward."
- (interactive)
- (when emms-player-playing-p
- (emms-player-seek emms-seek-seconds)))
-
-(defun emms-seek-backward ()
- "Seek ten seconds backward."
- (interactive)
- (when emms-player-playing-p
- (emms-player-seek (- emms-seek-seconds))))
-
-(defun emms-show (&optional insertp)
- "Describe the current EMMS track in the minibuffer.
-If INSERTP is non-nil, insert the description into the current buffer instead.
-This function uses `emms-show-format' to format the current track."
- (interactive "P")
- (let ((string (if emms-player-playing-p
- (format emms-show-format
- (emms-track-description
- (emms-playlist-current-selected-track)))
- "Nothing playing right now")))
- (if insertp
- (insert string)
- (message "%s" string))))
-
-(defun emms-shuffle ()
- "Shuffle the current playlist.
-This uses `emms-playlist-shuffle-function'."
- (interactive)
- (with-current-emms-playlist
- (save-excursion
- (funcall emms-playlist-shuffle-function))))
-
-(defun emms-sort ()
- "Sort the current playlist.
-This uses `emms-playlist-sort-function'."
- (interactive)
- (with-current-emms-playlist
- (save-excursion
- (funcall emms-playlist-sort-function))))
-
-(defun emms-uniq ()
- "Remove duplicates from the current playlist.
-This uses `emms-playlist-uniq-function'."
- (interactive)
- (with-current-emms-playlist
- (save-excursion
- (funcall emms-playlist-uniq-function))))
-
-(defun emms-toggle-single-track ()
- "Toggle if Emms plays a single track and stops."
- (interactive)
- (with-current-emms-playlist
- (cond (emms-single-track
- (setq emms-single-track nil)
- (message "single track mode disabled for %s"
- (buffer-name)))
- (t (setq emms-single-track t)
- (message "single track mode enabled for %s"
- (buffer-name))))))
-
-(defun emms-toggle-random-playlist ()
- "Toggle whether emms plays the tracks randomly or sequentially.
-See `emms-random-playlist'."
- (interactive)
- (setq emms-random-playlist (not emms-random-playlist))
- (if emms-random-playlist
- (progn (setq emms-player-next-function 'emms-random)
- (message "Will play the tracks randomly."))
- (setq emms-player-next-function 'emms-next-noerror)
- (message "Will play the tracks sequentially.")))
-
-(defun emms-toggle-repeat-playlist ()
- "Toggle whether emms repeats the playlist after it is done.
-See `emms-repeat-playlist'."
- (interactive)
- (setq emms-repeat-playlist (not emms-repeat-playlist))
- (if emms-repeat-playlist
- (message "Will repeat the playlist after it is done.")
- (message "Will stop after the playlist is over.")))
-
-(defun emms-toggle-repeat-track ()
- "Toggle whether emms repeats the current track.
-See `emms-repeat-track'."
- (interactive)
- (setq emms-repeat-track (not emms-repeat-track))
- (if emms-repeat-track
- (message "Will repeat the current track.")
- (message "Will advance to the next track after this one.")))
-
-(defun emms-sort-track-name-less-p (a b)
- "Return non-nil if the track name of A sorts before B."
- (string< (emms-track-name a)
- (emms-track-name b)))
-
-(defun emms-ensure-player-playing-p ()
- "Raise an error if no player is playing right now."
- (when (not emms-player-playing-p)
- (error "No EMMS player playing right now")))
-
-(defun emms-completing-read (&rest args)
- "Read a string in the minibuffer, with completion.
-Set `emms-completing-read' to determine which function to use.
-
-See `completing-read' for a description of ARGS."
- (apply emms-completing-read-function args))
-
-(defun emms-display-modes ()
- "Display the current EMMS play modes."
- (interactive)
- (with-current-emms-playlist
- (message
- "repeat playlist: %s, repeat track: %s, random: %s, single %s"
- (if emms-repeat-playlist "yes" "no")
- (if emms-repeat-track "yes" "no")
- (if emms-random-playlist "yes" "no")
- (if emms-single-track "yes" "no"))))
-
-
-;;; Compatibility functions
-
-(require 'emms-compat)
-
-
-;;; Utility functions
-
-(defun emms-insert-file-contents (filename &optional visit)
- "Insert the contents of file FILENAME after point.
-Do character code conversion and end-of-line conversion, but none
-of the other unnecessary things like format decoding or
-`find-file-hook'.
-
-If VISIT is non-nil, the buffer's visited filename
-and last save file modtime are set, and it is marked unmodified.
-If visiting and the file does not exist, visiting is completed
-before the error is signaled."
- (let ((format-alist nil)
- (after-insert-file-functions nil)
- (inhibit-file-name-handlers
- (append '(jka-compr-handler image-file-handler epa-file-handler)
- inhibit-file-name-handlers))
- (inhibit-file-name-operation 'insert-file-contents))
- (insert-file-contents filename visit)))
-
-
-;;; Dictionaries
-
-;; This is a simple helper data structure, used by both players
-;; and tracks.
-
-(defsubst emms-dictionary (name)
- "Create a new dictionary of type NAME."
- (list name))
-
-(defsubst emms-dictionary-type (dict)
- "Return the type of the dictionary DICT."
- (car dict))
-
-(defun emms-dictionary-get (dict name &optional default)
- "Return the value of NAME in DICT."
- (let ((item (assq name (cdr dict))))
- (if item
- (cdr item)
- default)))
-
-(defun emms-dictionary-set (dict name value)
- "Set the value of NAME in DICT to VALUE."
- (let ((item (assq name (cdr dict))))
- (if item
- (setcdr item value)
- (setcdr dict (append (cdr dict)
- (list (cons name value))))))
- dict)
-
-
-;;; Tracks
-
-;; This is a simple datatype to store track information.
-;; Each track consists of a type (a symbol) and a name (a string).
-;; In addition, each track has an associated dictionary of information.
-
-(defun emms-track (type name)
- "Create an EMMS track with type TYPE and name NAME."
- (let ((track (when emms-cache-get-function
- (funcall emms-cache-get-function type name))))
- (when (not track)
- (setq track (emms-dictionary '*track*))
- ;; Prevent the cache from being called for these two sets
- (let ((emms-cache-modified-function nil))
- (emms-track-set track 'type type)
- (emms-track-set track 'name name))
- (when emms-cache-set-function
- (funcall emms-cache-set-function type name track)))
- ;; run any hooks regardless of a cache hit, as the entry may be
- ;; old
- (run-hook-with-args 'emms-track-initialize-functions track)
- track))
-
-(defun emms-track-p (obj)
- "True if OBJ is an emms track."
- (and (listp obj)
- (eq (car obj) '*track*)))
-
-(defun emms-track-type (track)
- "Return the type of TRACK."
- (emms-track-get track 'type))
-
-(defun emms-track-name (track)
- "Return the name of TRACK."
- (emms-track-get track 'name))
-
-(defun emms-track-get (track name &optional default)
- "Return the value of NAME for TRACK.
-If there is no value, return DEFAULT (or nil, if not given)."
- (emms-dictionary-get track name default))
-
-(defun emms-track-set (track name value)
- "Set the value of NAME for TRACK to VALUE."
- (emms-dictionary-set track name value)
- (when emms-cache-modified-function
- (funcall emms-cache-modified-function track)))
-
-(defun emms-track-description (track)
- "Return a description of TRACK.
-This function uses the global value for
-`emms-track-description-function', rather than anything the
-current mode might have set.
-
-Use `emms-track-force-description' instead if you need to insert
-a description into a playlist buffer."
- (funcall (default-value 'emms-track-description-function) track))
-
-(defun emms-track-updated (track)
- "Information in TRACK got updated."
- (run-hook-with-args 'emms-track-info-filters track)
- (emms-playlist-track-updated track)
- (run-hook-with-args 'emms-track-updated-functions track))
-
-(defun emms-track-simple-description (track)
- "Simple function to give a user-readable description of a track.
-If it's a file track, just return the file name. Otherwise,
-return the type and the name with a colon in between.
-Hex-encoded characters in URLs are replaced by the decoded
-character."
- (let ((type (emms-track-type track)))
- (cond ((eq 'file type)
- (emms-track-name track))
- ((eq 'url type)
- (emms-format-url-track-name (emms-track-name track)))
- (t (concat (symbol-name type)
- ": " (emms-track-name track))))))
-
-(defun emms-format-url-track-name (name)
- "Format URL track name for better readability."
- (url-unhex-string name))
-
-(defun emms-track-force-description (track)
- "Always return text that describes TRACK.
-This is used when inserting a description into a buffer.
-
-The reason for this is that if no text was returned (i.e. the
-user defined a track function that returned nil or the empty
-string), a confusing error message would result."
- (let ((desc (funcall emms-track-description-function track)))
- (if (and (stringp desc) (not (string= desc "")))
- desc
- (emms-track-simple-description track))))
-
-(defun emms-track-get-year (track)
- "Get year of TRACK for display.
-There is the separation between the 'release date' and the
-'original date'. This difference matters e.g. for
-re-releases (anniversaries and such) where the release date is
-more recent than the original release date. In such cases the
-user probably wants the original release date so this is what we
-show."
- (or
- (emms-format-date-to-year (emms-track-get track 'info-date))
- (emms-format-date-to-year (emms-track-get track 'info-originaldate))
- (emms-track-get track 'info-year)
- (emms-track-get track 'info-originalyear)))
-
-(defun emms-format-date-to-year (date)
- "Try to extract year part from DATE.
-Return nil if the year cannot be extracted."
- (when date
- (let ((year (nth 5 (parse-time-string date))))
- (if year (number-to-string year)
- (when (string-match "^[ \t]*\\([0-9]\\{4\\}\\)" date)
- (match-string 1 date))))))
-
-
-;;; The Playlist
-
-;; Playlists are stored in buffers. The current playlist buffer is
-;; remembered in the `emms-playlist' variable. The buffer consists of
-;; any kind of data. Strings of text with a `emms-track' property are
-;; the tracks in the buffer.
-
-(defvar emms-playlist-buffers nil
- "The list of EMMS playlist buffers.
-You should use the `emms-playlist-buffer-list' function to
-retrieve a current list of EMMS buffers. Never use this variable
-for that purpose.")
-
-(defvar emms-playlist-selected-marker nil
- "The marker for the currently selected track.")
-(make-variable-buffer-local 'emms-playlist-selected-marker)
-
-(defvar emms-playlist-buffer-p nil
- "Non-nil if the current buffer is an EMMS playlist.")
-(make-variable-buffer-local 'emms-playlist-buffer-p)
-
-(defun emms-playlist-ensure-playlist-buffer ()
- "Throw an error if we're not in a playlist-buffer."
- (when (not emms-playlist-buffer-p)
- (error "Not an EMMS playlist buffer")))
-
-(defun emms-playlist-set-playlist-buffer (&optional buffer)
- "Set the current playlist buffer."
- (interactive
- (list (let* ((buf-list (mapcar #'(lambda (buf)
- (list (buffer-name buf)))
- (emms-playlist-buffer-list)))
- (sorted-buf-list (sort buf-list
- #'(lambda (lbuf rbuf)
- (< (length (car lbuf))
- (length (car rbuf))))))
- (default (or (and emms-playlist-buffer-p
- ;; default to current buffer
- (buffer-name))
- ;; pick shortest buffer name, since it is
- ;; likely to be a shared prefix
- (car sorted-buf-list))))
- (emms-completing-read "Playlist buffer to make current: "
- sorted-buf-list nil t default))))
- (let ((buf (if buffer
- (get-buffer buffer)
- (current-buffer))))
- (with-current-buffer buf
- (emms-playlist-ensure-playlist-buffer))
- (setq emms-playlist-buffer buf)
- (when (called-interactively-p 'interactive)
- (message "Set current EMMS playlist buffer"))
- buf))
-
-(defun emms-playlist-new (&optional name)
- "Create a new playlist buffer.
-The buffer is named NAME, but made unique. NAME defaults to
-`emms-playlist-buffer-name'. If called interactively, the new
-buffer is also selected."
- (interactive)
- (let ((buf (generate-new-buffer (or name
- emms-playlist-buffer-name))))
- (with-current-buffer buf
- (when (not (eq major-mode emms-playlist-default-major-mode))
- (funcall emms-playlist-default-major-mode))
- (setq emms-playlist-buffer-p t))
- (add-to-list 'emms-playlist-buffers buf)
- (when (called-interactively-p 'interactive)
- (switch-to-buffer buf))
- buf))
-
-(defun emms-playlist-buffer-list ()
- "Return a list of EMMS playlist buffers.
-The first element is guaranteed to be the current EMMS playlist
-buffer, if it exists, otherwise the slot will be used for the
-other EMMS buffers. The list will be in newest-first order."
- ;; prune dead buffers
- (setq emms-playlist-buffers (emms-delete-if (lambda (buf)
- (not (buffer-live-p buf)))
- emms-playlist-buffers))
- ;; add new buffers
- (mapc (lambda (buf)
- (when (buffer-live-p buf)
- (with-current-buffer buf
- (when (and emms-playlist-buffer-p
- (not (memq buf emms-playlist-buffers)))
- (setq emms-playlist-buffers
- (cons buf emms-playlist-buffers))))))
- (buffer-list))
- ;; force current playlist buffer to head position
- (when (and (buffer-live-p emms-playlist-buffer)
- (not (eq (car emms-playlist-buffers) emms-playlist-buffer)))
- (setq emms-playlist-buffers (cons emms-playlist-buffer
- (delete emms-playlist-buffer
- emms-playlist-buffers))))
- emms-playlist-buffers)
-
-(defun emms-playlist-current-kill ()
- "Kill the current EMMS playlist buffer and switch to the next one."
- (interactive)
- (when (buffer-live-p emms-playlist-buffer)
- (let ((new (cadr (emms-playlist-buffer-list))))
- (if new
- (let ((old emms-playlist-buffer))
- (setq emms-playlist-buffer new
- emms-playlist-buffers (cdr emms-playlist-buffers))
- (kill-buffer old)
- (switch-to-buffer emms-playlist-buffer))
- (with-current-buffer emms-playlist-buffer
- (bury-buffer))))))
-
-(defun emms-playlist-current-clear ()
- "Clear the current playlist.
-If no current playlist exists, a new one is generated."
- (interactive)
- (if (or (not emms-playlist-buffer)
- (not (buffer-live-p emms-playlist-buffer)))
- (setq emms-playlist-buffer (emms-playlist-new))
- (with-current-buffer emms-playlist-buffer
- (emms-playlist-clear))))
-
-(defun emms-playlist-clear ()
- "Clear the current buffer."
- (interactive)
- (emms-playlist-ensure-playlist-buffer)
- (let ((inhibit-read-only t))
- (widen)
- (delete-region (point-min)
- (point-max)))
- (run-hooks 'emms-playlist-cleared-hook))
-
-;;; Point movement within the playlist buffer.
-(defun emms-playlist-track-at (&optional pos)
- "Return the track at POS (point if not given), or nil if none."
- (emms-playlist-ensure-playlist-buffer)
- (emms-with-widened-buffer
- (get-text-property (or pos (point))
- 'emms-track)))
-
-(defun emms-playlist-next ()
- "Move to the next track in the current buffer."
- (emms-playlist-ensure-playlist-buffer)
- (let ((next (next-single-property-change (point)
- 'emms-track)))
- (when (not next)
- (error "No next track"))
- (when (not (emms-playlist-track-at next))
- (setq next (next-single-property-change next 'emms-track)))
- (when (or (not next)
- (= next (point-max)))
- (error "No next track"))
- (goto-char next)))
-
-(defun emms-playlist-previous ()
- "Move to the previous track in the current buffer."
- (emms-playlist-ensure-playlist-buffer)
- (let ((prev (previous-single-property-change (point)
- 'emms-track)))
- (when (not prev)
- (error "No previous track"))
- (when (not (get-text-property prev 'emms-track))
- (setq prev (or (previous-single-property-change prev 'emms-track)
- (point-min))))
- (when (or (not prev)
- (not (get-text-property prev 'emms-track)))
- (error "No previous track"))
- (goto-char prev)))
-
-(defun emms-playlist-first ()
- "Move to the first track in the current buffer."
- (emms-playlist-ensure-playlist-buffer)
- (let ((first (condition-case nil
- (save-excursion
- (goto-char (point-min))
- (when (not (emms-playlist-track-at (point)))
- (emms-playlist-next))
- (point))
- (error
- nil))))
- (if first
- (goto-char first)
- (error "No first track"))))
-
-(defun emms-playlist-last ()
- "Move to the last track in the current buffer."
- (emms-playlist-ensure-playlist-buffer)
- (let ((last (condition-case nil
- (save-excursion
- (goto-char (point-max))
- (emms-playlist-previous)
- (point))
- (error
- nil))))
- (if last
- (goto-char last)
- (error "No last track"))))
-
-(defun emms-playlist-delete-track ()
- "Delete the track at point."
- (emms-playlist-ensure-playlist-buffer)
- (funcall emms-playlist-delete-track-function))
-
-;;; Track selection
-(defun emms-playlist-selected-track ()
- "Return the currently selected track."
- (emms-playlist-ensure-playlist-buffer)
- (when emms-playlist-selected-marker
- (emms-playlist-track-at emms-playlist-selected-marker)))
-
-(defun emms-playlist-current-selected-track ()
- "Return the currently selected track in the current playlist."
- (with-current-emms-playlist
- (emms-playlist-selected-track)))
-
-(defun emms-playlist-selected-track-at-p (&optional point)
- "Return non-nil if POINT (defaulting to point) is on the selected track."
- (when emms-playlist-selected-marker
- (or (= emms-playlist-selected-marker
- (or point (point)))
- (let ((p (previous-single-property-change (or point (point))
- 'emms-track)))
- (when p
- (= emms-playlist-selected-marker
- p))))))
-
-(defun emms-playlist-select (pos)
- "Select the track at POS."
- (emms-playlist-ensure-playlist-buffer)
- (when (not (emms-playlist-track-at pos))
- (error "No track at position %s" pos))
- (when (not emms-playlist-selected-marker)
- (setq emms-playlist-selected-marker (make-marker)))
- (set-marker-insertion-type emms-playlist-selected-marker t)
- (set-marker emms-playlist-selected-marker pos)
- (run-hooks 'emms-playlist-selection-changed-hook))
-
-(defun emms-playlist-select-next ()
- "Select the next track in the current buffer."
- (emms-playlist-ensure-playlist-buffer)
- (save-excursion
- (goto-char (if (and emms-playlist-selected-marker
- (marker-position emms-playlist-selected-marker))
- emms-playlist-selected-marker
- (point-min)))
- (condition-case nil
- (progn
- (if emms-repeat-playlist
- (condition-case nil
- (emms-playlist-next)
- (error
- (emms-playlist-first)))
- (emms-playlist-next))
- (emms-playlist-select (point)))
- (error
- (error "No next track in playlist")))))
-
-(defun emms-playlist-current-select-next ()
- "Select the next track in the current playlist."
- (with-current-emms-playlist
- (emms-playlist-select-next)))
-
-(defun emms-playlist-select-previous ()
- "Select the previous track in the current buffer."
- (emms-playlist-ensure-playlist-buffer)
- (save-excursion
- (goto-char (if (and emms-playlist-selected-marker
- (marker-position emms-playlist-selected-marker))
- emms-playlist-selected-marker
- (point-max)))
- (condition-case nil
- (progn
- (if emms-repeat-playlist
- (condition-case nil
- (emms-playlist-previous)
- (error
- (emms-playlist-last)))
- (emms-playlist-previous))
- (emms-playlist-select (point)))
- (error
- (error "No previous track in playlist")))))
-
-(defun emms-playlist-current-select-previous ()
- "Select the previous track in the current playlist."
- (with-current-emms-playlist
- (emms-playlist-select-previous)))
-
-(defun emms-playlist-select-random ()
- "Select a random track in the current buffer."
- (emms-playlist-ensure-playlist-buffer)
- ;; FIXME: This is rather inefficient.
- (save-excursion
- (let ((track-indices nil))
- (goto-char (point-min))
- (emms-walk-tracks
- (setq track-indices (cons (point)
- track-indices)))
- (setq track-indices (vconcat track-indices))
- (emms-playlist-select (aref track-indices
- (random (length track-indices)))))))
-
-(defun emms-playlist-current-select-random ()
- "Select a random track in the current playlist."
- (with-current-emms-playlist
- (emms-playlist-select-random)))
-
-(defun emms-playlist-select-first ()
- "Select the first track in the current buffer."
- (emms-playlist-ensure-playlist-buffer)
- (save-excursion
- (emms-playlist-first)
- (emms-playlist-select (point))))
-
-(defun emms-playlist-current-select-first ()
- "Select the first track in the current playlist."
- (with-current-emms-playlist
- (emms-playlist-select-first)))
-
-(defun emms-playlist-select-last ()
- "Select the last track in the current buffer."
- (emms-playlist-ensure-playlist-buffer)
- (save-excursion
- (emms-playlist-last)
- (emms-playlist-select (point))))
-
-(defun emms-playlist-current-select-last ()
- "Select the last track in the current playlist."
- (with-current-emms-playlist
- (emms-playlist-select-last)))
-
-;;; Playlist manipulation
-(defun emms-playlist-insert-track (track)
- "Insert TRACK at the current position into the playlist.
-This uses `emms-playlist-insert-track-function'."
- (emms-playlist-ensure-playlist-buffer)
- (funcall emms-playlist-insert-track-function track))
-
-(defun emms-playlist-update-track ()
- "Update TRACK at point.
-This uses `emms-playlist-update-track-function'."
- (emms-playlist-ensure-playlist-buffer)
- (funcall emms-playlist-update-track-function))
-
-(defun emms-playlist-insert-source (source &rest args)
- "Insert tracks from SOURCE, supplying ARGS as arguments."
- (emms-playlist-ensure-playlist-buffer)
- (save-restriction
- (narrow-to-region (point)
- (point))
- (apply source args)
- (run-hooks 'emms-playlist-source-inserted-hook)))
-
-(defun emms-playlist-current-insert-source (source &rest args)
- "Insert tracks from SOURCE in the current playlist.
-This is supplying ARGS as arguments to the source."
- (with-current-emms-playlist
- (apply 'emms-playlist-insert-source source args)))
-
-(defun emms-playlist-tracks-in-region (beg end)
- "Return all tracks between BEG and END."
- (emms-playlist-ensure-playlist-buffer)
- (let ((tracks nil))
- (save-restriction
- (narrow-to-region beg end)
- (goto-char (point-min))
- (emms-walk-tracks
- (setq tracks (cons (emms-playlist-track-at (point))
- tracks))))
- tracks))
-
-(defun emms-playlist-track-updated (track)
- "Update TRACK in all playlist buffers."
- (mapc (lambda (buf)
- (with-current-buffer buf
- (when emms-playlist-buffer-p
- (save-excursion
- (let ((pos (text-property-any (point-min) (point-max)
- 'emms-track track)))
- (while pos
- (goto-char pos)
- (emms-playlist-update-track)
- (setq pos (text-property-any
- (next-single-property-change (point)
- 'emms-track)
- (point-max)
- 'emms-track
- track))))))))
- (buffer-list))
- t)
-
-;;; Simple playlist buffer
-(defun emms-playlist-simple-insert-track (track)
- "Insert the description of TRACK at point."
- (emms-playlist-ensure-playlist-buffer)
- (let ((inhibit-read-only t))
- (insert (emms-propertize (emms-track-force-description track)
- 'emms-track track)
- "\n")))
-
-(defun emms-playlist-simple-update-track ()
- "Update the track at point.
-Since we don't do anything special with the track anyway, just
-ignore this."
- nil)
-
-(defun emms-playlist-simple-delete-track ()
- "Delete the track at point."
- (emms-playlist-ensure-playlist-buffer)
- (when (not (emms-playlist-track-at (point)))
- (error "No track at point"))
- (let ((inhibit-read-only t)
- (region (emms-property-region (point) 'emms-track)))
- (delete-region (car region)
- (cdr region))))
-
-(defun emms-playlist-simple-shuffle ()
- "Shuffle the whole playlist buffer."
- (emms-playlist-ensure-playlist-buffer)
- (let ((inhibit-read-only t)
- (current nil))
- (widen)
- (when emms-player-playing-p
- (setq current (emms-playlist-selected-track))
- (goto-char emms-playlist-selected-marker)
- (emms-playlist-delete-track))
- (let* ((tracks (vconcat (emms-playlist-tracks-in-region (point-min)
- (point-max))))
- (len (length tracks))
- (i 0))
- (delete-region (point-min)
- (point-max))
- (run-hooks 'emms-playlist-cleared-hook)
- (emms-shuffle-vector tracks)
- (when current
- (emms-playlist-insert-track current))
- (while (< i len)
- (emms-playlist-insert-track (aref tracks i))
- (setq i (1+ i))))
- (emms-playlist-select-first)
- (goto-char (point-max))))
-
-(defun emms-playlist-simple-sort ()
- "Sort the whole playlist buffer."
- (emms-playlist-ensure-playlist-buffer)
- (widen)
- (let ((inhibit-read-only t)
- (current (emms-playlist-selected-track))
- (tracks (emms-playlist-tracks-in-region (point-min)
- (point-max))))
- (delete-region (point-min)
- (point-max))
- (run-hooks 'emms-playlist-cleared-hook)
- (mapc 'emms-playlist-insert-track
- (sort tracks emms-sort-lessp-function))
- (let ((pos (text-property-any (point-min)
- (point-max)
- 'emms-track current)))
- (if pos
- (emms-playlist-select pos)
- (emms-playlist-first)))))
-
-(defun emms-uniq-list (list stringify)
- "Compare stringfied element of list, and remove duplicate elements."
- ;; This uses a fast append list, keeping a pointer to the last cons
- ;; cell of the list (TAIL). It might be worthwhile to provide an
- ;; abstraction for this eventually.
- (let* ((hash (make-hash-table :test 'equal))
- (result (cons nil nil))
- (tail result))
- (dolist (element list)
- (let ((str (funcall stringify element)))
- (when (not (gethash str hash))
- (setcdr tail (cons element nil))
- (setq tail (cdr tail)))
- (puthash str t hash)))
- (cdr result)))
-
-(defun emms-playlist-simple-uniq ()
- "Remove duplicate tracks."
- ;; TODO: This seems unnecessarily destructive.
- (emms-playlist-ensure-playlist-buffer)
- (widen)
- (let ((inhibit-read-only t)
- (current (emms-playlist-selected-track))
- (tracks (emms-playlist-tracks-in-region (point-min)
- (point-max))))
- (delete-region (point-min) (point-max))
- (run-hooks 'emms-playlist-cleared-hook)
- (mapc 'emms-playlist-insert-track
- (nreverse
- (emms-uniq-list tracks 'emms-track-name)))
- (let ((pos (text-property-any (point-min)
- (point-max)
- 'emms-track current)))
- (if pos
- (emms-playlist-select pos)
- (emms-playlist-first)))))
-
-(defun emms-default-ok-track-function (track)
- "A function which OKs all tracks for playing by default."
- t)
-
-;;; Helper functions
-(defun emms-property-region (pos prop)
- "Return a pair of the beginning and end of the property PROP at POS.
-If POS does not contain PROP, try to find PROP just before POS."
- (let (begin end)
- (if (and (> pos (point-min))
- (get-text-property (1- pos) prop))
- (setq begin (previous-single-property-change (1- pos) prop))
- (if (get-text-property pos prop)
- (setq begin pos)
- (error "Cannot find the %s property at the given position" prop)))
- (if (get-text-property pos prop)
- (setq end (next-single-property-change pos prop))
- (if (and (> pos (point-min))
- (get-text-property (1- pos) prop))
- (setq end pos)
- (error "Cannot find the %s property at the given position" prop)))
- (cons (or begin (point-min))
- (or end (point-max)))))
-
-(defun emms-shuffle-vector (vector)
- "Shuffle VECTOR."
- (let ((i (- (length vector) 1)))
- (while (>= i 0)
- (let* ((r (random (1+ i)))
- (old (aref vector r)))
- (aset vector r (aref vector i))
- (aset vector i old))
- (setq i (- i 1))))
- vector)
-
-
-;;; Sources
-
-;; A source is just a function which is called in a playlist buffer.
-;; It should use `emms-playlist-insert-track' to insert the tracks it
-;; knows about.
-;;
-;; The define-emms-source macro also defines functions
-;; emms-play-SOURCE and emms-add-SOURCE. The former will replace the
-;; current playlist, while the latter will add to the end.
-
-(defmacro define-emms-source (name arglist &rest body)
- "Define a new EMMS source called NAME.
-This macro defines three functions: `emms-source-NAME',
-`emms-play-NAME' and `emms-add-NAME'. BODY should use
-`emms-playlist-insert-track' to insert all tracks to be played,
-which is exactly what `emms-source-NAME' will do. The other two
-functions will be simple wrappers around `emms-source-NAME'; any
-`interactive' form that you specify in BODY will end up in these.
-See emms-source-file.el for some examples."
- (let ((source-name (intern (format "emms-source-%s" name)))
- (source-play (intern (format "emms-play-%s" name)))
- (source-add (intern (format "emms-add-%s" name)))
- (source-insert (intern (format "emms-insert-%s" name)))
- (docstring "A source of tracks for EMMS.")
- (interactive nil)
- (call-args (delete '&rest
- (delete '&optional
- arglist))))
- (when (stringp (car body))
- (setq docstring (car body)
- body (cdr body)))
- (when (eq 'interactive (caar body))
- (setq interactive (car body)
- body (cdr body)))
- `(progn
- (defun ,source-name ,arglist
- ,docstring
- ,@body)
- (defun ,source-play ,arglist
- ,docstring
- ,interactive
- (if current-prefix-arg
- (let ((current-prefix-arg nil))
- (emms-source-add ',source-name ,@call-args))
- (emms-source-play ',source-name ,@call-args)))
- (defun ,source-add ,arglist
- ,docstring
- ,interactive
- (if current-prefix-arg
- (let ((current-prefix-arg nil))
- (emms-source-play ',source-name ,@call-args))
- (emms-source-add ',source-name ,@call-args)))
- (defun ,source-insert ,arglist
- ,docstring
- ,interactive
- (emms-source-insert ',source-name ,@call-args)))))
-
-(defun emms-source-play (source &rest args)
- "Play the tracks of SOURCE, after first clearing the EMMS playlist."
- (emms-stop)
- (emms-playlist-current-clear)
- (apply 'emms-playlist-current-insert-source source args)
- (emms-playlist-current-select-first)
- (emms-start))
-
-(defun emms-source-add (source &rest args)
- "Add the tracks of SOURCE at the current position in the playlist."
- (with-current-emms-playlist
- (save-excursion
- (goto-char (point-max))
- (apply 'emms-playlist-current-insert-source source args))
- (when (or (not emms-playlist-selected-marker)
- (not (marker-position emms-playlist-selected-marker)))
- (emms-playlist-select-first))))
-
-(defun emms-source-insert (source &rest args)
- "Insert the tracks from SOURCE in the current buffer."
- (if (not emms-playlist-buffer-p)
- (error "Not in an EMMS playlist buffer")
- (apply 'emms-playlist-insert-source source args)))
-
-;;; User-defined playlists
-;;; FIXME: Shuffle is bogus here! (because of narrowing)
-(defmacro define-emms-combined-source (name shufflep sources)
- "Define a `emms-play-X' and `emms-add-X' function for SOURCES."
- `(define-emms-source ,name ()
- "An EMMS source for a tracklist."
- (interactive)
- (mapc (lambda (source)
- (apply (car source)
- (cdr source)))
- ,sources)
- ,(when shufflep
- '(save-restriction
- (widen)
- (emms-shuffle)))))
-
-
-;;; Players
-
-;; A player is a data structure created by `emms-player'.
-;; See the docstring of that function for more information.
-
-(defvar emms-player-stopped-p nil
- "Non-nil if the last EMMS player was stopped by the user.")
-
-(defun emms-player (start stop playablep)
- "Create a new EMMS player.
-The start function will be START, and the stop function STOP.
-PLAYABLEP should return non-nil for tracks that this player can
-play.
-
-When trying to play a track, EMMS walks through
-`emms-player-list'. For each player, it calls the PLAYABLEP
-function. The player corresponding to the first PLAYABLEP
-function that returns non-nil is used to play the track. To
-actually play the track, EMMS calls the START function, passing
-the chosen track as a parameter.
-
-If the user tells EMMS to stop playing, the STOP function is
-called. Once the player has finished playing, it should call
-`emms-player-stopped' to let EMMS know."
- (let ((p (emms-dictionary '*player*)))
- (emms-player-set p 'start start)
- (emms-player-set p 'stop stop)
- (emms-player-set p 'playablep playablep)
- p))
-
-(defun emms-player-get (player name &optional inexistent)
- "Return the value of entry NAME in PLAYER."
- (let ((p (if (symbolp player)
- (symbol-value player)
- player)))
- (emms-dictionary-get p name inexistent)))
-
-(defun emms-player-set (player name value)
- "Set the value of entry NAME in PLAYER to VALUE."
- (let ((p (if (symbolp player)
- (symbol-value player)
- player)))
- (emms-dictionary-set p name value)))
-
-(defun emms-player-for (track)
- "Return an EMMS player capable of playing TRACK.
-This will be the first player whose PLAYABLEP function returns
-non-nil, or nil if no such player exists."
- (let ((lis emms-player-list))
- (while (and lis
- (not (funcall (emms-player-get (car lis) 'playablep)
- track)))
- (setq lis (cdr lis)))
- (if lis
- (car lis)
- nil)))
-
-(defun emms-player-start (track)
- "Start playing TRACK."
- (if emms-player-playing-p
- (error "A player is already playing")
- (let ((player (emms-player-for track)))
- (if (not player)
- (error "Don't know how to play track: %S" track)
- ;; Change default-directory so we don't accidentally block any
- ;; directories the current buffer was visiting.
- (let ((default-directory "/"))
- (funcall (emms-player-get player 'start)
- track))))))
-
-(defun emms-player-started (player)
- "Declare that the given EMMS PLAYER has started.
-This should only be done by the current player itself."
- (setq emms-player-playing-p player
- emms-player-paused-p nil)
- (run-hooks 'emms-player-started-hook))
-
-(defun emms-player-stop ()
- "Stop the current EMMS player."
- (when emms-player-playing-p
- (let ((emms-player-stopped-p t))
- (funcall (emms-player-get emms-player-playing-p 'stop)))
- (setq emms-player-playing-p nil)))
-
-(defun emms-player-stopped ()
- "Declare that the current EMMS player is finished.
-This should only be done by the current player itself."
- (setq emms-player-playing-p nil)
- (if emms-player-stopped-p
- (run-hooks 'emms-player-stopped-hook)
- (sleep-for emms-player-delay)
- (run-hooks 'emms-player-finished-hook)
- (funcall emms-player-next-function)))
-
-(defun emms-player-pause ()
- "Pause the current EMMS player."
- (cond
- ((not emms-player-playing-p)
- (error "Can't pause player, nothing is playing"))
- (emms-player-paused-p
- (let ((resume (emms-player-get emms-player-playing-p 'resume))
- (pause (emms-player-get emms-player-playing-p 'pause)))
- (cond
- (resume
- (funcall resume))
- (pause
- (funcall pause))
- (t
- (error "Player does not know how to pause"))))
- (setq emms-player-paused-p nil)
- (run-hooks 'emms-player-paused-hook))
- (t
- (let ((pause (emms-player-get emms-player-playing-p 'pause)))
- (if pause
- (funcall pause)
- (error "Player does not know how to pause")))
- (setq emms-player-paused-p t)
- (run-hooks 'emms-player-paused-hook))))
-
-(defun emms-player-seek (seconds)
- "Seek the current player by SECONDS seconds.
-This can be a floating point number for fractions of a second, or
-negative to seek backwards."
- (if (not emms-player-playing-p)
- (error "Can't seek player, nothing playing right now")
- (let ((seek (emms-player-get emms-player-playing-p 'seek)))
- (if (not seek)
- (error "Player does not know how to seek")
- (funcall seek seconds)
- (run-hook-with-args 'emms-player-seeked-functions seconds)))))
-
-(defun emms-player-seek-to (seconds)
- "Seek the current player to SECONDS seconds.
-This can be a floating point number for fractions of a second, or
-negative to seek backwards."
- (if (not emms-player-playing-p)
- (error "Can't seek-to player, nothing playing right now")
- (let ((seek (emms-player-get emms-player-playing-p 'seek-to)))
- (if (not seek)
- (error "Player does not know how to seek-to")
- (funcall seek seconds)
- (run-hook-with-args 'emms-player-time-set-functions seconds)))))
-
-(provide 'emms)
-;;; emms.el ends here