├── .gitignore ├── AUTHORS ├── COPYING ├── Makefile ├── NEWS ├── README ├── doc ├── Makefile ├── devel │ └── developer-release.txt ├── emms.texinfo ├── fdl.texi └── gpl.texi ├── emms-auto.in ├── emms-bookmarks.el ├── emms-browser.el ├── emms-cache.el ├── emms-compat.el ├── emms-cue.el ├── emms-filters.el ├── emms-history.el ├── emms-i18n.el ├── emms-idapi-browser.el ├── emms-idapi-musicbrainz.el ├── emms-idapi.el ├── emms-info-exiftool.el ├── emms-info-libtag.el ├── emms-info-metaflac.el ├── emms-info-mp3info.el ├── emms-info-native-flac.el ├── emms-info-native-mp3.el ├── emms-info-native-ogg.el ├── emms-info-native-opus.el ├── emms-info-native-spc.el ├── emms-info-native-vorbis.el ├── emms-info-native.el ├── emms-info-ogginfo.el ├── emms-info-opusinfo.el ├── emms-info-tinytag.el ├── emms-info.el ├── emms-jack.el ├── emms-last-played.el ├── emms-later-do.el ├── emms-librefm-scrobbler.el ├── emms-librefm-stream.el ├── emms-listenbrainz-scrobbler.el ├── emms-lyrics-lrclib.el ├── emms-lyrics.el ├── emms-maint.el ├── emms-mark.el ├── emms-metaplaylist-mode.el ├── emms-mode-line-icon.el ├── emms-mode-line.el ├── emms-mpris.el ├── emms-player-mpd.el ├── emms-player-mpg321-remote.el ├── emms-player-mplayer.el ├── emms-player-mpv.el ├── emms-player-simple.el ├── emms-player-vlc.el ├── emms-player-xine.el ├── emms-playing-time.el ├── emms-playlist-limit.el ├── emms-playlist-mode.el ├── emms-playlist-sort.el ├── emms-print-metadata.1 ├── emms-radio-browser.el ├── emms-score.el ├── emms-setup.el ├── emms-show-all.el ├── emms-source-beets.el ├── emms-source-file.el ├── emms-source-playlist.el ├── emms-stream-info.el ├── emms-streams.el ├── emms-tag-editor.el ├── emms-tag-tracktag.el ├── emms-url.el ├── emms-volume-amixer.el ├── emms-volume-mixerctl.el ├── emms-volume-mpv.el ├── emms-volume-pulse.el ├── emms-volume-sndioctl.el ├── emms-volume.el ├── emms.el ├── src ├── emms-print-metadata.c ├── emms-print-metadata.cpp └── emms-print-metadata.pl └── test ├── emms-info-native-flac-tests.el ├── emms-info-native-mp3-tests.el ├── emms-info-native-ogg-tests.el ├── emms-info-native-tests.el ├── emms-info-native-vorbis-tests.el ├── emms-tests.el └── resources ├── sine.flac ├── sine.mp3 ├── sine.ogg └── sine.opus /.gitignore: -------------------------------------------------------------------------------- 1 | # Files that we ignore when using git. 2 | 3 | *~ 4 | *.elc 5 | /ChangeLog 6 | /src/emms-print-metadata 7 | /doc/emms.info 8 | /doc/emms.html 9 | /emms-autoloads.el 10 | /emms-pkg.el 11 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | This file lists all people who contributed more than a few lines to 2 | emms. This is necessary to keep track of people who have copyright 3 | claims on sources, so please don't be too humble and add yourself. 4 | 5 | 6 | Alex Kost 7 | Akito Mikami 8 | Bram van der Kroef 9 | Bruno Félix R. Ribeiro 10 | Daimrod 11 | Damien Elmes 12 | Daniel Brockman 13 | Daniel Dehennin 14 | Daniel Semyonov 15 | David Engster 16 | Erica Lina Qi 17 | Feng Shu 18 | Grant Shangreaux 19 | Ian Eure 20 | Jean-Philippe Theberge 21 | Jorgen Schaefer 22 | Jose Antonio Ortega Ruiz 23 | Lawrence Mitchell 24 | Lucas Bonnet 25 | Mario Domgörgen 26 | Mario Lang 27 | Martin Schoenmakers 28 | Matthew Kennedy 29 | Michael Olson 30 | Morgan Smith 31 | Nick Alcock 32 | Omar Polo 33 | Petteri Hintsanen 34 | Pierre Neidhardt 35 | stardiviner 36 | Tassilo Horn 37 | Thierry Volpiatto 38 | Trent Buck 39 | Ulrik Jensen 40 | Warren Wilkinson 41 | William Xu 42 | Ye Wenbin 43 | Yoni (Johnathan) Rabkin 44 | mathias.dahl 45 | Rasmus Pank Roulund 46 | Richard Sent 47 | Sean McAfee 48 | ZHANG Weiyi 49 | Mike Kazantsev 50 | Fran Burstall 51 | 52 | 53 | The following is a list of people who contributed trivial patches, 54 | which is to say, simple patches and those with total of 12 lines or 55 | fewer. We started recording trivial patches this way in June of 2017, 56 | so trivial patches before that date would not appear below. 57 | 58 | Alexis 59 | Alfred M. Szmidt 60 | Brian Leung 61 | Christian Faulhammer 62 | Daniel Cerqueira 63 | David Michael 64 | Dieter Deyke 65 | Dirk-Jan C. Binnema 66 | ericalinag 67 | hiecaq 68 | Ian D 69 | Ian Dunn 70 | Isaac Haller 71 | Jesse Weinstein 72 | Katherine Whitlock 73 | Kierin Bell 74 | Leo Okawa Ericson 75 | Luca Capello 76 | Mandar Mitra 77 | Maxim Cournoyer 78 | Pavel Korytov 79 | Rehan Daphedar 80 | Rufus Segar 81 | Ryan Van Wagoner 82 | Sascha Wilde 83 | Sergey Trofimov 84 | Scarlett McAllister 85 | Tom Rauchenwald 86 | fledermaus at #emacs 87 | indio on #emacs 88 | tumashu 89 | 90 | 91 | ;; Local variables: 92 | ;; coding: utf-8 93 | ;; End: 94 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ## Copyright (C) 2006, 2008, 2010, 2012, 2014, 2016-2023 Free Software Foundation, Inc. 2 | ## 3 | ## This file is part of EMMS. 4 | ## 5 | ## EMMS is free software; you can redistribute it and/or modify 6 | ## it under the terms of the GNU General Public License as published by 7 | ## the Free Software Foundation; either version 3, or (at your option) 8 | ## any later version. 9 | ## 10 | ## EMMS is distributed in the hope that it will be useful, 11 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ## GNU General Public License for more details. 14 | ## 15 | ## You should have received a copy of the GNU General Public License 16 | ## along with EMMS; if not, write to the Free Software Foundation, 17 | ## Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | ## Release testing 20 | ## 21 | ## As a policy, each release of Emms should be tested to compile and 22 | ## run well on the two latest Emacs releases. 23 | 24 | 25 | ## Code: 26 | GZIP=gzip 27 | MAN1PAGES=emms-print-metadata.1 28 | DOCDIR=doc/ 29 | SRCDIR=src 30 | SITEFLAG=--no-site-file 31 | EMACS=emacs 32 | 33 | ALLSOURCE=$(wildcard *.el) 34 | SOURCE=$(filter-out $(SPECIAL),$(ALLSOURCE)) 35 | TARGET=$(patsubst %.el,%.elc,$(SOURCE)) 36 | ALLCOMPILED=$(wildcard *.elc) 37 | 38 | DESTDIR= 39 | PREFIX=$(DESTDIR)/usr/local 40 | INFODIR=$(PREFIX)/info 41 | MAN1DIR=$(PREFIX)/share/man/man1 42 | BINDIR=$(PREFIX)/bin 43 | SITELISP=$(PREFIX)/share/emacs/site-lisp/emms 44 | 45 | # testing against previous versions 46 | RELEASE_BIN=$(EMACS) 47 | PREV_RELEASE_BIN=$(EMACS) 48 | 49 | GINSTALLINFO = /usr/bin/ginstall-info --info-dir=$(INFODIR) 50 | # For systems without ginstall-info 51 | INSTALLINFO = /usr/bin/install-info --info-dir=$(INFODIR) 52 | CHANGELOG_CMD = git log --pretty=medium --no-merges 53 | 54 | # The currently released version of EMMS (no longer in use) 55 | VERSION=100.00 56 | 57 | .PHONY: all install docs clean 58 | .PRECIOUS: %.elc 59 | all: show_version emms-auto.el $(TARGET) docs 60 | 61 | emms-auto.el: emms-auto.in $(SOURCE) 62 | cp emms-auto.in emms-auto.el 63 | -rm -f emms-auto.elc 64 | @$(EMACS) -q $(SITEFLAG) -batch \ 65 | -l emms-maint.el \ 66 | -l emms-auto.el \ 67 | -f emms-generate-autoloads \ 68 | $(shell pwd)/emms-auto.el . 69 | 70 | %.elc: %.el 71 | @$(EMACS) -q $(SITEFLAG) -batch \ 72 | -l emms-maint.el \ 73 | -f batch-byte-compile $< 74 | 75 | docs: 76 | $(MAKE) -C $(DOCDIR) 77 | 78 | .PHONY: show_version 79 | show_version: 80 | @$(EMACS) $(SITEFLAG) -batch --eval='(message "\n%s" (emacs-version))' 81 | 82 | .PHONY: 83 | test_releases: 84 | ($(MAKE) EMACS=$(RELEASE_BIN); $(MAKE) clean) 85 | ($(MAKE) EMACS=$(PREV_RELEASE_BIN); $(MAKE) clean) 86 | 87 | emms-print-metadata: $(SRCDIR)/emms-print-metadata.cpp 88 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $(SRCDIR)/$@ $< `taglib-config --cflags --libs` 89 | 90 | install: 91 | test -d $(SITELISP) || mkdir -p $(SITELISP) 92 | test -d $(INFODIR) || install -d $(INFODIR) 93 | install -m 644 $(ALLSOURCE) $(SITELISP) 94 | install -m 644 $(ALLCOMPILED) $(SITELISP) 95 | install -m 0644 $(DOCDIR)emms.info $(INFODIR)/emms.info 96 | for p in $(MAN1PAGES) ; do $(GZIP) -9c $$p > $(MAN1DIR)/$$p.gz ; done 97 | if [ -x /usr/bin/ginstall-info ]; then \ 98 | $(GINSTALLINFO) $(DOCDIR)emms.info; \ 99 | else \ 100 | $(INSTALLINFO) $(DOCDIR)emms.info; \ 101 | fi 102 | if [ -x $(SRCDIR)/emms-print-metadata ]; then \ 103 | echo "emms-print-metadata found, installing"; \ 104 | install -m 755 $(SRCDIR)/emms-print-metadata $(BINDIR)/emms-print-metadata; \ 105 | else \ 106 | echo "skipping emms-print-metadata install"; \ 107 | fi 108 | 109 | remove-info: 110 | if [ -x /usr/bin/ginstall-info ]; then \ 111 | $(GINSTALLINFO) --remove $(DOCDIR)emms.info; \ 112 | else \ 113 | $(INSTALLINFO) --remove $(DOCDIR)emms.info; \ 114 | fi 115 | 116 | ChangeLog: 117 | $(CHANGELOG_CMD) > $@ 118 | 119 | clean: 120 | -rm -f *~ $(DOCDIR)emms.info $(DOCDIR)emms.html $(SRCDIR)/emms-print-metadata 121 | -rm -f *~ *.elc emms-auto.el 122 | 123 | dist: clean emms-auto.el 124 | git archive --format=tar --prefix=emms-$(VERSION)/ HEAD | \ 125 | (cd .. && tar xf -) 126 | rm -f ../emms-$(VERSION)/.gitignore 127 | cp emms-auto.el ../emms-$(VERSION) 128 | $(CHANGELOG_CMD) > ../emms-$(VERSION)/ChangeLog 129 | 130 | release: dist 131 | (cd .. && tar -czf emms-$(VERSION).tar.gz \ 132 | emms-$(VERSION) ; \ 133 | gpg --detach emms-$(VERSION).tar.gz) 134 | 135 | upload: 136 | (cd .. && echo "version: 1.2\ndirectory: emms\nfilename: "emms-$(VERSION).tar.gz"\ncomment: new version of Emms" | gpg --clearsign > emms-$(VERSION).tar.gz.directive.asc && echo open ftp://ftp-upload.gnu.org > upload.lftp ; echo cd /incoming/ftp >> upload.lftp ; echo mput emms-$(VERSION).tar.gz* >> upload.lftp ; echo close >> upload.lftp ; lftp -f upload.lftp ; rm -f upload.lftp) 137 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Emms is the Emacs Multimedia System. Emms displays and plays 2 | multimedia from within GNU/Emacs using a variety of external players 3 | and from different sources. 4 | 5 | Emms can run as a minimalist player and controlled with a handful of 6 | M-x Emacs commands, or a fully-fledged, interactive media 7 | browser. Emms can display album art, play streaming audio, tag music 8 | files, search for lyrics, provide MPD connectivity, control the 9 | volume, and more. 10 | 11 | Please read the info fine manual which is shipped with Emms and 12 | available in the doc directory. We do our best to make sure that it's 13 | up to date and informative. 14 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Don't delete if make is interrupted 2 | .PRECIOUS: %.info %.html 3 | 4 | all: emms.info 5 | 6 | clean: 7 | rm -f *.info 8 | 9 | %.info: %.texinfo 10 | makeinfo --no-split $< 11 | 12 | %.html: %.texinfo 13 | makeinfo --html --no-split $< 14 | -------------------------------------------------------------------------------- /doc/devel/developer-release.txt: -------------------------------------------------------------------------------- 1 | -*- outline -*- 2 | This is an explanation of how to make a release for Emms. Emms is 3 | developed at Savannah (https://savannah.gnu.org/projects/emms/) and 4 | distributed via Emacs ELPA (https://elpa.gnu.org/). 5 | 6 | * clean compilation 7 | Check for clean compilation on the two latest major Emacs releases. 8 | 9 | 10 | * check for basic functionality 11 | Check for clean loading and running with basic functionality on the 12 | two latest major Emacs releases. 13 | 14 | 15 | * version bump 16 | Increase the version number in emms.el (`emms-version' and the elpa 17 | header as a comment). 18 | 19 | 20 | * NEWS 21 | update the NEWS file from the git log to include all significant 22 | user-facing changes. 23 | 24 | 25 | * contributors 26 | Update AUTHORS file with the names of any new contributors. This is a 27 | good chance to make sure we are releasing everything with proper 28 | copyleft. 29 | 30 | 31 | * copyleft notices 32 | Check that any new files have correct copyright notices, and that all 33 | modified files have updated years reflecting when they were changed. 34 | 35 | 36 | * update the manual 37 | Update the manual: 38 | 39 | $ makeinfo --html --no-split emms.texinfo 40 | 41 | Make sure that the manual compiles cleanly and that it looks right. 42 | 43 | 44 | * website update 45 | Update the website with all any pertinent information. Upload a new 46 | version of the manual if it has changed in this release: 47 | 48 | $ cvs commit -m "update website" index.html 49 | 50 | 51 | * tag release and push the tag 52 | Commit and push all of the above changes, then tag the release in git, 53 | for example: 54 | 55 | $ git tag -a 4.2 -m "4.2" 56 | $ git push --tags origin "4.2" 57 | 58 | 59 | * push to repo 60 | Push the version update itself to the git repo. We have automatic 61 | synchronization set up for GNU ELPA, so we are done. 62 | 63 | 64 | * announce 65 | Announce the release in the emms mailing list if it is called for. 66 | -------------------------------------------------------------------------------- /emms-auto.in: -------------------------------------------------------------------------------- 1 | ;;; -*-emacs-lisp-*- 2 | 3 | (defvar generated-autoload-file) 4 | (defvar command-line-args-left) 5 | (defun emms-generate-autoloads () 6 | (interactive) 7 | (require 'autoload) 8 | (setq generated-autoload-file (car command-line-args-left)) 9 | (setq command-line-args-left (cdr command-line-args-left)) 10 | (batch-update-autoloads)) 11 | 12 | (add-to-list 'load-path (directory-file-name 13 | (or (file-name-directory load-file-name) 14 | (car load-path)))) 15 | 16 | (provide 'emms-auto) 17 | ;;; Generated autoloads follow (made by autoload.el). 18 | -------------------------------------------------------------------------------- /emms-bookmarks.el: -------------------------------------------------------------------------------- 1 | ;;; emms-bookmarks.el --- Bookmarks for Emms. -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. 4 | 5 | ;; Author: Yoni Rabkin 6 | ;; Keywords: emms, bookmark 7 | 8 | ;; This file is part of EMMS. 9 | 10 | ;; EMMS is free software; you can redistribute it and/or modify 11 | ;; it under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation; either version 3, or (at your option) 13 | ;; any later version. 14 | ;; 15 | ;; EMMS is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | ;; 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with EMMS; if not, write to the Free Software Foundation, 22 | ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | ;; 26 | ;; You can use this to add ``temporal bookmarks'' (term by Lucas 27 | ;; Bonnet) to your media files. The interesting functions here are 28 | ;; `emms-bookmarks-next', `emms-bookmarks-prev', `emms-bookmarks-add' 29 | ;; (which pauses the player while you describe the bookmark) and 30 | ;; `emms-bookmarks-clear'. All of which do exactly what you think 31 | ;; they do. 32 | 33 | ;;; Code: 34 | 35 | 36 | ;; dependencies 37 | (require 'emms) 38 | (require 'emms-playing-time) 39 | 40 | (defvar emms-bookmarks-prev-overshoot 5 41 | "Time in seconds for skipping a previous bookmark.") 42 | 43 | (defun emms-bookmarks-reset (track) 44 | "Remove all the bookmarks from TRACK." 45 | (emms-track-set track 'bookmarks nil)) 46 | 47 | (defun emms-bookmarks-straight-insertion-sort (item l acc) 48 | "Insert ITEM into the already sorted L, ACC should be nil." 49 | (if (null l) 50 | (append acc (list item)) 51 | (cond ((< (cdr item) (cdr (car l))) (append acc (list item (car l)) (cdr l))) 52 | (t (emms-bookmarks-straight-insertion-sort item (cdr l) (append acc (list (car l)))))))) 53 | 54 | (defun emms-bookmarks-get (track) 55 | "Return the bookmark property from TRACK." 56 | (emms-track-get track 'bookmarks)) 57 | 58 | (defun emms-bookmarks-set (track desc time) 59 | "Set bookmark property for TRACK, text DESC at TIME seconds." 60 | (let ((old-bookmarks (emms-track-get track 'bookmarks)) 61 | (new-bookmarks nil)) 62 | (setq new-bookmarks (emms-bookmarks-straight-insertion-sort (cons desc time) old-bookmarks nil)) 63 | (emms-track-set track 'bookmarks new-bookmarks))) 64 | 65 | (defun emms-bookmarks-set-current (desc) 66 | "Set bookmark property for the current track with text DESC." 67 | (emms-bookmarks-set (emms-playlist-current-selected-track) desc emms-playing-time)) 68 | 69 | (defun emms-bookmarks-search (time track test) 70 | "Return a bookmark based on heuristics. 71 | 72 | TIME should be a reference point in seconds. 73 | TRACK should be an Emms track. 74 | TEST should be a numerical comparator predicate." 75 | (let* ((s (append (list (cons "time" time)) 76 | (copy-sequence (emms-bookmarks-get track)))) 77 | (s (sort s #'(lambda (a b) (funcall test (cdr a) (cdr b)))))) 78 | (while (not (= time (cdar s))) 79 | (setq s (cdr s))) 80 | (when (cdr s) 81 | (car (cdr s))))) 82 | 83 | (defun emms-bookmarks-next-1 (time track) 84 | "Return the bookmark after TIME for TRACK, otherwise return nil." 85 | (emms-bookmarks-search time track #'<)) 86 | 87 | (defun emms-bookmarks-prev-1 (time track) 88 | "Return the bookmark before TIME for TRACK, otherwise return nil." 89 | (emms-bookmarks-search (- time emms-bookmarks-prev-overshoot) track #'>)) 90 | 91 | (defun emms-bookmarks-goto (search-f track failure-message) 92 | "Seek the player to a bookmark. 93 | 94 | SEARCH-F should be a function which returns a bookmark. 95 | TRACK should be an Emms track. 96 | FAILURE-MESSAGE should be a string." 97 | ;; note that when emms is paused then `emms-player-playing-p' => t 98 | (when (not emms-player-playing-p) 99 | (emms-start)) 100 | (let ((m (funcall search-f emms-playing-time track))) 101 | (if m 102 | (progn 103 | (emms-player-seek-to (cdr m)) 104 | (message "%s" (car m))) 105 | (message "%s" failure-message)))) 106 | 107 | 108 | ;; entry points 109 | 110 | (defun emms-bookmarks-next () 111 | "Seek to the next bookmark in the current track." 112 | (interactive) 113 | (emms-bookmarks-goto #'emms-bookmarks-next-1 114 | (emms-playlist-current-selected-track) 115 | "No next bookmark")) 116 | 117 | (defun emms-bookmarks-prev () 118 | "Seek to the previous bookmark in the current track." 119 | (interactive) 120 | (emms-bookmarks-goto #'emms-bookmarks-prev-1 121 | (emms-playlist-current-selected-track) 122 | "No previous bookmark")) 123 | 124 | (defmacro emms-bookmarks-with-paused-player (&rest body) 125 | "Eval BODY with player paused." 126 | `(progn 127 | (when (not emms-player-paused-p) (emms-pause)) 128 | ,@body 129 | (when emms-player-paused-p (emms-pause)))) 130 | 131 | ;; can't use `interactive' to promt the user here because we want to 132 | ;; pause the player before the prompt appears. 133 | (defun emms-bookmarks-add () 134 | "Add a new bookmark to the current track. 135 | 136 | This function pauses the player while prompting the user for a 137 | description of the bookmark. The function resumes the player 138 | after the prompt." 139 | (interactive) 140 | (emms-bookmarks-with-paused-player 141 | (let ((desc (read-string "Description: "))) 142 | (if (emms-playlist-current-selected-track) 143 | (emms-bookmarks-set-current desc) 144 | (error "No current track to bookmark"))))) 145 | 146 | (defun emms-bookmarks-clear () 147 | "Remove all the bookmarks from the current track." 148 | (interactive) 149 | (let ((this (emms-playlist-current-selected-track))) 150 | (when this (emms-bookmarks-reset this)))) 151 | 152 | (provide 'emms-bookmarks) 153 | 154 | ;;; emms-bookmarks.el ends here 155 | -------------------------------------------------------------------------------- /emms-cache.el: -------------------------------------------------------------------------------- 1 | ;;; emms-cache.el --- persistence for emms-track -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2006, 2007, 2008, 2009, 2022 Free Software Foundation, Inc. 4 | 5 | ;; Author: Damien Elmes , Yoni Rabkin 6 | ;; Keywords: emms, mp3, mpeg, multimedia 7 | 8 | ;; This file is part of EMMS. 9 | 10 | ;; EMMS is free software; you can redistribute it and/or modify 11 | ;; it under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation; either version 3, or (at your option) 13 | ;; any later version. 14 | 15 | ;; EMMS is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with EMMS; see the file COPYING. If not, write to the 22 | ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 23 | ;; Boston, MA 02110-1301, USA. 24 | 25 | ;;; Commentary: 26 | 27 | ;; The cache is a mapping of a full path name to information, and so 28 | ;; it is invalidated when you rename or move files about. It also 29 | ;; does not differentiate between file or uri tracks. 30 | 31 | ;; Because cache lookups are much faster than disk access, this works 32 | ;; much better with a emms-later-do-interval of something like 0.001. Also 33 | ;; consider using synchronous mode, as it's quite fast now. 34 | 35 | ;; This code is activated by (emms-standard) and above. 36 | 37 | ;; To activate it by hand, use: 38 | 39 | ;; (emms-cache 1) 40 | 41 | ;;; Code: 42 | 43 | (require 'emms) 44 | (require 'emms-info) 45 | 46 | (when (fboundp 'define-hash-table-test) 47 | (define-hash-table-test 'string-hash 'string= 'sxhash)) 48 | 49 | (defvar emms-cache-db (make-hash-table 50 | :test (if (fboundp 'define-hash-table-test) 51 | 'string-hash 52 | 'equal)) 53 | "A mapping of paths to file info. 54 | This is used to cache over emacs sessions.") 55 | 56 | (defvar emms-cache-dirty nil 57 | "True if the cache has been updated since init.") 58 | 59 | (defcustom emms-cache-file (concat (file-name-as-directory emms-directory) "cache") 60 | "A file used to store cached file information over sessions." 61 | :group 'emms 62 | :type 'file) 63 | 64 | (defcustom emms-cache-file-coding-system 'utf-8 65 | "Coding system used for saving `emms-cache-file'." 66 | :group 'emms 67 | :type 'coding-system) 68 | 69 | (defun emms-cache (arg) 70 | "Turn on Emms caching if ARG is positive, off otherwise." 71 | (interactive "p") 72 | (if (and arg (> arg 0)) 73 | (progn 74 | (unless emms-cache-dirty 75 | (emms-cache-restore)) 76 | (unless noninteractive 77 | (add-hook 'kill-emacs-hook 'emms-cache-save)) 78 | (setq emms-cache-get-function 'emms-cache-get) 79 | (setq emms-cache-set-function 'emms-cache-set) 80 | (setq emms-cache-modified-function 'emms-cache-dirty)) 81 | (remove-hook 'kill-emacs-hook 'emms-cache-save) 82 | (setq emms-cache-get-function nil) 83 | (setq emms-cache-set-function nil) 84 | (setq emms-cache-modified-function nil))) 85 | 86 | ;;;###autoload 87 | (defun emms-cache-enable () 88 | "Enable caching of Emms track data." 89 | (interactive) 90 | (emms-cache 1) 91 | (message "Emms cache enabled")) 92 | 93 | ;;;###autoload 94 | (defun emms-cache-disable () 95 | "Disable caching of Emms track data." 96 | (interactive) 97 | (emms-cache -1) 98 | (message "Emms cache disabled")) 99 | 100 | ;;;###autoload 101 | (defun emms-cache-toggle () 102 | "Toggle caching of Emms track data." 103 | (interactive) 104 | (if emms-cache-get-function 105 | (emms-cache-disable) 106 | (emms-cache-enable))) 107 | 108 | (defsubst emms-cache-dirty (&rest _ignored) 109 | "Mark the cache as dirty." 110 | (setq emms-cache-dirty t)) 111 | 112 | (defun emms-cache-get (type path) 113 | "Return a cache element for PATH, or nil." 114 | (ignore type) ;; implicitly ignored before 2021-03-02 115 | (gethash path emms-cache-db)) 116 | 117 | (defun emms-cache-set (type path track) 118 | "Set PATH to TRACK in the cache." 119 | (ignore type) ;; implicitly ignored before 2021-03-02 120 | (puthash path track emms-cache-db) 121 | (emms-cache-dirty)) 122 | 123 | (defun emms-cache-del (path) 124 | "Remove a track from the cache, with key PATH." 125 | (remhash path emms-cache-db) 126 | (emms-cache-dirty)) 127 | 128 | (defun emms-cache-save () 129 | "Save the track cache to a file." 130 | (interactive) 131 | (when emms-cache-dirty 132 | (message "Saving emms track cache...") 133 | (with-temp-buffer 134 | (insert 135 | (concat ";;; .emms-cache -*- mode: emacs-lisp; coding: " 136 | (symbol-name emms-cache-file-coding-system) 137 | "; -*-\n")) 138 | (maphash (lambda (k v) 139 | (insert (format 140 | "(puthash %S '%S emms-cache-db)\n" k v))) 141 | emms-cache-db) 142 | (when (fboundp 'set-buffer-file-coding-system) 143 | (set-buffer-file-coding-system emms-cache-file-coding-system)) 144 | (unless (file-directory-p (file-name-directory emms-cache-file)) 145 | (make-directory (file-name-directory emms-cache-file))) 146 | (write-region (point-min) (point-max) emms-cache-file) 147 | (message "Saving emms track cache...done")) 148 | (setq emms-cache-dirty nil))) 149 | 150 | (defun emms-cache-restore () 151 | "Restore the track cache from a file." 152 | (interactive) 153 | (load emms-cache-file t nil t) 154 | (setq emms-cache-dirty nil)) 155 | 156 | (defun emms-cache-sync (arg) 157 | "Sync the cache with the data on disc. 158 | Remove non-existent files, and update data for files which have 159 | been modified. With prefix argument, update data for all files 160 | regardless of whether they have been modified or not." 161 | (interactive "P") 162 | (let (removed) 163 | (maphash (lambda (path track) 164 | (when (emms-track-file-p track) 165 | ;; if no longer here, remove 166 | (if (not (file-exists-p path)) 167 | (progn 168 | (remhash path emms-cache-db) 169 | (setq removed t)) 170 | (let ((file-mtime (emms-info-track-file-mtime track)) 171 | (info-mtime (emms-track-get track 'info-mtime))) 172 | (when (or (not info-mtime) 173 | (emms-time-less-p info-mtime file-mtime) 174 | arg) 175 | (emms-info-initialize-track track arg)))))) 176 | emms-cache-db) 177 | (when removed 178 | (setq emms-cache-dirty t)))) 179 | 180 | (defun emms-cache-reset () 181 | "Reset the cache." 182 | (interactive) 183 | (when (yes-or-no-p "Really reset the cache? ") 184 | (setq emms-cache-db 185 | (make-hash-table 186 | :test (if (fboundp 'define-hash-table-test) 187 | 'string-hash 188 | 'equal))) 189 | (setq emms-cache-dirty t) 190 | (emms-cache-save))) 191 | 192 | (provide 'emms-cache) 193 | ;;; emms-cache.el ends here 194 | -------------------------------------------------------------------------------- /emms-compat.el: -------------------------------------------------------------------------------- 1 | ;;; emms-compat.el --- Compatibility routines for EMMS -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. 4 | 5 | ;; Author: Michael Olson 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | ;; 14 | ;; EMMS is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | ;; 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; see the file COPYING. If not, write to the 21 | ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 22 | ;; Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; These are functions and macros that EMMS needs in order to be 27 | ;; compatible with various Emacs and XEmacs versions. 28 | 29 | ;;; Code: 30 | 31 | 32 | ;;; Miscellaneous 33 | 34 | (defun emms-propertize (string &rest properties) 35 | (if (fboundp 'propertize) 36 | (apply #'propertize string properties) 37 | (set-text-properties 0 (length string) properties string) 38 | string)) 39 | 40 | ;; Emacs accepts three arguments to `make-obsolete', but the XEmacs 41 | ;; version only takes two arguments 42 | (defun emms-make-obsolete (old-name new-name when) 43 | "Make the byte-compiler warn that OLD-NAME is obsolete. 44 | The warning will say that NEW-NAME should be used instead. 45 | WHEN should be a string indicating when the function was 46 | first made obsolete, either the file's revision number or an 47 | EMMS release version number." 48 | (if (featurep 'xemacs) 49 | (make-obsolete old-name new-name) 50 | (make-obsolete old-name new-name when))) 51 | 52 | 53 | ;;; Time and timers 54 | 55 | (defun emms-cancel-timer (timer) 56 | "Cancel the given TIMER." 57 | (when timer 58 | (cond ((fboundp 'cancel-timer) 59 | (cancel-timer timer)) 60 | ((fboundp 'delete-itimer) 61 | (delete-itimer timer))))) 62 | 63 | (defun emms-time-less-p (t1 t2) 64 | "Say whether time T1 is less than time T2." 65 | (or (< (car t1) (car t2)) 66 | (and (= (car t1) (car t2)) 67 | (< (nth 1 t1) (nth 1 t2))))) 68 | 69 | 70 | ;;; Highline 71 | 72 | (defun emms-activate-highlighting-mode () 73 | "Activate highline mode." 74 | (if (featurep 'xemacs) 75 | (progn 76 | (require 'highline) 77 | (highline-local-mode 1)) 78 | (progn 79 | (require 'hl-line) 80 | (hl-line-mode 1)))) 81 | 82 | (declare-function hl-line-highlight "" nil) 83 | 84 | ;; called from emms-lyrics 85 | (defun emms-line-highlight () 86 | "Highlight the current line. You must call 87 | emms-activate-highlighting-mode beforehand." 88 | (if (featurep 'xemacs) 89 | (highline-highlight-current-line) 90 | (hl-line-highlight))) 91 | 92 | 93 | ;;; Movement and position 94 | 95 | (defun emms-move-beginning-of-line (arg) 96 | "Move point to beginning of current line as displayed. 97 | If there's an image in the line, this disregards newlines 98 | which are part of the text that the image rests on." 99 | (if (fboundp 'move-beginning-of-line) 100 | (move-beginning-of-line arg) 101 | (if (numberp arg) 102 | (forward-line (1- arg)) 103 | (forward-line 0)))) 104 | 105 | (defun emms-line-number-at-pos (&optional pos) 106 | "Return (narrowed) buffer line number at position POS. 107 | If POS is nil, use current buffer location." 108 | (if (fboundp 'line-number-at-pos) 109 | (line-number-at-pos pos) 110 | (let ((opoint (or pos (point))) start) 111 | (save-excursion 112 | (goto-char (point-min)) 113 | (setq start (point)) 114 | (goto-char opoint) 115 | (forward-line 0) 116 | (1+ (count-lines start (point))))))) 117 | 118 | 119 | ;;; Regular expression matching 120 | 121 | (defun emms-replace-regexp-in-string (regexp replacement text 122 | &optional fixedcase literal) 123 | "Replace REGEXP with REPLACEMENT in TEXT. 124 | If fourth arg FIXEDCASE is non-nil, do not alter case of replacement text. 125 | If fifth arg LITERAL is non-nil, insert REPLACEMENT literally." 126 | (cond 127 | ((fboundp 'replace-regexp-in-string) 128 | (replace-regexp-in-string regexp replacement text fixedcase literal)) 129 | ((and (featurep 'xemacs) (fboundp 'replace-in-string)) 130 | (replace-in-string text regexp replacement literal)) 131 | (t (let ((repl-len (length replacement)) 132 | start) 133 | (save-match-data 134 | (while (setq start (string-match regexp text start)) 135 | (setq start (+ start repl-len) 136 | text (replace-match replacement fixedcase literal text))))) 137 | text))) 138 | 139 | (defun emms-match-string-no-properties (num &optional string) 140 | (if (fboundp 'match-string-no-properties) 141 | (match-string-no-properties num string) 142 | (match-string num string))) 143 | 144 | 145 | ;;; Common Lisp 146 | 147 | (defun emms-delete-if (predicate seq) 148 | "Remove all items satisfying PREDICATE in SEQ. 149 | This is a destructive function: it reuses the storage of SEQ 150 | whenever possible." 151 | ;; remove from car 152 | (while (when (funcall predicate (car seq)) 153 | (setq seq (cdr seq)))) 154 | ;; remove from cdr 155 | (let ((ptr seq) 156 | (next (cdr seq))) 157 | (while next 158 | (when (funcall predicate (car next)) 159 | (setcdr ptr (if (consp next) 160 | (cdr next) 161 | nil))) 162 | (setq ptr (cdr ptr)) 163 | (setq next (cdr ptr)))) 164 | seq) 165 | 166 | (defun emms-find-if (predicate seq) 167 | "Find the first item satisfying PREDICATE in SEQ. 168 | Return the matching item, or nil if not found." 169 | (catch 'found 170 | (dolist (el seq) 171 | (when (funcall predicate el) 172 | (throw 'found el))))) 173 | 174 | (defun emms-remove-if-not (predicate seq) 175 | "Remove all items not satisfying PREDICATE in SEQ. 176 | This is a non-destructive function; it makes a copy of SEQ to 177 | avoid corrupting the original SEQ." 178 | (let (newseq) 179 | (dolist (el seq) 180 | (when (funcall predicate el) 181 | (setq newseq (cons el newseq)))) 182 | (nreverse newseq))) 183 | 184 | (provide 'emms-compat) 185 | ;;; emms-compat.el ends here 186 | -------------------------------------------------------------------------------- /emms-cue.el: -------------------------------------------------------------------------------- 1 | ;;; emms-cue.el --- Recognize cue sheet file -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2009 Free Software Foundation, Inc. 4 | 5 | ;; Author: William Xu 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or 10 | ;; modify it under the terms of the GNU General Public License 11 | ;; as published by the Free Software Foundation; either version 3 12 | ;; of the License, or (at your option) any later version. 13 | 14 | ;; EMMS is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; if not, write to the Free Software Foundation, 21 | ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | ;;; Commentary: 24 | 25 | ;; By parsing cue file, we will be able to jump to arbitary track or 26 | ;; play next/previous track from a single .ape or .flac file. 27 | 28 | ;;; Code: 29 | 30 | (require 'emms-playing-time) 31 | (require 'emms-info) 32 | 33 | (defun emms-cue-next () 34 | "Play next track from .cue file." 35 | (interactive) 36 | (let ((cue-track (emms-cue-next-track))) 37 | (if (cdr cue-track) 38 | (progn 39 | (emms-seek-to (cdr cue-track)) 40 | (message "Will play: %s" (car cue-track))) 41 | (message "Nothing to seek or missing .cue file?")))) 42 | 43 | (defun emms-cue-previous () 44 | "Play previous track from .cue file." 45 | (interactive) 46 | (let ((cue-track (emms-cue-previous-track))) 47 | (if (cdr cue-track) 48 | (progn 49 | (emms-seek-to (cdr cue-track)) 50 | (message "Will play: %s" (car cue-track))) 51 | (message "Nothing to seek or missing .cue file?")))) 52 | 53 | (defun emms-cue-jump () 54 | "Select a track from .cue file to play using completion." 55 | (interactive) 56 | (let ((cue-track (emms-cue-select-track))) 57 | (if (cdr cue-track) 58 | (progn 59 | (emms-seek-to (cdr cue-track)) 60 | (message "Will play: %s" (car cue-track))) 61 | (message "Nothing to seek or missing .cue file?")))) 62 | 63 | (defun emms-cue-next-track (&optional previous-p) 64 | "Get title and offset of next track from .cue file. 65 | 66 | When PREVIOUS-P is t, get previous track info instead." 67 | (let* ((track (emms-playlist-current-selected-track)) 68 | (name (emms-track-get track 'name)) 69 | (cue (concat (file-name-sans-extension name)".cue"))) 70 | (when (file-exists-p cue) 71 | (with-temp-buffer 72 | (emms-insert-file-contents cue) 73 | (save-excursion 74 | (if previous-p 75 | (goto-char (point-max)) 76 | (goto-char (point-min))) 77 | (let ((offset nil) 78 | (title "") 79 | ;; We should search one more track far when getting previous 80 | ;; track. 81 | (one-more-track previous-p)) 82 | (while (and (not offset) 83 | (funcall 84 | (if previous-p 'search-backward-regexp 'search-forward-regexp) 85 | "INDEX 01 \\([0-9][0-9]\\):\\([0-9][0-9]\\):\\([0-9][0-9]\\)" nil t 1)) 86 | (let* ((min (string-to-number (match-string-no-properties 1))) 87 | (sec (string-to-number (match-string-no-properties 2))) 88 | (msec (string-to-number (match-string-no-properties 3))) 89 | (total-sec (+ (* min 60) sec (/ msec 100.0)))) 90 | (when (funcall (if previous-p '> '<) emms-playing-time total-sec) 91 | (if (not one-more-track) 92 | (progn 93 | (setq offset total-sec) 94 | (when (search-backward-regexp "TITLE \"\\(.*\\)\"" nil t 1) 95 | (setq title (match-string-no-properties 1)))) 96 | (setq one-more-track nil))))) 97 | (cons title offset))))))) 98 | 99 | (defun emms-cue-previous-track () 100 | "See `emms-cue-next-track'." 101 | (emms-cue-next-track t)) 102 | 103 | (defun emms-cue-select-track () 104 | "Get a list of title and offset of tracks from .cue file and call 105 | completing-read to select one" 106 | (let* ((track (emms-playlist-current-selected-track)) 107 | (name (emms-track-get track 'name)) 108 | (cue (concat (file-name-sans-extension name)".cue")) 109 | (tracks-found '())) 110 | (when (file-exists-p cue) 111 | (with-temp-buffer 112 | (emms-insert-file-contents cue) 113 | (save-excursion 114 | (goto-char (point-max)) ; search backwards 115 | (while (search-backward-regexp "INDEX 01 \\([0-9][0-9]\\):\\([0-9][0-9]\\):\\([0-9][0-9]\\)" nil t 1) 116 | (let* ((min (string-to-number (match-string-no-properties 1))) 117 | (sec (string-to-number (match-string-no-properties 2))) 118 | (msec (string-to-number (match-string-no-properties 3))) 119 | (total-sec (+ (* min 60) sec (/ msec 100.0))) 120 | (title "")) 121 | (when (search-backward-regexp "TITLE \"\\(.*\\)\"" nil t 1) 122 | (setq title (match-string-no-properties 1))) 123 | (push (cons title total-sec) tracks-found))))) 124 | (let* ((tracks-complete-table (lambda (string pred action) 125 | (if (eq action 'metadata) 126 | `(metadata (display-sort-function . ,#'identity)) ; don't sort 127 | (complete-with-action action (mapcar #'car tracks-found) string pred)))) 128 | (selection (completing-read "Select a track to play: " tracks-complete-table nil t))) 129 | (assoc selection tracks-found))))) 130 | 131 | (defun emms-info-cueinfo (track) 132 | "Add track information to TRACK. 133 | This is a useful element for `emms-info-functions'." 134 | (when (and (emms-track-file-p track) 135 | (string-match "\\.\\(ape\\|flac\\)\\'" (emms-track-name track))) 136 | (let ((cue (concat (file-name-sans-extension (emms-track-name track)) 137 | ".cue"))) 138 | (when (file-exists-p cue) 139 | (with-temp-buffer 140 | (emms-insert-file-contents cue) 141 | (save-excursion 142 | (mapc (lambda (i) 143 | (goto-char (point-min)) 144 | (when (let ((case-fold-search t)) 145 | (search-forward-regexp 146 | (concat (car i) " \\(.*\\)") nil t 1)) 147 | (emms-track-set track 148 | (cdr i) 149 | (replace-regexp-in-string 150 | "\\`\"\\|\"\\'" "" (match-string 1))))) 151 | '(("performer" . info-artist) 152 | ("title" . info-album) 153 | ("title" . info-title) 154 | ("rem date" . info-year))))))))) 155 | 156 | 157 | (provide 'emms-cue) 158 | ;;; emms-cue.el ends here 159 | -------------------------------------------------------------------------------- /emms-history.el: -------------------------------------------------------------------------------- 1 | ;;; emms-history.el -- save all playlists when exiting emacs -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2006-2021 Free Software Foundation, Inc. 4 | ;; 5 | ;; Author: Ye Wenbin 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; This program is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | ;; 14 | ;; This program is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | ;; 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with this program; if not, write to the Free Software 21 | ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 | 23 | ;;; Commentary: 24 | 25 | ;; Saves all playlists when you close emacs. When you start it up again use 26 | ;; M-x emms-history-load to restore all saved playlists. 27 | 28 | ;; To use it put the following into your ~/.emacs: 29 | ;; 30 | ;; (require 'emms-history) 31 | ;; 32 | ;; If all playlists should be restored on startup add this, too: 33 | ;; 34 | ;; (emms-history-load) 35 | 36 | ;;; Code: 37 | 38 | (require 'emms) 39 | 40 | (defgroup emms-history nil 41 | "Saving and restoring all playlists when closing/restarting 42 | Emacs." 43 | :prefix "emms-history-" 44 | :group 'emms) 45 | 46 | (defcustom emms-history-file (concat (file-name-as-directory emms-directory) "history") 47 | "The file to save playlists in." 48 | :type 'string) 49 | 50 | (defcustom emms-history-start-playing nil 51 | "If non-nil emms starts playing the current track after 52 | `emms-history-load' was invoked." 53 | :type 'boolean) 54 | 55 | (defcustom emms-history-file-coding-system 'utf-8 56 | "Coding system used for saving `emms-history-file'." 57 | :type 'coding-system) 58 | 59 | (defun emms-history-save () 60 | "Save all playlists that are open in this Emacs session." 61 | (interactive) 62 | (when (stringp emms-history-file) 63 | (let ((oldbuf emms-playlist-buffer) 64 | ;; print with no limit 65 | print-length print-level 66 | emms-playlist-buffer playlists) 67 | (save-excursion 68 | (dolist (buf (emms-playlist-buffer-list)) 69 | (set-buffer buf) 70 | (when (> (buffer-size) 0) ; make sure there is track in the buffer 71 | (setq emms-playlist-buffer buf 72 | playlists 73 | (cons 74 | (list (buffer-name) 75 | (or 76 | (and emms-playlist-selected-marker 77 | (marker-position emms-playlist-selected-marker)) 78 | (point-min)) 79 | (save-restriction 80 | (widen) 81 | (nreverse 82 | (emms-playlist-tracks-in-region (point-min) 83 | (point-max))))) 84 | playlists)))) 85 | (with-temp-buffer 86 | (insert 87 | (concat ";;; emms history -*- mode: emacs-lisp; coding: " 88 | (symbol-name emms-history-file-coding-system) 89 | "; -*-\n")) 90 | (insert "(\n;; active playlist\n") 91 | (prin1 (buffer-name oldbuf) (current-buffer)) 92 | (insert "\n;; playlists: ((BUFFER_NAME SELECT_POSITION TRACKS) ...)\n") 93 | (prin1 playlists (current-buffer)) 94 | (insert "\n;; play method\n") 95 | (prin1 `((emms-repeat-track . ,emms-repeat-track) 96 | (emms-repeat-playlist . ,emms-repeat-playlist)) 97 | (current-buffer)) 98 | (insert "\n)") 99 | (write-file emms-history-file)))))) 100 | 101 | (unless noninteractive 102 | (add-hook 'kill-emacs-hook 'emms-history-save)) 103 | 104 | (defun emms-history-load () 105 | "Restore all playlists in `emms-history-file'." 106 | (interactive) 107 | (when (and (stringp emms-history-file) 108 | (file-exists-p emms-history-file)) 109 | (let (history buf) 110 | (with-temp-buffer 111 | (emms-insert-file-contents emms-history-file) 112 | (setq history (read (current-buffer))) 113 | (dolist (playlist (cadr history)) 114 | (with-current-buffer (emms-playlist-new (car playlist)) 115 | (setq emms-playlist-buffer (current-buffer)) 116 | (if (string= (car playlist) (car history)) 117 | (setq buf (current-buffer))) 118 | (mapc 'emms-playlist-insert-track 119 | (nth 2 playlist)) 120 | (run-hooks 'emms-playlist-source-inserted-hook) 121 | (ignore-errors 122 | (emms-playlist-select (cadr playlist))))) 123 | (setq emms-playlist-buffer buf) 124 | (dolist (method (nth 2 history)) 125 | (set (car method) (cdr method))) 126 | (ignore-errors 127 | (when emms-history-start-playing 128 | (emms-start))))))) 129 | 130 | (provide 'emms-history) 131 | ;;; emms-history.el ends here 132 | -------------------------------------------------------------------------------- /emms-i18n.el: -------------------------------------------------------------------------------- 1 | ;;; emms-i18n.el --- functions for handling coding systems -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. 4 | 5 | ;; Author: Ye Wenbin 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; This program is free software; you can redistribute it and/or 10 | ;; modify it under the terms of the GNU General Public License as 11 | ;; published by the Free Software Foundation; either version 3, or (at 12 | ;; your option) any later version. 13 | 14 | ;; This program is distributed in the hope that it will be useful, but 15 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | ;; General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with this program; if not, write to the Free Software 21 | ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 | 23 | ;;; Commentary: 24 | 25 | ;; When reading from process, first check the car part of 26 | ;; `emms-i18n-default-coding-system'; if non-nil, use this for 27 | ;; decoding, and never detect coding system; if nil, first call 28 | ;; `emms-i18n-coding-detect-functions' to get coding system, if 29 | ;; success, decode the result, otherwise, use 30 | ;; `emms-i18n-detect-coding-function', the Emacs detect coding 31 | ;; function, if the coding detected is not in 32 | ;; `emms-i18n-never-used-coding-system', decode it, otherwise use 33 | ;; locale-coding-system. 34 | 35 | ;; When writing/sending data to process, first check the cdr part of 36 | ;; `emms-i18n-default-coding-system', if non-nil, use this to encode 37 | ;; data, otherwise do nothing, that means use 38 | ;; `default-process-coding-system' or `process-coding-system-alist' to 39 | ;; encode data. 40 | 41 | ;; Put this file into your load-path and the following into your 42 | ;; ~/.emacs: 43 | 44 | ;; (require 'emms-i18n) 45 | 46 | ;;; Code: 47 | 48 | (provide 'emms-i18n) 49 | 50 | ;; TODO: Use defcustom. 51 | (defvar emms-i18n-never-used-coding-system 52 | '(raw-text undecided) 53 | "If the `emms-i18n-coding-detect-functions' return a coding 54 | system in this list, use `emms-i18n-default-coding-system' 55 | instead.") 56 | 57 | ;; TODO: Use defcustom. 58 | (defvar emms-i18n-coding-system-for-read 59 | 'utf-8 60 | "If coding detect fails, use this for decoding.") 61 | 62 | ;; TODO: Use defcustom. 63 | (defvar emms-i18n-default-coding-system 64 | '(no-conversion . no-conversion) 65 | "If non-nil, use this for decoding and encoding.") 66 | 67 | ;; TODO: Use defcustom. 68 | (defvar emms-i18n-coding-detect-functions 69 | nil 70 | "A list of functions to call to detect codings.") 71 | 72 | ;; TODO: Use defcustom. 73 | (defvar emms-i18n-detect-max-size 74 | 10000 75 | "Maximum amount of bytes to detect the coding system. nil 76 | means to scan the whole buffer.") 77 | 78 | (defun emms-i18n-iconv (from to str) 79 | "Convert string STR from FROM coding to TO coding." 80 | (if (and from to) 81 | (decode-coding-string 82 | (encode-coding-string str to) 83 | from) 84 | str)) 85 | 86 | (defun emms-i18n-iconv-region (beg end from to) 87 | (when (and from to) 88 | (save-restriction 89 | (narrow-to-region beg end) 90 | (encode-coding-region (point-min) (point-max) to) 91 | (decode-coding-region (point-min) (point-max) from)))) 92 | 93 | (defun emms-i18n-iconv-buffer (from to &optional buf) 94 | "Convert buffer BUF from FROM coding to TO coding. BUF 95 | defaults to the current buffer." 96 | (save-excursion 97 | (and buf (set-buffer buf)) 98 | (emms-i18n-iconv-region (point-min) (point-max) from to))) 99 | 100 | (defun emms-i18n-set-default-coding-system (read-coding write-coding) 101 | "Set `emms-i18n-default-coding-system'." 102 | (interactive "zSet coding system for read: \nzSet coding system for write: ") 103 | (setq emms-i18n-default-coding-system 104 | (cons 105 | (and (coding-system-p read-coding) read-coding) 106 | (and (coding-system-p write-coding) write-coding))) 107 | (message (concat 108 | (if (car emms-i18n-default-coding-system) 109 | (format "The coding system for reading is %S." (car emms-i18n-default-coding-system)) 110 | "Good, you want me to detect the coding system!") 111 | (format " The coding system for writing is %S." 112 | (or (cdr emms-i18n-default-coding-system) 113 | (cdr default-process-coding-system)))))) 114 | 115 | (defun emms-i18n-call-process-simple (&rest args) 116 | "Run a program and return the program result. 117 | If the car part of `emms-i18n-default-coding-system' is non-nil, 118 | the program result will be decoded using the car part of 119 | `emms-i18n-default-coding-system'. Otherwise, use 120 | `emms-i18n-coding-detect-functions' to detect the coding system 121 | of the result. If the `emms-i18n-coding-detect-functions' 122 | failed, use `emms-i18n-detect-coding-function' to detect coding 123 | system. If all the coding systems are nil or in 124 | `emms-i18n-never-used-coding-system', decode the result using 125 | `emms-i18n-coding-system-for-read'. 126 | 127 | ARGS are the same as in `call-process', except BUFFER should 128 | always have the value t. Otherwise the coding detection will not 129 | be performed." 130 | (let ((default-process-coding-system (copy-tree default-process-coding-system)) 131 | (process-coding-system-alist nil) exit pos) 132 | (when (eq (nth 2 args) 't) 133 | (setcar default-process-coding-system (car emms-i18n-default-coding-system)) 134 | (setq pos (point))) 135 | (setq exit (apply 'call-process args)) 136 | (when (and (eq (nth 2 args) 't) 137 | (eq (car emms-i18n-default-coding-system) 'no-conversion)) 138 | (save-restriction 139 | (narrow-to-region pos (point)) 140 | (decode-coding-region (point-min) (point-max) (emms-i18n-detect-buffer-coding-system)))) 141 | exit)) 142 | 143 | ;; TODO: Is this function useful? 144 | (defun emms-i18n-call-process (&rest args) 145 | "Run the program like `call-process'. If the cdr part of 146 | `emms-i18n-default-coding-system' is non-nil, the string in ARGS 147 | will be encoded by the cdr part of 148 | `emms-i18n-default-coding-system'; otherwise, all parameters are 149 | simply passed to `call-process'." 150 | (with-temp-buffer 151 | (if (cdr emms-i18n-default-coding-system) 152 | (let ((default-process-coding-system emms-i18n-default-coding-system) 153 | (process-coding-system-alist nil)) 154 | (apply 'call-process args)) 155 | (apply 'call-process args)))) 156 | 157 | (defun emms-i18n-detect-coding-function (size) 158 | (detect-coding-region (point) 159 | (+ (if (null emms-i18n-detect-max-size) 160 | size 161 | (min size emms-i18n-detect-max-size)) 162 | (point)) t)) 163 | 164 | (defun emms-i18n-detect-buffer-coding-system (&optional buf) 165 | "Before calling this function, make sure the buffer is literal." 166 | (let ((size (- (point-max) (point-min))) 167 | (_func (append emms-i18n-coding-detect-functions 'emms-i18n-detect-coding-function)) 168 | coding) 169 | (save-excursion 170 | (and buf (set-buffer buf)) 171 | (goto-char (point-min)) 172 | (when (> size 0) 173 | (setq coding (run-hook-with-args-until-success 'func size)) 174 | (if (member (coding-system-base coding) emms-i18n-never-used-coding-system) 175 | (setq coding (emms-i18n-detect-coding-function size)))) 176 | (if (or (null coding) (member (coding-system-base coding) emms-i18n-never-used-coding-system)) 177 | emms-i18n-coding-system-for-read 178 | coding)))) 179 | 180 | ;;; emms-i18n.el ends here 181 | -------------------------------------------------------------------------------- /emms-idapi.el: -------------------------------------------------------------------------------- 1 | ;;; emms-idapi.el --- EMMS Music ID API support -*- lexical-binding: t; -*- 2 | ;; 3 | 4 | ;; Copyright (C) 2024 Free Software Foundation, Inc. 5 | 6 | ;; Author: Yoni Rabkin 7 | 8 | ;; This file is part of EMMS. 9 | 10 | ;; EMMS is free software; you can redistribute it and/or modify it 11 | ;; under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation; either version 3, or (at your option) 13 | ;; any later version. 14 | 15 | ;; EMMS is distributed in the hope that it will be useful, but WITHOUT 16 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 17 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 18 | ;; License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with EMMS; see the file COPYING. If not, write to the Free 22 | ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 23 | ;; MA 02110-1301, USA. 24 | 25 | 26 | ;;; Commentary: 27 | ;; 28 | 29 | ;;; Code: 30 | (require 'emms-idapi-musicbrainz) 31 | 32 | 33 | (defvar emms-idapi-services-alist 34 | '((musicbrainz . ((search-f . emms-idapi-musicbrainz-search) 35 | (name . "MusicBrainz") 36 | (website . "https://musicbrainz.org/")))) 37 | "Association list of services supported by IDAPI.") 38 | 39 | (defvar emms-idapi-service nil 40 | "The music search service currently in use.") 41 | 42 | 43 | (defun emms-idapi-search (service search-term-alist) 44 | "Search against SERVICE for SEARCH-TERM-ALIST." 45 | (let ((search-function (alist-get 'search-f 46 | (alist-get service 47 | emms-idapi-services-alist)))) 48 | (if (not search-function) 49 | (error "`%s' is an unsupported service." service)) 50 | (funcall search-function search-term-alist))) 51 | 52 | 53 | (provide 'emms-idapi) 54 | 55 | ;;; emms-idapi.el ends here 56 | -------------------------------------------------------------------------------- /emms-info-exiftool.el: -------------------------------------------------------------------------------- 1 | ;;; emms-info-exiftool.el --- info-method for EMMS using exiftool -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2020, 2022, 2025 Free Software Foundation, Inc. 4 | 5 | ;; Author: Yoni Rabkin (yrk@gnu.org) 6 | ;; Keywords: multimedia 7 | 8 | ;; EMMS is free software; you can redistribute it and/or modify it 9 | ;; under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; EMMS is distributed in the hope that it will be useful, but WITHOUT 14 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 15 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 16 | ;; License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with EMMS; see the file COPYING.. If not, see 20 | ;; . 21 | 22 | ;;; Commentary: 23 | 24 | ;; (setq emms-info-functions '(emms-info-exiftool)) 25 | 26 | ;; To use this you would need to have exiftool installed on your 27 | ;; system. 28 | 29 | 30 | ;;; Code: 31 | 32 | (require 'emms-info) 33 | (require 'json) 34 | 35 | 36 | (defgroup emms-info-exiftool nil 37 | "Options for EMMS." 38 | :group 'emms-info) 39 | 40 | (defvar emms-info-exiftool-field-map 41 | '((info-album . Album) 42 | (info-artist . Artist) 43 | (info-title . Title) 44 | (info-tracknumber . TrackNumber) 45 | (info-composer . Composer) 46 | (info-year . Year) 47 | (info-discnumber . Discnumber) 48 | (info-genre . Genre) 49 | (info-note . Comment) 50 | (info-playing-time . Duration) 51 | (info-albumartist . Albumartist)) 52 | "Mapping for exiftool output.") 53 | 54 | 55 | ;; should only be called inside a buffer containing the json output of 56 | ;; exiftool 57 | (defun emms-info-exiftool-time () 58 | "Convert from exiftool-time to seconds." 59 | (save-excursion 60 | (goto-char (point-min)) 61 | (if (re-search-forward "duration.+\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)" 62 | (point-max) t) 63 | (let ((hours (string-to-number (match-string-no-properties 1))) 64 | (minutes (string-to-number (match-string-no-properties 2))) 65 | (seconds (string-to-number (match-string-no-properties 3)))) 66 | (+ (* hours 60 60) 67 | (* minutes 60) 68 | seconds)) 69 | 0))) 70 | 71 | (defun emms-info-exiftool (track) 72 | "Set TRACK info using exiftool." 73 | (when (eq (emms-track-type track) 'file) 74 | (with-temp-buffer 75 | (when (zerop 76 | (let ((coding-system-for-read 'utf-8)) 77 | (call-process "exiftool" nil '(t nil) nil 78 | "-json" (emms-track-name track)))) 79 | (goto-char (point-min)) 80 | (condition-case nil 81 | (let ((json-fields (elt (json-read) 0))) 82 | (mapc 83 | (lambda (field-map) 84 | (let ((emms-field (car field-map)) 85 | (exiftool-field (cdr field-map))) 86 | (let ((track-field (assoc exiftool-field json-fields))) 87 | (when track-field 88 | (emms-track-set 89 | track 90 | emms-field 91 | (cond ((eq emms-field 'info-playing-time) 92 | (emms-info-exiftool-time)) 93 | ((memq emms-field '(info-album 94 | info-artist 95 | info-albumartist 96 | info-composer 97 | info-note 98 | info-tracknumber 99 | info-title 100 | info-year 101 | info-discnumber)) 102 | (format "%s" (cdr track-field))) 103 | (t (cdr track-field)))))))) 104 | emms-info-exiftool-field-map)) 105 | (error (message "error while reading track info"))) 106 | track)))) 107 | 108 | 109 | (provide 'emms-info-exiftool) 110 | 111 | ;;; emms-info-exiftool.el ends here 112 | -------------------------------------------------------------------------------- /emms-info-libtag.el: -------------------------------------------------------------------------------- 1 | ;;; emms-info-libtag.el --- Info-method for EMMS using libtag -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2003-2021 Free Software Foundation, Inc. 4 | 5 | ;; Authors: Ulrik Jensen 6 | ;; Jorgen Schäfer 7 | ;; Keywords: 8 | 9 | ;; This file is part of EMMS. 10 | 11 | ;; EMMS is free software; you can redistribute it and/or modify 12 | ;; it under the terms of the GNU General Public License as published by 13 | ;; the Free Software Foundation; either version 3, or (at your option) 14 | ;; any later version. 15 | 16 | ;; EMMS is distributed in the hope that it will be useful, 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;; GNU General Public License for more details. 20 | 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with EMMS; see the file COPYING. If not, write to the 23 | ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24 | ;; Boston, MA 02110-1301, USA. 25 | 26 | ;;; Commentary: 27 | 28 | ;; This code has been adapted from code found in mp3player.el, written 29 | ;; by Jean-Philippe Theberge (jphiltheberge@videotron.ca), Mario 30 | ;; Domgoergen (kanaldrache@gmx.de) and Jorgen Schäfer 31 | ;; 32 | 33 | ;; To activate this method for getting info, use something like: 34 | 35 | ;; (require 'emms-info-libtag) 36 | ;; (add-hook 'emms-info-functions 'emms-info-libtag) 37 | 38 | ;; Note that you should remove emms-info-mp3info and emms-info-ogginfo 39 | ;; from the emms-info-functions list if you want to avoid 40 | ;; conflicts. For example, to set libtag as your exclusive info 41 | ;; provider: 42 | 43 | ;; (setq emms-info-functions '(emms-info-libtag)) 44 | 45 | ;; You may have to compile the program from source. 46 | ;; Make sure that you have libtag installed. 47 | ;; In the EMMS source directory do 48 | ;; 49 | ;; make emms-print-metadata 50 | ;; 51 | ;; and copy src/emms-print-metadata to your PATH. 52 | 53 | ;; If compilation fails and libtag is installed, you may have to 54 | ;; change the line 55 | ;; 56 | ;; #include 57 | ;; 58 | ;; to the correction location, e.g. 59 | ;; 60 | ;; #include 61 | 62 | ;;; Code: 63 | 64 | (require 'emms-info) 65 | 66 | (defgroup emms-info-libtag nil 67 | "Options for EMMS." 68 | :group 'emms-info) 69 | 70 | (defvar emms-info-libtag-coding-system 'utf-8) 71 | 72 | (defcustom emms-info-libtag-program-name "emms-print-metadata" 73 | "Name of emms-info-libtag program." 74 | :type '(string)) 75 | 76 | (defcustom emms-info-libtag-known-extensions 77 | (regexp-opt '("mp3" "mp4" "m4a" "ogg" "flac" "spx" "wma" "opus")) 78 | "Regexp of known extensions compatible with `emms-info-libtag-program-name'. 79 | 80 | Case is irrelevant." 81 | :type '(string)) 82 | 83 | (defun emms-info-libtag (track) 84 | (when (and (emms-track-file-p track) 85 | (let ((case-fold-search t)) 86 | (string-match 87 | emms-info-libtag-known-extensions 88 | (emms-track-name track)))) 89 | (with-temp-buffer 90 | (when (zerop 91 | (let ((coding-system-for-read 'utf-8)) 92 | (call-process emms-info-libtag-program-name 93 | nil '(t nil) nil 94 | (emms-track-name track)))) 95 | (goto-char (point-min)) 96 | ;; Crush the trailing whitespace 97 | (while (re-search-forward "[[:space:]]+$" nil t) 98 | (replace-match "" nil nil)) 99 | (goto-char (point-min)) 100 | (while (looking-at "^\\([^=\n]+\\)=\\(.*\\)$") 101 | (let ((name (intern-soft (match-string 1))) 102 | (value (match-string 2))) 103 | (when (> (length value) 104 | 0) 105 | (emms-track-set track 106 | name 107 | (if (eq name 'info-playing-time) 108 | (string-to-number value) 109 | value)))) 110 | (forward-line 1)))))) 111 | 112 | (provide 'emms-info-libtag) 113 | ;;; emms-info-libtag.el ends here 114 | -------------------------------------------------------------------------------- /emms-info-metaflac.el: -------------------------------------------------------------------------------- 1 | ;;; emms-info-metaflac.el --- Info-method for EMMS using metaflac -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2006-2021 Free Software Foundation, Inc. 4 | 5 | ;; Author: Matthew Kennedy 6 | ;; Keywords: 7 | 8 | ;; This file is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation; either version 2, or (at your option) 11 | ;; any later version. 12 | 13 | ;; This file is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with GNU Emacs; see the file COPYING. If not, write to the 20 | ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 21 | ;; Boston, MA 02110-1301 USA 22 | 23 | ;;; Commentary: 24 | 25 | ;; This code has been adapted from code found in emms-info-mp3info.el 26 | ;; written by Ulrik Jensen which contains the 27 | ;; following attribution: 28 | 29 | ;; This code has been adapted from code found in mp3player.el, written 30 | ;; by Jean-Philippe Theberge (jphiltheberge@videotron.ca), Mario 31 | ;; Domgoergen (kanaldrache@gmx.de) and Jorgen Schäfer 32 | ;; 33 | 34 | ;; To activate this method for getting info, use something like: 35 | 36 | ;; (require 'emms-info-metaflac) 37 | ;; (add-hook 'emms-info-methods-list 'emms-info-metaflac) 38 | 39 | ;;; Code: 40 | 41 | (require 'emms-info) 42 | 43 | (defvar emms-info-metaflac-version "0.1 $Revision: 1.10 $" 44 | "EMMS info metaflac version string.") 45 | 46 | ;; $Id: emms-info-mp3info.el,v 1.10 2005/08/12 18:01:16 xwl Exp $ 47 | 48 | (defgroup emms-info-metaflac nil 49 | "An EMMS-info method for getting/setting FLAC tags, using the 50 | external metaflac program" 51 | :group 'emms-info) 52 | 53 | (defcustom emms-info-metaflac-program-name "metaflac" 54 | "The name/path of the metaflac program." 55 | :type 'string) 56 | 57 | (defcustom emms-info-metaflac-options 58 | '("--no-utf8-convert" 59 | "--show-tag=TITLE" 60 | "--show-tag=ARTIST" 61 | "--show-tag=ALBUMARTIST" 62 | "--show-tag=ALBUM" 63 | "--show-tag=NOTE" 64 | "--show-tag=YEAR" 65 | "--show-tag=TRACKNUMBER" 66 | "--show-tag=DISCNUMBER" 67 | "--show-tag=GENRE") 68 | "The argument to pass to `emms-info-metaflac-program-name'." 69 | :type '(repeat string)) 70 | 71 | (defun emms-info-metaflac (track) 72 | "Get the FLAC tag of file TRACK, using `emms-info-metaflac-program' 73 | and return an emms-info structure representing it." 74 | (when (and (emms-track-file-p track) 75 | (string-match "\\.\\(flac\\|FLAC\\)\\'" (emms-track-name track))) 76 | (with-temp-buffer 77 | (when (zerop 78 | (apply 'call-process 79 | emms-info-metaflac-program-name 80 | nil t nil 81 | "--show-total-samples" 82 | "--show-sample-rate" 83 | (append emms-info-metaflac-options 84 | (list (emms-track-name track))))) 85 | (goto-char (point-min)) 86 | (emms-track-set track 'info-playing-time 87 | (/ (string-to-number (buffer-substring (point) (line-end-position))) 88 | (progn 89 | (forward-line 1) 90 | (string-to-number (buffer-substring (point) (line-end-position)))))) 91 | (forward-line 1) 92 | (while (looking-at "^\\([^=\n]+\\)=\\(.*\\)$") 93 | (let ((name (intern (concat "info-" (downcase (match-string 1))))) 94 | (value (match-string 2))) 95 | (when (> (length value) 96 | 0) 97 | (emms-track-set track 98 | name 99 | (if (eq name 'info-playing-time) 100 | (string-to-number value) 101 | value)))) 102 | (forward-line 1)))))) 103 | 104 | (provide 'emms-info-metaflac) 105 | 106 | ;;; emms-info-metaflac.el ends here 107 | -------------------------------------------------------------------------------- /emms-info-mp3info.el: -------------------------------------------------------------------------------- 1 | ;;; emms-info-mp3info.el --- Info-method for EMMS using mp3info -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2003-2021 Free Software Foundation, Inc. 4 | 5 | ;; Authors: Ulrik Jensen 6 | ;; Jorgen Schäfer 7 | ;; Keywords: 8 | 9 | ;; This file is part of EMMS. 10 | 11 | ;; EMMS is free software; you can redistribute it and/or modify 12 | ;; it under the terms of the GNU General Public License as published by 13 | ;; the Free Software Foundation; either version 3, or (at your option) 14 | ;; any later version. 15 | 16 | ;; EMMS is distributed in the hope that it will be useful, 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;; GNU General Public License for more details. 20 | 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with EMMS; see the file COPYING. If not, write to the 23 | ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 24 | ;; Boston, MA 02110-1301, USA. 25 | 26 | ;;; Commentary: 27 | 28 | ;; This code has been adapted from code found in mp3player.el, written 29 | ;; by Jean-Philippe Theberge (jphiltheberge@videotron.ca), Mario 30 | ;; Domgoergen (kanaldrache@gmx.de) and Jorgen Schäfer 31 | ;; 32 | 33 | ;; To activate this method for getting info, use something like: 34 | 35 | ;; (require 'emms-info-mp3info) 36 | ;; (add-to-list 'emms-info-functions 'emms-info-mp3info) 37 | 38 | ;;; Code: 39 | 40 | (require 'emms-info) 41 | 42 | (defvar emms-info-mp3info-version "0.2 $Revision: 1.10 $" 43 | "EMMS info mp3info version string.") 44 | ;; $Id: emms-info-mp3info.el,v 1.10 2005/08/12 18:01:16 xwl Exp $ 45 | 46 | (defgroup emms-info-mp3info nil 47 | "An EMMS-info method for getting/setting ID3v1 tags, using the 48 | external mp3info program" 49 | :group 'emms-info) 50 | 51 | (defcustom emms-info-mp3info-coding-system 'utf-8 52 | "Coding system used in the output of mp3info." 53 | :type 'coding-system) 54 | 55 | (defcustom emms-info-mp3info-program-name "mp3info" 56 | "The name/path of the mp3info tag program." 57 | :type 'string) 58 | 59 | (defcustom emms-info-mp3find-arguments 60 | `("-p" ,(concat "info-artist=%a\\n" 61 | "info-title=%t\\n" 62 | "info-album=%l\\n" 63 | "info-tracknumber=%n\\n" 64 | "info-year=%y\\n" 65 | "info-genre=%g\\n" 66 | "info-note=%c\\n" 67 | "info-playing-time=%S\\n")) 68 | "The argument to pass to `emms-info-mp3info-program-name'. 69 | This should be a list of info-flag=value lines." 70 | :type '(repeat string)) 71 | 72 | (defun emms-info-mp3info (track) 73 | "Add track information to TRACK. 74 | This is a useful element for `emms-info-functions'." 75 | (when (and (emms-track-file-p track) 76 | (string-match "\\.[Mm][Pp]3\\'" (emms-track-name track))) 77 | (with-temp-buffer 78 | (when (zerop 79 | (apply (if (fboundp 'emms-i18n-call-process-simple) 80 | 'emms-i18n-call-process-simple 81 | 'call-process) 82 | emms-info-mp3info-program-name 83 | nil t nil 84 | (append emms-info-mp3find-arguments 85 | (list (emms-track-name track))))) 86 | (goto-char (point-min)) 87 | (while (looking-at "^\\([^=\n]+\\)=\\(.*\\)$") 88 | (let ((name (intern (match-string 1))) 89 | (value (match-string 2))) 90 | (when (> (length value) 91 | 0) 92 | (emms-track-set track 93 | name 94 | (if (eq name 'info-playing-time) 95 | (string-to-number value) 96 | value)))) 97 | (forward-line 1)))))) 98 | 99 | (provide 'emms-info-mp3info) 100 | ;;; emms-info-mp3info.el ends here 101 | -------------------------------------------------------------------------------- /emms-info-native-opus.el: -------------------------------------------------------------------------------- 1 | ;;; emms-info-native-opus.el --- EMMS Opus info support -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2020-2023 Free Software Foundation, Inc. 4 | 5 | ;; Author: Petteri Hintsanen 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify it 10 | ;; under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; EMMS is distributed in the hope that it will be useful, but WITHOUT 15 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 17 | ;; License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; see the file COPYING. If not, write to the Free 21 | ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 | ;; MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; This file contains Opus-specific code for `emms-info-native-ogg' 27 | ;; feature. 28 | 29 | ;;; Code: 30 | 31 | (require 'emms-info-native-vorbis) 32 | (require 'bindat) 33 | 34 | (defvar bindat-raw) 35 | 36 | (defvar emms-info-native-opus--channel-count 0 37 | "Last decoded Opus channel count.") 38 | 39 | (defconst emms-info-native-opus--id-magic-pattern "OpusHead" 40 | "Opus identification header magic pattern.") 41 | 42 | (defconst emms-info-native-opus--channel-mapping-bindat-spec 43 | (if (eval-when-compile (fboundp 'bindat-type)) 44 | (bindat-type 45 | (stream-count u8) 46 | (coupled-count u8) 47 | (channel-mapping vec emms-info-native-opus--channel-count)) 48 | '((stream-count u8) 49 | (coupled-count u8) 50 | (channel-mapping vec (eval emms-info-native-opus--channel-count)))) 51 | "Opus channel mapping table specification.") 52 | 53 | (defconst emms-info-native-opus--id-header-bindat-spec 54 | (if (eval-when-compile (fboundp 'bindat-type)) 55 | (bindat-type 56 | (opus-head str 8) 57 | (_ unit (unless (equal opus-head emms-info-native-opus--id-magic-pattern) 58 | (error "Opus framing mismatch: expected `%s', got `%s'" 59 | emms-info-native-opus--id-magic-pattern 60 | opus-head))) 61 | (opus-version u8) 62 | (_ unit (unless (< opus-version 16) 63 | (error "Opus version mismatch: expected < 16, got %s" 64 | opus-version))) 65 | (channel-count u8) 66 | (_ unit (progn (setq emms-info-native-opus--channel-count channel-count) nil)) 67 | (pre-skip uintr 16) 68 | (sample-rate uintr 32) 69 | (output-gain uintr 16) 70 | (channel-mapping-family u8) 71 | (_ . (if (> channel-mapping-family 0) 72 | (type emms-info-native-opus--channel-mapping-bindat-spec) 73 | (unit nil)))) 74 | '((opus-head str 8) 75 | (eval (unless (equal last emms-info-native-opus--id-magic-pattern) 76 | (error "Opus framing mismatch: expected `%s', got `%s'" 77 | emms-info-native-opus--id-magic-pattern 78 | last))) 79 | (opus-version u8) 80 | (eval (unless (< last 16) 81 | (error "Opus version mismatch: expected < 16, got %s" 82 | last))) 83 | (channel-count u8) 84 | (eval (setq emms-info-native-opus--channel-count last)) 85 | (pre-skip u16r) 86 | (sample-rate u32r) 87 | (output-gain u16r) 88 | (channel-mapping-family u8) 89 | (union (channel-mapping-family) 90 | (0 nil) 91 | (t (struct emms-info-native-opus--channel-mapping-bindat-spec))))) 92 | "Opus identification header specification.") 93 | 94 | (defconst emms-info-native-opus--tags-magic-pattern "OpusTags" 95 | "Opus comment header magic pattern.") 96 | 97 | (defconst emms-info-native-opus--comment-header-bindat-spec 98 | (if (eval-when-compile (fboundp 'bindat-type)) 99 | (bindat-type 100 | (opus-tags str 8) 101 | (_ unit (unless (equal opus-tags emms-info-native-opus--tags-magic-pattern) 102 | (error "Opus framing mismatch: expected `%s', got `%s'" 103 | emms-info-native-opus--tags-magic-pattern 104 | opus-tags))) 105 | (vendor-length uintr 32) 106 | (_ unit (when (> vendor-length (length bindat-raw)) 107 | (error "Opus vendor length %s is too long" 108 | vendor-length))) 109 | (vendor-string str vendor-length) 110 | (user-comments-list-length uintr 32) 111 | (_ unit (when (> user-comments-list-length (length bindat-raw)) 112 | (error "Opus user comment list length %s is too long" 113 | user-comments-list-length))) 114 | (user-comments repeat user-comments-list-length 115 | type emms-info-native-vorbis--comment-field-bindat-spec)) 116 | '((opus-tags str 8) 117 | (eval (unless (equal last emms-info-native-opus--tags-magic-pattern) 118 | (error "Opus framing mismatch: expected `%s', got `%s'" 119 | emms-info-native-opus--tags-magic-pattern 120 | last))) 121 | (vendor-length u32r) 122 | (eval (when (> last (length bindat-raw)) 123 | (error "Opus vendor length %s is too long" last))) 124 | (vendor-string str (vendor-length)) 125 | (user-comments-list-length u32r) 126 | (eval (when (> last (length bindat-raw)) 127 | (error "Opus user comment list length %s is too long" 128 | last))) 129 | (user-comments repeat 130 | (user-comments-list-length) 131 | (struct emms-info-native-vorbis--comment-field-bindat-spec)))) 132 | "Opus comment header specification.") 133 | 134 | (defconst emms-info-native-opus--headers-bindat-spec 135 | (if (eval-when-compile (fboundp 'bindat-type)) 136 | (bindat-type 137 | (identification-header type emms-info-native-opus--id-header-bindat-spec) 138 | (comment-header type emms-info-native-opus--comment-header-bindat-spec)) 139 | '((identification-header struct emms-info-native-opus--id-header-bindat-spec) 140 | (comment-header struct emms-info-native-opus--comment-header-bindat-spec))) 141 | "Specification for two first Opus header packets. 142 | They are always an identification header followed by a comment 143 | header.") 144 | 145 | (provide 'emms-info-native-opus) 146 | 147 | ;;; emms-info-native-opus.el ends here 148 | -------------------------------------------------------------------------------- /emms-info-native-spc.el: -------------------------------------------------------------------------------- 1 | ;;; emms-info-native-spc.el --- EMMS info functions for SPC files -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2023 Free Software Foundation, Inc. 4 | 5 | ;; Author: Warren Wilkinson 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify it 10 | ;; under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; EMMS is distributed in the hope that it will be useful, but WITHOUT 15 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 17 | ;; License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; see the file COPYING. If not, write to the Free 21 | ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 | ;; MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; This file provides a native emms-info-method for SPC files. (well, 27 | ;; actually the ID666 tag embedded inside them). "Native" means a 28 | ;; pure Emacs Lisp implementation instead of one relying on external 29 | ;; tools or libraries. 30 | 31 | ;;; Code: 32 | 33 | (require 'bindat) 34 | 35 | (defconst emms-info-native-spc--id666-magic-pattern 36 | "SNES-SPC700 Sound File Data v0.30" 37 | "ID666 header magic pattern.") 38 | 39 | (defconst emms-info-native-spc--id666-header-bindat-spec 40 | (if (eval-when-compile (fboundp 'bindat-type)) 41 | (bindat-type 42 | (file-identifier str 33) 43 | (_ unit (unless (equal file-identifier 44 | emms-info-native-spc--id666-magic-pattern) 45 | (error "ID666 framing mismatch: expected `%s', got `%s'" 46 | emms-info-native-spc--id666-magic-pattern 47 | file-identifier))) 48 | (unused uint 16) 49 | (has-id666 u8) 50 | (revision u8) 51 | (pc-reg uint 16) 52 | (a-reg u8) 53 | (x-reg u8) 54 | (y-reg u8) 55 | (psw-reg u8) 56 | (sp-reg u8) 57 | (res-reg uint 16) 58 | (song-title strz 32) 59 | (game-title strz 32) 60 | (dumper strz 16) 61 | (comment strz 32) 62 | (date strz 11) 63 | (fadeout vec 3) 64 | (fadeout-length vec 5) 65 | (artist strz 32)) 66 | '((file-identifier str 33) 67 | (eval (unless (equal last emms-info-native-spc--id666-magic-pattern) 68 | (error "ID666 framing mismatch: expected `%s', got `%s'" 69 | emms-info-native-spc--id666-magic-pattern 70 | last))) 71 | (unused u16) 72 | (has-id666 u8) 73 | (revision u8) 74 | (pc-reg u16) 75 | (a-reg u8) 76 | (x-reg u8) 77 | (y-reg u8) 78 | (psw-reg u8) 79 | (sp-reg u8) 80 | (res-reg u16) 81 | (song-title strz 32) 82 | (game-title strz 32) 83 | (dumper strz 16) 84 | (comment strz 32) 85 | (date strz 11) 86 | (fadeout vec 3) 87 | (fadeout-length vec 5) 88 | (artist strz 32))) 89 | "ID666 header specification. 90 | 91 | Sources: 92 | 93 | - URL `https://ocremix.org/info/SPC_Format_Specification' 94 | - URL `https://picard-docs.musicbrainz.org/en/appendices/tag_mapping.html'") 95 | 96 | (defun emms-info-native-spc--decode-id666-header (filename) 97 | "Read and decode ID666 header from FILENAME." 98 | (with-temp-buffer 99 | (set-buffer-multibyte nil) 100 | (insert-file-contents-literally filename nil 0 210) 101 | (bindat-unpack emms-info-native-spc--id666-header-bindat-spec 102 | (buffer-string)))) 103 | 104 | (defun emms-info-native-spc-decode-id666 (filename) 105 | "Read and decode ID666 metadata from FILENAME. 106 | Return metadata in a list of (FIELD . VALUE) cons cells, or nil 107 | in case of errors or if there were no known fields in FILENAME." 108 | (condition-case nil 109 | (let ((header (emms-info-native-spc--decode-id666-header filename))) 110 | (when (= 26 (bindat-get-field header 'has-id666)) 111 | (list 112 | (cons 'info-title (bindat-get-field header 'song-title)) 113 | (cons 'info-album (bindat-get-field header 'game-title)) 114 | (cons 'info-artist (bindat-get-field header 'artist)) 115 | (cons 'info-composer (bindat-get-field header 'artist)) 116 | (cons 'info-note (bindat-get-field header 'comment))))) 117 | (error nil))) 118 | 119 | (provide 'emms-info-native-spc) 120 | 121 | ;;; emms-info-native-spc.el ends here 122 | -------------------------------------------------------------------------------- /emms-info-native.el: -------------------------------------------------------------------------------- 1 | ;;; emms-info-native.el --- Native Emacs Lisp info method for EMMS -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2020-2023 Free Software Foundation, Inc. 4 | 5 | ;; Author: Petteri Hintsanen 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify it 10 | ;; under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; EMMS is distributed in the hope that it will be useful, but WITHOUT 15 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 17 | ;; License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; see the file COPYING. If not, write to the Free 21 | ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 | ;; MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; This file provides a native emms-info-method for EMMS. Here 27 | ;; "native" means a pure Emacs Lisp implementation instead of one 28 | ;; relying on external tools or libraries like 29 | ;; `emms-info-native-ogginfo' or `emms-info-libtag'. 30 | ;; 31 | ;; To use this method, add `emms-info-native' to 32 | ;; `emms-info-functions'. 33 | ;; 34 | ;; The following file formats are supported: 35 | ;; 36 | ;; - Vorbis: Ogg Vorbis I Profile, filename extension `.ogg', 37 | ;; elementary streams only. 38 | ;; 39 | ;; - Opus: Ogg Opus profile, filename extension `.opus', elementary 40 | ;; streams only. 41 | ;; 42 | ;; - FLAC streams in native encapsulation format, filename extension 43 | ;; `.flac'. 44 | ;; 45 | ;; - MP3 files with extension `.mp3' and ID3v2 tags. All ID3v2 46 | ;; versions should work, but many features like compression and 47 | ;; encryption are not supported. 48 | ;; 49 | ;; - SPC files with extension `.spc' and ID666 tags. This is an audio 50 | ;; file based on a memory dump from an SPC700, a special audio chip 51 | ;; found within Super Nintendos. 52 | ;; 53 | ;; Format detection is based solely on filename extension, which is 54 | ;; matched case-insensitively. 55 | 56 | ;;; Code: 57 | 58 | (require 'emms-info) 59 | (require 'emms-info-native-flac) 60 | (require 'emms-info-native-ogg) 61 | (require 'emms-info-native-mp3) 62 | (require 'emms-info-native-spc) 63 | 64 | (defun emms-info-native (track) 65 | "Set info fields for TRACK. 66 | Supports Ogg Vorbis/Opus, FLAC, MP3 and SPC files." 67 | (condition-case env 68 | (let* ((filename 69 | (emms-track-name track)) 70 | (info-fields 71 | (emms-info-native--decode-info-fields filename))) 72 | (dolist (field info-fields) 73 | (let ((name (intern (concat "info-" (car field)))) 74 | (value (cdr field))) 75 | (when (stringp value) 76 | (setq value (string-trim-right value))) 77 | (emms-track-set track name value)))) 78 | (error (message "emms-info-native error processing %s: %s" 79 | (emms-track-name track) env)))) 80 | 81 | (defun emms-info-native--decode-info-fields (filename) 82 | "Decode info fields from FILENAME. 83 | Return a list of (FIELD . VALUE) cons cells, where FIELD is an 84 | info field and VALUE is the corresponding info value. Both are 85 | strings." 86 | (let ((stream-type (emms-info-native--find-stream-type filename))) 87 | (cond ((or (eq stream-type 'vorbis) (eq stream-type 'opus)) 88 | (emms-info-native-ogg-decode-metadata filename stream-type)) 89 | ((eq stream-type 'flac) 90 | (emms-info-native-flac-decode-metadata filename)) 91 | ((eq stream-type 'mp3) 92 | (emms-info-native-mp3-decode-metadata filename)) 93 | ((eq stream-type 'spc) 94 | (emms-info-native-spc-decode-id666 filename)) 95 | (t nil)))) 96 | 97 | (defun emms-info-native--find-stream-type (filename) 98 | "Deduce the stream type from FILENAME. 99 | This is a naive implementation that relies solely on filename 100 | extension. 101 | 102 | Return one of `vorbis', `opus', `flac', `mp3' or `spc', or nil if 103 | the stream type cannot be deduced." 104 | (let ((case-fold-search t)) 105 | (cond ((string-match ".ogg$" filename) 'vorbis) 106 | ((string-match ".opus$" filename) 'opus) 107 | ((string-match ".flac$" filename) 'flac) 108 | ((string-match ".mp3$" filename) 'mp3) 109 | ((string-match ".spc$" filename) 'spc) 110 | (t nil)))) 111 | 112 | (provide 'emms-info-native) 113 | 114 | ;;; emms-info-native.el ends here 115 | -------------------------------------------------------------------------------- /emms-info-ogginfo.el: -------------------------------------------------------------------------------- 1 | ;;; emms-info-ogginfo.el --- Emms information from Ogg Vorbis files. -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2005-2021 Free Software Foundation, Inc. 4 | 5 | ;; Author: Jorgen Schaefer 6 | ;; Yoni Rabkin 7 | 8 | ;; This file is part of EMMS. 9 | 10 | ;; EMMS is free software; you can redistribute it and/or modify it 11 | ;; under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation; either version 3 of the License, or 13 | ;; (at your option) any later version. 14 | 15 | ;; EMMS is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with EMMS; if not, write to the Free Software Foundation, 22 | ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | ;; 26 | 27 | ;;; Code: 28 | 29 | (require 'emms-info) 30 | 31 | (defgroup emms-info-ogginfo nil 32 | "An EMMS-info method for getting, using the external ogginfo 33 | program" 34 | :group 'emms-info) 35 | 36 | (defcustom emms-info-ogginfo-coding-system 'utf-8 37 | "Coding system used in the output of ogginfo." 38 | :type 'coding-system) 39 | 40 | (defcustom emms-info-ogginfo-program-name "ogginfo" 41 | "The name/path of the ogginfo tag program." 42 | :type 'string) 43 | 44 | (defun emms-info-ogginfo (track) 45 | "Add track information to TRACK. 46 | This is a useful element for `emms-info-functions'." 47 | (when (and (emms-track-file-p track) 48 | (string-match "\\.[Oo][Gg][Gg]\\'" (emms-track-name track))) 49 | 50 | (with-temp-buffer 51 | (call-process emms-info-ogginfo-program-name 52 | nil t nil (emms-track-name track)) 53 | 54 | ;; play time, emms-info-ogg.el [U. Jensen] 55 | (goto-char (point-min)) 56 | (when (re-search-forward 57 | "Playback length: \\([0-9]*\\)m:\\([0-9]*\\)" nil t) 58 | (let* ((minutes (string-to-number (match-string 1))) 59 | (seconds (string-to-number (match-string 2))) 60 | (ptime-total (+ (* minutes 60) seconds)) 61 | (ptime-min minutes) 62 | (ptime-sec seconds)) 63 | (emms-track-set track 'info-playing-time ptime-total) 64 | (emms-track-set track 'info-playing-time-min ptime-min) 65 | (emms-track-set track 'info-playing-time-sec ptime-sec) 66 | (emms-track-set track 'info-file (emms-track-name track)))) 67 | 68 | ;; all the rest of the info available 69 | (goto-char (point-min)) 70 | (when (re-search-forward "^.*\\.\\.\\.$" (point-max) t) 71 | (while (zerop (forward-line 1)) 72 | (when (looking-at "^\t\\(.*?\\)=\\(.*\\)$") ; recognize the first '=' 73 | (let ((a (match-string 1)) 74 | (b (match-string 2))) 75 | (when (and (< 0 (length a)) 76 | (< 0 (length b) 1024)) 77 | (emms-track-set track 78 | (intern (downcase (concat "info-" (match-string 1)))) 79 | (match-string 2)))))))))) 80 | 81 | (provide 'emms-info-ogginfo) 82 | 83 | ;;; emms-info-ogginfo.el ends here 84 | -------------------------------------------------------------------------------- /emms-info-opusinfo.el: -------------------------------------------------------------------------------- 1 | ;;; emms-info-opusinfo.el --- Emms information from Ogg Opus files. -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2018-2021 Free Software Foundation, Inc. 4 | 5 | ;; Author: Pierre Neidhardt 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify it 10 | ;; under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3 of the License, or 12 | ;; (at your option) any later version. 13 | 14 | ;; EMMS is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; if not, write to the Free Software Foundation, 21 | ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | ;;; Commentary: 24 | ;; 25 | 26 | ;;; Code: 27 | 28 | (require 'emms-info) 29 | 30 | (defgroup emms-info-opusinfo nil 31 | "An EMMS-info method for getting, using the external opusinfo 32 | program" 33 | :group 'emms-info) 34 | 35 | (defcustom emms-info-opusinfo-coding-system 'utf-8 36 | "Coding system used in the output of opusinfo." 37 | :type 'coding-system) 38 | 39 | (defcustom emms-info-opusinfo-program-name "opusinfo" 40 | "The name/path of the opusinfo tag program." 41 | :type 'string) 42 | 43 | (defun emms-info-opusinfo (track) 44 | "Add track information to TRACK. 45 | This is a useful element for `emms-info-functions'." 46 | (when (and (emms-track-file-p track) 47 | (or (string-match "\\.[Oo][Gg][Gg]\\'" (emms-track-name track)) 48 | (string-match "\\.[Oo][Pp][Uu][Ss]\\'" (emms-track-name track)))) 49 | 50 | (with-temp-buffer 51 | (call-process emms-info-opusinfo-program-name 52 | nil t nil (emms-track-name track)) 53 | 54 | ;; play time 55 | (goto-char (point-min)) 56 | (when (re-search-forward 57 | "Playback length: \\([0-9]*\\)m:\\([0-9]*\\)" nil t) 58 | (let* ((minutes (string-to-number (match-string 1))) 59 | (seconds (string-to-number (match-string 2))) 60 | (ptime-total (+ (* minutes 60) seconds)) 61 | (ptime-min minutes) 62 | (ptime-sec seconds)) 63 | (emms-track-set track 'info-playing-time ptime-total) 64 | (emms-track-set track 'info-playing-time-min ptime-min) 65 | (emms-track-set track 'info-playing-time-sec ptime-sec) 66 | (emms-track-set track 'info-file (emms-track-name track)))) 67 | 68 | ;; all the rest of the info available 69 | (goto-char (point-min)) 70 | (when (re-search-forward "^.*\\.\\.\\.$" (point-max) t) 71 | (while (zerop (forward-line 1)) 72 | (when (looking-at "^\t\\(.*?\\)=\\(.*\\)$") ; recognize the first '=' 73 | (let ((a (match-string 1)) 74 | (b (match-string 2))) 75 | (when (and (< 0 (length a)) 76 | (< 0 (length b))) 77 | (emms-track-set track 78 | (intern (downcase (concat "info-" (match-string 1)))) 79 | (match-string 2)))))))))) 80 | 81 | (provide 'emms-info-opusinfo) 82 | 83 | ;;; emms-info-opusinfo.el ends here 84 | -------------------------------------------------------------------------------- /emms-info-tinytag.el: -------------------------------------------------------------------------------- 1 | ;;; emms-info-tinytag.el --- Info-method for EMMS using tinytag -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2020-2021 Free Software Foundation, Inc. 4 | 5 | ;; Author: Fran Burstall 6 | ;; Keywords: multimedia 7 | 8 | ;; EMMS is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; EMMS is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with EMMS; see the file COPYING.. If not, see 20 | ;; . 21 | 22 | ;;; Commentary: 23 | 24 | ;; This code has been adapted from code found in emms-info-libtag.el, 25 | ;; written by Ulrik Jensen and Jorgen Schäfer 26 | ;; . 27 | 28 | ;; To activate this method for getting info, use: 29 | 30 | ;; (require 'emms-info-tinytag) 31 | ;; (add-to-list 'emms-info-functions 'emms-info-tinytag) 32 | 33 | ;; Note that you should remove emms-info-mp3info and emms-info-ogginfo 34 | ;; from the emms-info-functions list if you want to avoid 35 | ;; conflicts. For example, to set tinytag as your exclusive info 36 | ;; provider: 37 | 38 | ;; (setq emms-info-functions '(emms-info-tinytag)) 39 | 40 | ;; To use this provider, you need to install the tinytag python module 41 | ;; with something like: 42 | 43 | ;; pip install tinytag 44 | 45 | ;;; Code: 46 | 47 | (require 'emms-info) 48 | (require 'json) ;see below 49 | 50 | (defgroup emms-info-tinytag nil 51 | "Options for EMMS." 52 | :group 'emms-info) 53 | 54 | (defvar emms-info-tinytag-coding-system 'utf-8) ;is this used anywhere? 55 | 56 | (defcustom emms-info-tinytag-python-name "python" 57 | "Name of python we use." 58 | :type '(string)) 59 | 60 | (defcustom emms-info-tinytag-known-extensions 61 | (regexp-opt '("mp3" "mp4" "m4a" "m4b" "ogg" "opus" "flac" "wma" "wav")) 62 | "Regexp of known extensions that `emms-info-tinytag' can handle. 63 | 64 | Case is irrelevant." 65 | :type '(string)) 66 | 67 | (defvar emms-info-tinytag--info-fields 68 | '((info-album . album) 69 | (info-albumartist . albumartist) 70 | (info-artist . artist) 71 | (info-composer . composer) 72 | (info-year . year) 73 | (info-discnumber . disc) 74 | (info-genre . genre) 75 | (info-note . comment) 76 | (info-playing-time . duration) 77 | (info-title . title) 78 | (info-tracknumber . track)) 79 | "An alist mapping info-* fields to tinytag fields.") 80 | 81 | (defun emms-info-tinytag (track) 82 | "Set tags for TRACK using tinytag." 83 | (when (and (emms-track-file-p track) 84 | (let ((case-fold-search t)) 85 | (string-match 86 | emms-info-tinytag-known-extensions 87 | (emms-track-name track)))) 88 | (with-temp-buffer 89 | (when (zerop 90 | (let ((coding-system-for-read 'utf-8)) 91 | (call-process emms-info-tinytag-python-name 92 | nil '(t nil) nil 93 | "-m" "tinytag" (emms-track-name track)))) 94 | (goto-char (point-min)) 95 | ;; tinytag can output json or [ct]sv. Sadly, in the latter 96 | ;; case, null values are unhelpfully represented by the string 97 | ;; "None" so we parse the json. 98 | (let ((track-info (json-read))) 99 | (dolist (field emms-info-tinytag--info-fields) 100 | (let ((name (car field)) 101 | (value (alist-get (cdr field) track-info))) 102 | (when (and value (or (numberp value) (> (length value) 103 | 0))) 104 | (emms-track-set track 105 | name 106 | (cond ((eq name 'info-playing-time) 107 | (round value)) 108 | ;; for m4a, disc or track is an int: issue raised upstream 109 | ((and (or (eq name 'info-discnumber) 110 | (eq name 'info-tracknumber)) 111 | (numberp value)) 112 | (number-to-string value)) 113 | (t value))))))))))) 114 | 115 | (provide 'emms-info-tinytag) 116 | 117 | ;;; emms-info-tinytag.el ends here 118 | -------------------------------------------------------------------------------- /emms-info.el: -------------------------------------------------------------------------------- 1 | ;;; emms-info.el --- Retrieving track information -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2005-2024 Free Software Foundation, Inc. 4 | 5 | ;; Author: Jorgen Schaefer 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or 10 | ;; modify it under the terms of the GNU General Public License 11 | ;; as published by the Free Software Foundation; either version 3 12 | ;; of the License, or (at your option) any later version. 13 | 14 | ;; EMMS is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; if not, write to the Free Software 21 | ;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 22 | ;; 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; This EMMS module provides a way to add information for a track. 27 | ;; This can use an ID3 or OGG comment like syntax. 28 | 29 | ;; The code will add info symbols to the track. The following symbols 30 | ;; are defined: 31 | 32 | ;; info-artist - string naming the artist 33 | ;; info-composer - string naming the composer 34 | ;; info-performer - string naming the performer 35 | ;; info-title - string naming the title of the song 36 | ;; info-album - string naming the album 37 | ;; info-tracknumber - string(?) naming the track number 38 | ;; info-discnumber - string naming the disc number 39 | ;; info-year - string naming the year 40 | ;; info-note - string of free-form entry 41 | ;; info-genre - string naming the genre 42 | ;; info-playing-time - number giving the seconds of playtime 43 | 44 | ;;; Code: 45 | 46 | (require 'emms) 47 | (require 'emms-later-do) 48 | 49 | (defgroup emms-info nil 50 | "*Track information. ID3, OGG, etc." 51 | :group 'emms) 52 | 53 | (defcustom emms-info-auto-update t 54 | "Non-nil when EMMS should update track information if the file changes. 55 | This will cause hard drive activity on track loading. If this is 56 | too annoying for you, set this variable to nil." 57 | :type 'boolean) 58 | 59 | (defcustom emms-info-asynchronously t 60 | "Non-nil when track information should be loaded asynchronously." 61 | :type 'boolean) 62 | 63 | (defcustom emms-info-report-each-num-tracks 200 64 | "Non-zero will report progress information every number of tracks. 65 | The default is to display a message every 200 tracks. 66 | This variable is only used when adding tracks asynchronously." 67 | :type 'integer) 68 | 69 | (defcustom emms-info-functions nil 70 | "Functions which add information to tracks. 71 | Each is called with a track as argument." 72 | :type 'hook) 73 | 74 | (defcustom emms-info-init-done-hook nil 75 | "Called after asynchronously initializing all tracks." 76 | :type 'hook) 77 | 78 | (defvar emms-info-asynchronous-tracks 0 79 | "Number of tracks we're waiting for to be done.") 80 | 81 | (defun emms-info-initialize-track (track &optional force) 82 | "Initialize TRACK with emms-info information. 83 | Update TRACK information if it is new or has been modified since 84 | last update, or if FORCE is non-nil. 85 | 86 | This is a suitable value for `emms-track-initialize-functions'." 87 | (if (not emms-info-asynchronously) 88 | (emms-info-really-initialize-track track force) 89 | (setq emms-info-asynchronous-tracks (1+ emms-info-asynchronous-tracks)) 90 | (emms-later-do 'emms-info-really-initialize-track track force))) 91 | 92 | (defun emms-info-really-initialize-track (track &optional force) 93 | "Really initialize TRACK. 94 | Return t when the track got changed." 95 | (let ((file-mtime (when emms-info-auto-update 96 | (emms-info-track-file-mtime track))) 97 | (info-mtime (emms-track-get track 'info-mtime))) 98 | 99 | ;; if the file's been modified or is new 100 | (when (or (not file-mtime) 101 | (not info-mtime) 102 | (emms-time-less-p info-mtime file-mtime) 103 | force) 104 | (run-hook-with-args 'emms-info-functions track) 105 | ;; not set by info functions 106 | (when file-mtime 107 | (emms-track-set track 'info-mtime file-mtime)) 108 | (emms-track-updated track)) 109 | 110 | (when emms-info-asynchronously 111 | (setq emms-info-asynchronous-tracks (1- emms-info-asynchronous-tracks)) 112 | (if (zerop emms-info-asynchronous-tracks) 113 | (progn 114 | (run-hook-with-args 'emms-info-init-done-hook) 115 | (message "EMMS: All track information loaded.")) 116 | (unless (zerop emms-info-report-each-num-tracks) 117 | (if (zerop 118 | (mod emms-info-asynchronous-tracks 119 | emms-info-report-each-num-tracks)) 120 | (message "EMMS: %d tracks to go.." 121 | emms-info-asynchronous-tracks))))))) 122 | 123 | (defun emms-info-track-file-mtime (track) 124 | "Return the mtime of the file of TRACK, if any. 125 | Return nil otherwise." 126 | (if (eq (emms-track-type track) 127 | 'file) 128 | (nth 5 (file-attributes (emms-track-name track))) 129 | nil)) 130 | 131 | (defun emms-info-track-description (track) 132 | "Return a description of TRACK." 133 | (let ((artist (emms-track-get track 'info-artist)) 134 | (album (emms-track-get track 'info-album)) 135 | (composer (emms-track-get track 'info-composer)) 136 | (title (emms-track-get track 'info-title))) 137 | (cond 138 | ((and composer artist title album) 139 | (concat (if (string= composer artist) 140 | artist 141 | (concat artist " - " composer)) 142 | " - " album " - " title)) 143 | ((and artist title album) 144 | (concat artist " - " album " - " title)) 145 | ((and artist title) 146 | (concat artist " - " title)) 147 | (title 148 | title) 149 | (t 150 | (emms-track-simple-description track))))) 151 | 152 | (provide 'emms-info) 153 | ;;; emms-info.el ends here 154 | -------------------------------------------------------------------------------- /emms-last-played.el: -------------------------------------------------------------------------------- 1 | ;;; emms-last-played.el --- Support for last-played-time of a track -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2006-2021 Free Software Foundation, Inc. 4 | 5 | ;; Author: Lucas Bonnet 6 | ;; Keywords: emms, mp3, mpeg, multimedia 7 | 8 | ;; This file is part of EMMS. 9 | 10 | ;; EMMS is free software; you can redistribute it and/or modify 11 | ;; it under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation; either version 3, or (at your option) 13 | ;; any later version. 14 | 15 | ;; EMMS is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with EMMS; see the file COPYING. If not, write to the 22 | ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 23 | ;; Boston, MA 02110-1301, USA. 24 | 25 | ;;; Commentary: 26 | 27 | ;; Records when the track was last played. 28 | ;; Big portions of the time handling fuctions are copied from 29 | ;; gnus-util.el, and slightly adapted. 30 | 31 | ;;; Code: 32 | 33 | (require 'emms) 34 | 35 | (defvar emms-last-played-keep-count t 36 | "Specifies if EMMS should record the number of times you play a track. 37 | Set it to t if you want such a feature, and to nil if you don't.") 38 | 39 | (defvar emms-last-played-format-alist 40 | '(((emms-last-played-seconds-today) . "%k:%M") 41 | (604800 . "%a %k:%M") ;;that's one week 42 | ((emms-last-played-seconds-month) . "%a %d") 43 | ((emms-last-played-seconds-year) . "%b %d") 44 | (t . "%b %d '%y")) ;;this one is used when no 45 | ;;other does match 46 | "Specifies date format depending on when a track was last played. 47 | This is an alist of items (AGE . FORMAT). AGE can be a number (of 48 | seconds) or a Lisp expression evaluating to a number. When the age of 49 | the track is less than this number, then use `format-time-string' 50 | with the corresponding FORMAT for displaying the date of the track. 51 | If AGE is not a number or a Lisp expression evaluating to a 52 | non-number, then the corresponding FORMAT is used as a default value. 53 | 54 | Note that the list is processed from the beginning, so it should be 55 | sorted by ascending AGE. Also note that items following the first 56 | non-number AGE will be ignored. 57 | 58 | You can use the functions `emms-last-played-seconds-today', 59 | `emms-last-played-seconds-month' and 60 | `emms-last-played-seconds-year' in the AGE spec. They return the 61 | number of seconds passed since the start of today, of this month, 62 | of this year, respectively.") 63 | 64 | 65 | (defun emms-last-played-update-track (track) 66 | "Updates the last-played time of TRACK." 67 | (emms-track-set track 'last-played (current-time))) 68 | 69 | (defun emms-last-played-increment-count (track) 70 | "Increments the play-count property of TRACK. 71 | If non-existent, it is set to 1." 72 | (let ((play-count (emms-track-get track 'play-count))) 73 | (if play-count 74 | (emms-track-set track 'play-count (1+ play-count)) 75 | (emms-track-set track 'play-count 1)))) 76 | 77 | (defun emms-last-played-update-current () 78 | "Updates the current track." 79 | (emms-last-played-update-track (emms-playlist-current-selected-track)) 80 | (if emms-last-played-keep-count 81 | (emms-last-played-increment-count (emms-playlist-current-selected-track)))) 82 | 83 | (defun emms-last-played-seconds-today () 84 | "Return the number of seconds passed today." 85 | (let ((now (decode-time (current-time)))) 86 | (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600)))) 87 | 88 | (defun emms-last-played-seconds-month () 89 | "Return the number of seconds passed this month." 90 | (let ((now (decode-time (current-time)))) 91 | (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600) 92 | (* (- (car (nthcdr 3 now)) 1) 3600 24)))) 93 | 94 | (defun emms-last-played-seconds-year () 95 | "Return the number of seconds passed this year." 96 | (let ((now (decode-time (current-time))) 97 | (days (format-time-string "%j" (current-time)))) 98 | (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600) 99 | (* (- (string-to-number days) 1) 3600 24)))) 100 | 101 | (defun emms-last-played-format-date (messy-date) 102 | "Format the messy-date according to `emms-last-played-format-alist'. 103 | Returns \" ? \" if there's bad input or if an other error occurs. 104 | Input should look like this: \"Sun, 14 Oct 2001 13:34:39 +0200\"." 105 | (condition-case () 106 | (let* ((messy-date (float-time messy-date)) 107 | (now (float-time (current-time))) 108 | ;;If we don't find something suitable we'll use this one 109 | (my-format "%b %d '%y")) 110 | (let* ((difference (- now messy-date)) 111 | (templist emms-last-played-format-alist) 112 | (top (eval (caar templist) t))) 113 | (while (if (numberp top) (< top difference) (not top)) 114 | (progn 115 | (setq templist (cdr templist)) 116 | (setq top (eval (caar templist) t)))) 117 | (if (stringp (cdr (car templist))) 118 | (setq my-format (cdr (car templist))))) 119 | (format-time-string (eval my-format t) (seconds-to-time messy-date))) 120 | (error "Never."))) 121 | 122 | (provide 'emms-last-played) 123 | ;;; emms-last-played.el ends here 124 | -------------------------------------------------------------------------------- /emms-later-do.el: -------------------------------------------------------------------------------- 1 | ;;; emms-later-do.el --- Execute Lisp code ... later -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2004-2021 Free Software Foundation, Inc. 4 | 5 | ;; Author: Jorgen Schaefer 6 | 7 | ;; This program is free software; you can redistribute it and/or 8 | ;; modify it under the terms of the GNU General Public License 9 | ;; as published by the Free Software Foundation; either version 3 10 | ;; of the License, or (at your option) any later version. 11 | 12 | ;; This program is distributed in the hope that it will be useful, 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ;; GNU General Public License for more details. 16 | 17 | ;; You should have received a copy of the GNU General Public License 18 | ;; along with this program; if not, write to the Free Software 19 | ;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 | ;; 02110-1301 USA 21 | 22 | ;;; Commentary 23 | 24 | ;; This file will execute lisp code ``later on''. This way it is 25 | ;; possible to work while elisp does some longer calculations, if you 26 | ;; can convert those calculations into a sequence of function calls. 27 | 28 | ;; 2020-09-22: Name changed from later-do to emms-later-do in order to 29 | ;; avoid polluting the namespace. 30 | 31 | ;;; Code: 32 | 33 | (defvar emms-later-do-version "0.2emms4 (2018-04-07)" 34 | "Version string of emms-later-do.") 35 | 36 | (defgroup emms-later-do nil 37 | "*Running functions ... later!" 38 | :prefix "emms-later-do-" 39 | :group 'development) 40 | 41 | (defcustom emms-later-do-interval 0.5 42 | "How many seconds to wait between running events." 43 | :type 'number) 44 | 45 | (defcustom emms-later-do-batch 20 46 | "How many functions to process before waiting `emms-later-do-interval'. 47 | The functions are processed from `emms-later-do-list'. Must be 1 or 48 | greater. Too high a value might make Emacs slower while the 49 | list is being processed." 50 | :type 'number) 51 | 52 | (defvar emms-later-do-list nil 53 | "A list of functions to be called later on.") 54 | 55 | (defvar emms-later-do-timer nil 56 | "The timer that emms-later-do uses.") 57 | 58 | (defun emms-later-do (function &rest args) 59 | "Apply FUNCTION to ARGS later on. This is an unspecified 60 | amount of time after this call, and definitely not while lisp is 61 | still executing. Code added using `emms-later-do' is guaranteed to be 62 | executed in the sequence it was added." 63 | (setq emms-later-do-list (nconc emms-later-do-list 64 | (list (cons function args)))) 65 | (unless emms-later-do-timer 66 | (setq emms-later-do-timer 67 | (run-with-timer emms-later-do-interval nil 'emms-later-do-timer)))) 68 | 69 | (defun emms-later-do-timer () 70 | "Run the next element in `emms-later-do-list', or do nothing if it's 71 | empty." 72 | (if (null emms-later-do-list) 73 | (setq emms-later-do-timer nil) 74 | (let (res) 75 | (unwind-protect 76 | (dotimes (_b (min emms-later-do-batch (length emms-later-do-list)) res) 77 | (let ((fun (caar emms-later-do-list)) 78 | (args (cdar emms-later-do-list))) 79 | (setq emms-later-do-list (cdr emms-later-do-list)) 80 | (setq res (apply fun args)))) 81 | (setq emms-later-do-timer (run-with-timer emms-later-do-interval 82 | nil 83 | 'emms-later-do-timer)))))) 84 | 85 | (provide 'emms-later-do) 86 | ;;; emms-later-do.el ends here 87 | -------------------------------------------------------------------------------- /emms-lyrics-lrclib.el: -------------------------------------------------------------------------------- 1 | ;;; emms-lyrics-lrclib.el --- Fetch synchronized lyrics through LRCLIB -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2024 Free Software Foundation, Inc. 4 | 5 | ;; Author: Daniel Semyonov 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify it 10 | ;; under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; EMMS is distributed in the hope that it will be useful, but WITHOUT 15 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 17 | ;; License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; see the file COPYING. If not, write to the Free 21 | ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 | ;; MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; This file provides a command/track initialization function which 27 | ;; automatically fetches synchronized lyrics for tracks (the current 28 | ;; track interactively) through an LRCLIB server. 29 | 30 | ;;; Code: 31 | 32 | (eval-when-compile 33 | (declare-function json-parse-buffer "json.c")) 34 | 35 | (require 'emms-lyrics) 36 | (require 'emms-later-do) 37 | 38 | (defgroup emms-lyrics-lrclib nil 39 | "EMMS module for fetching synchronized lyrics through LRCLIB servers." 40 | :group 'emms-lyrics 41 | :prefix "emms-lyrics-lrclib-") 42 | 43 | (defcustom emms-lyrics-lrclib-url "https://lrclib.net/api/" 44 | "Base URL for LRCLIB API requests." 45 | :type 'string) 46 | 47 | (defconst emms-lyrics-lrclib-max-requests 250 48 | "Maximum number of concurrent requests to LRCLIB.") 49 | 50 | (defvar emms-lyrics-lrclib-requests 0 51 | "Current number of concurrent requests to LRCLIB.") 52 | 53 | (defun emms-lyrics-lrclib-encode-name (name) 54 | "Encode (artist/album/track) NAME for an LRCLIB search." 55 | (and (stringp name) (string-replace " " "+" name))) 56 | 57 | (defun emms-lyrics-lrclib-parse (_ file track interactive) 58 | "Parse and save synced lyrics in FILE. 59 | If TRACK is the selected track in the current playlist, catch up. 60 | When INTERACTIVE is non-nil, display messages and confirm overwrite." 61 | (unwind-protect 62 | (let (lyrics) 63 | (search-forward "\n\n") 64 | (if-let* (((functionp 'json-available-p)) 65 | ((json-available-p)) 66 | (p (json-parse-buffer :null-object nil))) 67 | (and (hash-table-p p) (setq lyrics (gethash "syncedLyrics" p))) 68 | (when-let* ((beg (search-forward "\"syncedLyrics\":\"" nil t)) 69 | (end (1- (search-forward-regexp "[^\\]\"" nil t)))) 70 | (replace-string-in-region "\\n" "\n" beg end) 71 | (setq lyrics (buffer-substring-no-properties 72 | beg (1- (point)))))) 73 | (and lyrics interactive (file-exists-p file) 74 | (not (y-or-n-p (format "Overwrite existing file (\"%s\")?" file))) 75 | (setq lyrics nil)) 76 | (when lyrics 77 | (with-temp-file file (insert lyrics)) 78 | (when interactive (message "Saved synced lyrics at \"%s\"" file)) 79 | (and (boundp 'emms-lyrics-display-p) 80 | emms-lyrics-display-p emms-player-playing-p 81 | (equal track (emms-playlist-current-selected-track)) 82 | (emms-lyrics-catchup file)))) 83 | (setq emms-lyrics-lrclib-requests (1- emms-lyrics-lrclib-requests)))) 84 | 85 | ;;;###autoload 86 | (defun emms-lyrics-lrclib-get (&optional track force interactive) 87 | "Search for synchronized lyrics for TRACK through LRCLIB's API. 88 | If TRACK is omitted or nil, use the selected track in the current playlist. 89 | The lyrics are saved in an \".lrc\" file alongside the track, unless the 90 | file already exists (in which case the search isn't performed). 91 | When called interactively (non-nil INTERACTIVE), display informative 92 | messages, and with prefix argument FORCE, ask to overwrite existing 93 | \".lrc\" files." 94 | (interactive (list nil current-prefix-arg t)) 95 | (if (> emms-lyrics-lrclib-requests emms-lyrics-lrclib-max-requests) 96 | (emms-later-do #'emms-lyrics-lrclib-get track force interactive) 97 | (when-let* ((track (or track (emms-playlist-current-selected-track))) 98 | ((eq (emms-track-type track) 'file)) 99 | (file (emms-track-name track)) 100 | (lrc (replace-regexp-in-string "\\.[^.]+\\'" ".lrc" file)) 101 | ((or force (not (file-exists-p lrc)))) 102 | ((file-writable-p lrc)) 103 | (title (emms-lyrics-lrclib-encode-name 104 | (emms-track-get track 'info-title))) 105 | (artist (emms-lyrics-lrclib-encode-name 106 | (emms-track-get track 'info-artist))) 107 | (album (emms-lyrics-lrclib-encode-name 108 | (emms-track-get track 'info-album))) 109 | (time (emms-track-get track 'info-playing-time))) 110 | (setq emms-lyrics-lrclib-requests (1+ emms-lyrics-lrclib-requests)) 111 | (when interactive (message "Searching for lyrics...")) 112 | (url-retrieve 113 | (url-encode-url 114 | (format "%sget?artist_name=%s&track_name=%s&album_name=%s&duration=%d" 115 | emms-lyrics-lrclib-url artist title album time)) 116 | #'emms-lyrics-lrclib-parse (list lrc track interactive))))) 117 | 118 | (provide 'emms-lyrics-lrclib) 119 | 120 | ;;; emms-lyrics-lrclib.el ends here 121 | -------------------------------------------------------------------------------- /emms-maint.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | ;; Copyright (C) 2003-2020 Free Software Foundation, Inc. 3 | 4 | (add-to-list 'load-path ".") 5 | -------------------------------------------------------------------------------- /emms-mode-line-icon.el: -------------------------------------------------------------------------------- 1 | ;; emms-mode-line-icon.el --- show an icon in the Emacs mode-line -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2006-2021 Free Software Foundation, Inc. 4 | 5 | ;; Version: 1.1 6 | ;; Keywords: emms 7 | 8 | ;; Author: Daniel Brockman 9 | ;; Maintainer: Lucas Bonnet 10 | 11 | ;; This file is part of EMMS. 12 | 13 | ;; EMMS is free software; you can redistribute it and/or 14 | ;; modify it under the terms of the GNU General Public License 15 | ;; as published by the Free Software Foundation; either version 3 16 | ;; of the License, or (at your option) any later version. 17 | 18 | ;; EMMS is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; You should have received a copy of the GNU General Public License 24 | ;; along with EMMS; if not, write to the Free Software Foundation, 25 | ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 26 | 27 | ;; Commentary: 28 | 29 | ;; This EMMS extension shows an icon in the mode-line next to the 30 | ;; info-tag. 31 | 32 | ;; Code: 33 | 34 | (require 'emms-mode-line) 35 | 36 | 37 | (defvar emms-mode-line-icon-enabled-p t 38 | "Disable icon display when NIL.") 39 | 40 | (defvar emms-mode-line-icon-color "black" 41 | "Color of the little icon displayed in the mode-line.") 42 | 43 | (defvar emms-mode-line-icon-before-format "" 44 | "String to put before the icon, in the mode-line. 45 | For example, if you want to have something like : 46 | \[ Foo - The Foo Song ] 47 | You should set it to \"[\", and set emms-mode-line-format to \"%s ]\"") 48 | 49 | (defun emms-mode-line-icon-generate (color) 50 | `(image :type xpm :ascent center :data ,(concat "/* XPM */ 51 | static char *note[] = { 52 | /* width height num_colors chars_per_pixel */ 53 | \" 10 11 2 1\", 54 | /* colors */ 55 | \". c " color "\", 56 | \"# c None s None\", 57 | /* pixels */ 58 | \"###...####\", 59 | \"###.#...##\", 60 | \"###.###...\", 61 | \"###.#####.\", 62 | \"###.#####.\", 63 | \"#...#####.\", 64 | \"....#####.\", 65 | \"#..######.\", 66 | \"#######...\", 67 | \"######....\", 68 | \"#######..#\"};"))) 69 | 70 | (defun emms-mode-line-icon-function () 71 | (if emms-mode-line-icon-enabled-p 72 | (concat " " 73 | emms-mode-line-icon-before-format 74 | (emms-propertize "NP:" 'display 75 | (emms-mode-line-icon-generate 76 | emms-mode-line-icon-color)) 77 | (emms-mode-line-playlist-current)) 78 | (emms-mode-line-playlist-current))) 79 | 80 | (setq emms-mode-line-mode-line-function #'emms-mode-line-icon-function) 81 | 82 | ;; This is needed for text properties to work in the mode line. 83 | (put 'emms-mode-line-string 'risky-local-variable t) 84 | 85 | (provide 'emms-mode-line-icon) 86 | ;;; emms-mode-line-icone.el ends here 87 | -------------------------------------------------------------------------------- /emms-mode-line.el: -------------------------------------------------------------------------------- 1 | ;;; emms-mode-line.el --- Mode-Line and titlebar infos for emms -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2004-2025 Free Software Foundation, Inc. 4 | 5 | ;; Author: Mario Domgörgen 6 | ;; Keywords: multimedia 7 | 8 | ;; This file is part of EMMS. 9 | 10 | ;; EMMS is free software; you can redistribute it and/or modify 11 | ;; it under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation; either version 3, or (at your option) 13 | ;; any later version. 14 | 15 | ;; EMMS is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with EMMS; see the file COPYING. If not, write to the 22 | ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 23 | ;; Boston, MA 02110-1301, USA. 24 | 25 | ;;; Commentary: 26 | ;; 27 | ;; To activate put simply the following line in your Emacs: 28 | ;; 29 | ;; (require 'emms-mode-line) 30 | ;; (emms-mode-line 1) 31 | 32 | ;;; Code: 33 | 34 | (require 'emms) 35 | 36 | (defgroup emms-mode-line nil 37 | "Showing information on mode-line and titlebar" 38 | :prefix "emms-mode-line-" 39 | :group 'emms) 40 | 41 | (defcustom emms-mode-line-mode-line-function #'emms-mode-line-playlist-current 42 | "Function for showing infos in mode-line or nil if don't want to." 43 | :type '(choice (const :tag "Don't show info on mode-line" nil) function)) 44 | 45 | (defcustom emms-mode-line-titlebar-function nil 46 | "Function for showing infos in titlebar or nil if you don't want to." 47 | :type '(choice (const :tag "Don't show info on titlebar" nil) function)) 48 | 49 | (defcustom emms-mode-line-format " [ %s ] " 50 | "String used for displaying the current track in mode-line and titlebar." 51 | :type 'string 52 | :group 'emms) 53 | 54 | (defcustom emms-mode-line-length-limit 70 55 | "Maximum length before track information is truncated." 56 | :type 'natnum 57 | :group 'emms) 58 | 59 | 60 | (defun emms-mode-line-playlist-current () 61 | "Format the currently playing song." 62 | (let ((track-desc (emms-track-description 63 | (emms-playlist-current-selected-track)))) 64 | (format emms-mode-line-format 65 | (if (< (string-width track-desc) emms-mode-line-length-limit) 66 | track-desc 67 | (truncate-string-to-width track-desc emms-mode-line-length-limit 0 nil t))))) 68 | 69 | 70 | (define-obsolete-variable-alias 'emms-mode-line-active-p 71 | 'emms-mode-line-mode "Apr 2021") 72 | (defvar emms-mode-line-string "") 73 | 74 | (defvar emms-mode-line-initial-titlebar frame-title-format) 75 | 76 | (defun emms-mode-line (arg) 77 | (declare (obsolete emms-mode-line-mode "Apr 2021")) 78 | (emms-mode-line-mode (if (and arg (> arg 0)) 1 -1))) 79 | 80 | ;;;###autoload 81 | (define-minor-mode emms-mode-line-mode 82 | "Turn on `emms-mode-line' if ARG is positive, off otherwise." 83 | :global t 84 | (or global-mode-string (setq global-mode-string '(""))) 85 | (if emms-mode-line-mode 86 | (progn 87 | (add-hook 'emms-track-updated-functions #'emms-mode-line-alter) 88 | (add-hook 'emms-player-finished-hook #'emms-mode-line-blank) 89 | (add-hook 'emms-player-stopped-hook #'emms-mode-line-blank) 90 | (add-hook 'emms-player-started-hook #'emms-mode-line-alter) 91 | (when (and emms-mode-line-mode-line-function 92 | (not (member 'emms-mode-line-string global-mode-string))) 93 | (setq global-mode-string 94 | (append global-mode-string 95 | '(emms-mode-line-string)))) 96 | (when emms-player-playing-p (emms-mode-line-alter))) 97 | (remove-hook 'emms-track-updated-functions #'emms-mode-line-alter) 98 | (remove-hook 'emms-player-finished-hook #'emms-mode-line-blank) 99 | (remove-hook 'emms-player-stopped-hook #'emms-mode-line-blank) 100 | (remove-hook 'emms-player-started-hook #'emms-mode-line-alter) 101 | (emms-mode-line-restore-titlebar) 102 | (emms-mode-line-restore-mode-line))) 103 | 104 | ;;;###autoload 105 | (defun emms-mode-line-enable () 106 | "Turn on `emms-mode-line'." 107 | (declare (obsolete emms-mode-line-mode "Apr 2021")) 108 | (interactive) 109 | (emms-mode-line-mode 1)) 110 | 111 | ;;;###autoload 112 | (defun emms-mode-line-disable () 113 | "Turn off `emms-mode-line'." 114 | (interactive) 115 | (emms-mode-line-mode -1)) 116 | 117 | ;;;###autoload 118 | (defun emms-mode-line-toggle () 119 | "Toggle `emms-mode-line'." 120 | (declare (obsolete emms-mode-line-mode "Apr 2021")) 121 | (interactive) 122 | (emms-mode-line-mode 'toggle)) 123 | 124 | (defun emms-mode-line-alter (&optional track) 125 | "Alter mode-line/titlebar. 126 | 127 | Optional TRACK is used to be compatible with 128 | `emms-track-updated-functions'. It's simply ignored currently." 129 | (ignore track) 130 | (emms-mode-line-alter-mode-line) 131 | (emms-mode-line-alter-titlebar)) 132 | 133 | (defun emms-mode-line-alter-mode-line () 134 | "Update the mode-line with song info." 135 | (when (and emms-mode-line-mode-line-function 136 | emms-player-playing-p) 137 | (setq emms-mode-line-string 138 | (funcall emms-mode-line-mode-line-function)) 139 | (force-mode-line-update))) 140 | 141 | (defun emms-mode-line-alter-titlebar () 142 | "Update the titlebar with song info." 143 | (when emms-mode-line-titlebar-function 144 | (setq frame-title-format 145 | (list "" emms-mode-line-initial-titlebar (funcall emms-mode-line-titlebar-function))))) 146 | 147 | 148 | (defun emms-mode-line-blank () 149 | "Blank mode-line and titlebar but not quit `emms-mode-line'." 150 | (setq emms-mode-line-string nil) 151 | (force-mode-line-update) 152 | (emms-mode-line-restore-titlebar)) 153 | 154 | (defun emms-mode-line-restore-mode-line () 155 | "Restore the mode-line." 156 | (when emms-mode-line-mode-line-function 157 | (setq global-mode-string 158 | (remove 'emms-mode-line-string global-mode-string)) 159 | (force-mode-line-update))) 160 | 161 | (defun emms-mode-line-restore-titlebar () 162 | "Restore the mode-line." 163 | (when emms-mode-line-titlebar-function 164 | (setq frame-title-format 165 | (list emms-mode-line-initial-titlebar)))) 166 | 167 | (provide 'emms-mode-line) 168 | ;;; emms-mode-line.el ends here 169 | -------------------------------------------------------------------------------- /emms-player-mplayer.el: -------------------------------------------------------------------------------- 1 | ;;; emms-player-mplayer.el --- mplayer support for EMMS -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. 4 | 5 | ;; Authors: William Xu 6 | ;; Jorgen Schaefer 7 | 8 | ;; This file is part of EMMS. 9 | 10 | ;; EMMS is free software; you can redistribute it and/or 11 | ;; modify it under the terms of the GNU General Public License 12 | ;; as published by the Free Software Foundation; either version 3 13 | ;; of the License, or (at your option) any later version. 14 | 15 | ;; EMMS is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with EMMS; if not, write to the Free Software Foundation, 22 | ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; This provides a player that uses mplayer. It supports pause and 27 | ;; seeking. For loading subtitles automatically, try adding 28 | ;; "sub-fuzziness=1" to your `~/.mplayer/config', see mplayer manual for 29 | ;; more. 30 | 31 | ;;; Code: 32 | 33 | (require 'emms-compat) 34 | (require 'emms-player-simple) 35 | 36 | (define-emms-simple-player mplayer '(file url) 37 | (concat "\\`\\(http[s]?\\|mms\\)://\\|" 38 | (apply #'emms-player-simple-regexp 39 | emms-player-base-format-list)) 40 | "mplayer" "-slave" "-quiet" "-really-quiet") 41 | 42 | (define-emms-simple-player mplayer-playlist '(streamlist) 43 | "\\`http[s]?://" 44 | "mplayer" "-slave" "-quiet" "-really-quiet" "-playlist") 45 | 46 | (emms-player-set emms-player-mplayer 47 | 'pause 48 | 'emms-player-mplayer-pause) 49 | 50 | ;;; Pause is also resume for mplayer 51 | (emms-player-set emms-player-mplayer 52 | 'resume 53 | nil) 54 | 55 | (emms-player-set emms-player-mplayer 56 | 'seek 57 | 'emms-player-mplayer-seek) 58 | 59 | (emms-player-set emms-player-mplayer 60 | 'seek-to 61 | 'emms-player-mplayer-seek-to) 62 | 63 | (defun emms-player-mplayer-pause () 64 | "Depends on mplayer's -slave mode." 65 | (process-send-string 66 | emms-player-simple-process-name "pause\n")) 67 | 68 | (defun emms-player-mplayer-seek (sec) 69 | "Depends on mplayer's -slave mode." 70 | (process-send-string 71 | emms-player-simple-process-name 72 | (format "seek %d\n" sec))) 73 | 74 | (defun emms-player-mplayer-seek-to (sec) 75 | "Depends on mplayer's -slave mode." 76 | (process-send-string 77 | emms-player-simple-process-name 78 | (format "seek %d 2\n" sec))) 79 | 80 | (provide 'emms-player-mplayer) 81 | ;;; emms-player-mplayer.el ends here 82 | -------------------------------------------------------------------------------- /emms-player-vlc.el: -------------------------------------------------------------------------------- 1 | ;;; emms-player-vlc.el --- vlc support for EMMS -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2008-2021 Free Software Foundation, Inc. 4 | 5 | ;; Authors: Yoni Rabkin 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or 10 | ;; modify it under the terms of the GNU General Public License 11 | ;; as published by the Free Software Foundation; either version 3 12 | ;; of the License, or (at your option) any later version. 13 | 14 | ;; EMMS is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; if not, write to the Free Software Foundation, 21 | ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | ;;; Commentary: 24 | 25 | ;;; Code: 26 | 27 | (require 'emms-compat) 28 | (require 'emms-player-simple) 29 | 30 | ;; I use this macro, and later override some of the stuff it defines 31 | ;; in order to accomodate VLC's particular idioms. 32 | (define-emms-simple-player vlc '(file url) 33 | (concat "\\`\\(http[s]?\\|mms\\)://\\|" 34 | (apply #'emms-player-simple-regexp 35 | emms-player-base-format-list)) 36 | "vlc" "--intf=rc") 37 | 38 | (define-emms-simple-player vlc-playlist '(streamlist) 39 | "\\`http[s]?://" 40 | "vlc" "--intf=rc") 41 | 42 | ;; (kludge) By default, VLC does not quit after finishing to play a 43 | ;; track, so the player sentinel has no way of telling that the next 44 | ;; track should be played. Therefore I redefine this low-level 45 | ;; function and add a "quit" track which is invisible to Emms. 46 | (advice-add 'emms-player-vlc-start :override #'emms--vlc-quit-after-finish) 47 | (defun emms--vlc-quit-after-finish (track &rest _) 48 | (let ((process (apply #'start-process 49 | emms-player-simple-process-name 50 | nil 51 | emms-player-vlc-command-name 52 | ;; splice in params here 53 | (append emms-player-vlc-parameters 54 | (list (emms-track-name track)) 55 | '("vlc://quit"))))) 56 | ;; Add a sentinel for signaling termination. 57 | (set-process-sentinel process #'emms-player-simple-sentinel)) 58 | (emms-player-started emms-player-vlc)) 59 | 60 | (defun emms-player-vlc-pause () 61 | "Depends on vlc's rc mode." 62 | (process-send-string 63 | emms-player-simple-process-name "pause\n")) 64 | 65 | (defun emms-player-vlc-seek (sec) 66 | "Seek relative within a stream." 67 | (when (not (= 0 sec)) 68 | (process-send-string 69 | emms-player-simple-process-name 70 | (if (< 0 sec) "fastforward\n" "rewind\n")))) 71 | 72 | (defun emms-player-vlc-seek-to (sec) 73 | "Seek to time SEC within the stream." 74 | (process-send-string 75 | emms-player-simple-process-name 76 | (format "seek %d\n" sec))) 77 | 78 | (emms-player-set emms-player-vlc 'pause 'emms-player-vlc-pause) 79 | (emms-player-set emms-player-vlc 'resume nil) ; pause is also resume 80 | (emms-player-set emms-player-vlc 'start 'emms-player-vlc-start) 81 | (emms-player-set emms-player-vlc 'seek 'emms-player-vlc-seek) 82 | (emms-player-set emms-player-vlc 'seek-to 'emms-player-vlc-seek-to) 83 | 84 | (provide 'emms-player-vlc) 85 | 86 | ;;; emms-player-vlc.el ends here 87 | -------------------------------------------------------------------------------- /emms-player-xine.el: -------------------------------------------------------------------------------- 1 | ;;; emms-player-xine.el --- xine support for EMMS -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. 4 | 5 | ;; Author: Tassilo Horn 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or 10 | ;; modify it under the terms of the GNU General Public License 11 | ;; as published by the Free Software Foundation; either version 3 12 | ;; of the License, or (at your option) any later version. 13 | 14 | ;; EMMS is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; if not, write to the Free Software Foundation, 21 | ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | ;;; Commentary: 24 | 25 | ;; This provides a player that uses xine. It supports pause and 26 | ;; seeking. 27 | 28 | ;;; Code: 29 | 30 | ;; TODO: The video window cannot be disabled. I asked on 31 | ;; gmane.comp.video.xine.user (<87y7ohqcbq.fsf@baldur.tsdh.de>)... 32 | 33 | ;; TODO: Implement seek-to with "SetPositionX%\n" where X is in {0,10,..,90} 34 | 35 | (require 'emms-player-simple) 36 | 37 | (define-emms-simple-player xine '(file url) 38 | (concat "\\`\\(http[s]?\\|mms\\)://\\|" 39 | (emms-player-simple-regexp 40 | "ogg" "mp3" "wav" "mpg" "mpeg" "wmv" "wma" 41 | "mov" "avi" "divx" "ogm" "ogv" "asf" "mkv" 42 | "rm" "rmvb" "mp4" "flac" "vob")) 43 | "xine" "--no-gui" "--no-logo" "--no-splash" "--no-reload" "--stdctl") 44 | 45 | (emms-player-set emms-player-xine 46 | 'pause 47 | 'emms-player-xine-pause) 48 | 49 | ;;; Pause is also resume for xine 50 | (emms-player-set emms-player-xine 51 | 'resume 52 | nil) 53 | 54 | (emms-player-set emms-player-xine 55 | 'seek 56 | 'emms-player-xine-seek) 57 | 58 | (defun emms-player-xine-pause () 59 | "Depends on xine's --stdctl mode." 60 | (process-send-string 61 | emms-player-simple-process-name "pause\n")) 62 | 63 | (defun emms-player-xine-seek (secs) 64 | "Depends on xine's --stdctl mode." 65 | ;; xine-ui's stdctl supports only seeking forward/backward in 7/15/30 and 60 66 | ;; second steps, so we take the value that is nearest to SECS. 67 | (let ((s (emms-nearest-value secs '(-60 -30 -15 -7 7 15 30 60)))) 68 | (when (/= s secs) 69 | (message (concat "EMMS: Xine only supports seeking for [+/-] 7/15/30/60 " 70 | "seconds, so we seeked %d seconds") s)) 71 | (process-send-string 72 | emms-player-simple-process-name 73 | (if (< s 0) 74 | (format "SeekRelative%d\n" s) 75 | (format "SeekRelative+%d\n" s))))) 76 | 77 | (defun emms-nearest-value (val list) 78 | "Returns the value of LIST which is nearest to VAL. 79 | 80 | LIST should be a list of integers." 81 | (let* ((nearest (car list)) 82 | (dist (abs (- val nearest)))) 83 | (dolist (lval (cdr list)) 84 | (let ((ndist (abs (- val lval)))) 85 | (when (< ndist dist) 86 | (setq nearest lval 87 | dist ndist)))) 88 | nearest)) 89 | 90 | 91 | (provide 'emms-player-xine) 92 | ;;; emms-player-xine.el ends here 93 | -------------------------------------------------------------------------------- /emms-print-metadata.1: -------------------------------------------------------------------------------- 1 | .\" Hey, EMACS: -*- nroff -*- 2 | .\" First parameter, NAME, should be all caps 3 | .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection 4 | .\" other parameters are allowed: see man(7), man(1) 5 | .TH EMMS-PRINT-METADATA 1 "April 29, 2006" EMMS 6 | .\" Please adjust this date whenever revising the manpage. 7 | .\" 8 | .\" Some roff macros, for reference: 9 | .\" .nh disable hyphenation 10 | .\" .hy enable hyphenation 11 | .\" .ad l left justify 12 | .\" .ad b justify to both left and right margins 13 | .\" .nf disable filling 14 | .\" .fi enable filling 15 | .\" .br insert line break 16 | .\" .sp insert n+1 empty lines 17 | .\" for manpage-specific macros, see man(7) 18 | .SH NAME 19 | emms-print-metadata \- Print information about music files 20 | .SH SYNOPSIS 21 | .B emms-print-metadata 22 | .RI file.ext 23 | .br 24 | .SH DESCRIPTION 25 | .\" TeX users may be more comfortable with the \fB\fP and 26 | .\" \fI\fP escape sequences to invoke bold face and italics, 27 | .\" respectively. 28 | \fBemms-print-metadata\fP will print metadata about music files to 29 | stdout, to be used primarily by EMMS, the Emacs MultiMedia System. 30 | .SH SEE ALSO 31 | .BR emms (info) 32 | .br 33 | .SH AUTHOR 34 | emms-print-metadata was written by Trent Buck. 35 | -------------------------------------------------------------------------------- /emms-show-all.el: -------------------------------------------------------------------------------- 1 | ;;; emms-show-all.el --- Detailed track information for Emms. -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2016-2021, 2025 Free Software Foundation, Inc. 4 | 5 | ;; Author: Yoni Rabkin 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | ;; 14 | ;; EMMS is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | ;; 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; if not, write to the Free Software Foundation, 21 | ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | ;;; Commentary: 24 | ;; 25 | ;; Shows all of the available information Emms can provide on the 26 | ;; currently playing track. Based on an idea suggested on the 27 | ;; emms-help mailing list by Ivan Truskov. 28 | 29 | ;;; Code: 30 | 31 | (require 'emms) 32 | (require 'emms-tag-editor) 33 | 34 | 35 | (defvar emms-show-all-buffer-name "Emms Track Information" 36 | "Name of buffer used by `emms-show-all'.") 37 | 38 | (defvar emms-show-all-kill-buffer-on-quit-p nil 39 | "If t, kill the show-all buffer when quitting.") 40 | 41 | (defvar emms-show-all-track-alist nil 42 | "Declare so as to silence the compiler.") 43 | 44 | (defvar emms-show-all-mode-map 45 | (let ((map (make-sparse-keymap))) 46 | (set-keymap-parent map text-mode-map) 47 | (define-key map (kbd "q") #'emms-show-all-mode-bury-buffer) 48 | (define-key map (kbd "E") #'emms-show-all-edit-track) 49 | map) 50 | "Keymap for `emms-show-all-mode'.") 51 | 52 | (define-derived-mode emms-show-all-mode text-mode "Emms-Show-All" 53 | "Major mode for `emms-show-all' 54 | \\{emms-show-all-mode-map}") 55 | 56 | (defun emms-show-all-edit-track () 57 | "Edit the track being shown." 58 | (interactive) 59 | (let ((track emms-show-all-track-alist)) 60 | (emms-show-all-mode-bury-buffer) 61 | (emms-tag-editor-edit-track track))) 62 | 63 | (defun emms-show-all-mode-bury-buffer () 64 | "Bury, and optionally kill the show buffer." 65 | (interactive) 66 | (quit-restore-window 67 | (selected-window) 68 | (when emms-show-all-kill-buffer-on-quit-p 'kill))) 69 | 70 | (defun emms-show-all-setup-buffer () 71 | "Prepare the display buffer." 72 | (let ((buffer (get-buffer-create emms-show-all-buffer-name))) 73 | (with-current-buffer buffer 74 | (when (not (local-variable-p 'emms-show-all-track-alist)) 75 | (make-local-variable 'emms-show-all-track-alist)) 76 | (setq buffer-read-only t) 77 | (when (not (equal major-mode 'emms-show-all-mode)) 78 | (emms-show-all-mode)) 79 | (let ((inhibit-read-only t)) 80 | (erase-buffer))) 81 | buffer)) 82 | 83 | (defun emms-show-all-format (track) 84 | "Format information for TRACK." 85 | (let ((s "")) 86 | (dolist (e (mapcar #'(lambda (tag) 87 | (cons 88 | (format "%s" (car tag)) 89 | (or (emms-track-get track (car tag)) ""))) 90 | emms-tag-editor-tags)) 91 | (setq s (concat s (format "%-17s: %s\n" (car e) (cdr e))))) 92 | s)) 93 | 94 | (defun emms-show-all-insert (track) 95 | "Insert information for TRACK in current buffer." 96 | (let ((type (emms-track-type track))) 97 | (cond ((eq 'file type) 98 | (insert (emms-show-all-format track))) 99 | ((eq 'url type) 100 | (insert 101 | (emms-format-url-track-name (emms-track-name track)))) 102 | (t (insert (concat (symbol-name type) 103 | ": " (emms-track-name track))))))) 104 | 105 | (defun emms-show-all-track (track) 106 | "Display information for TRACK." 107 | (let ((buffer (emms-show-all-setup-buffer))) 108 | (with-current-buffer buffer 109 | (let ((inhibit-read-only t)) 110 | (setq emms-show-all-track-alist track) 111 | (emms-show-all-insert track)) 112 | (pop-to-buffer (current-buffer))))) 113 | 114 | (defun emms-show-all () 115 | "Describe the current EMMS track in detail." 116 | (interactive) 117 | (if emms-player-playing-p 118 | (emms-show-all-track 119 | (emms-playlist-current-selected-track)) 120 | (message "nothing playing right now"))) 121 | 122 | 123 | (provide 'emms-show-all) 124 | 125 | ;;; emms-playlist-mode.el ends here 126 | -------------------------------------------------------------------------------- /emms-stream-info.el: -------------------------------------------------------------------------------- 1 | ;;; emms-stream-info.el --- Info from streaming audio -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 4 | ;; 2009, 2019 Free Software Foundation, Inc. 5 | 6 | ;; Author: Yoni Rabkin 7 | 8 | ;; This file is part of EMMS. 9 | 10 | ;; EMMS is free software; you can redistribute it and/or 11 | ;; modify it under the terms of the GNU General Public License as 12 | ;; published by the Free Software Foundation; either version 3 of the 13 | ;; License, or (at your option) any later version. 14 | 15 | ;; EMMS is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with EMMS; if not, write to the Free Software Foundation, 22 | ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 23 | 24 | ;;; Code: 25 | 26 | ;; to be implemented! 27 | 28 | (provide 'emms-stream-info) 29 | 30 | ;;; emms-stream-info.el ends here 31 | -------------------------------------------------------------------------------- /emms-streams.el: -------------------------------------------------------------------------------- 1 | ;; emms-streams.el -- A collection of online streaming audio -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2019, 2022 Free Software Foundation, Inc. 4 | 5 | ;; Authors: Yoni Rabkin 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify it 10 | ;; under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3 of the License, or 12 | ;; (at your option) any later version. 13 | 14 | ;; EMMS is distributed in the hope that it will be useful, but WITHOUT 15 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 17 | ;; License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; if not, write to the Free Software Foundation, 21 | ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | ;;; Commentary: 24 | 25 | ;; 2019-11-05 - This is a complete re-write of the emms-streams.el, 26 | ;; using a different approach. 27 | 28 | ;; This includes the built-in list of streams which come with Emms by 29 | ;; default. Emms has no affiliation of any kind with the streaming 30 | ;; audio stations listed below, nor is this an endorsement of these 31 | ;; stations. Instead, this is a collection of stations submitted to 32 | ;; the project over the years by people who enjoy Emms. We hope you 33 | ;; will enjoy them too. 34 | 35 | ;;; Code: 36 | 37 | (require 'emms) 38 | (require 'emms-source-playlist) 39 | (require 'emms-playlist-mode) 40 | (require 'emms-cache) 41 | 42 | 43 | ;;; ------------------------------------------------------------------ 44 | ;;; definitions 45 | ;;; ------------------------------------------------------------------ 46 | 47 | (defvar emms-streams-buffer-name "Emms Streams" 48 | "Name for creating a streams buffer.") 49 | 50 | (defvar emms-streams-built-in-disclaimer 51 | ";; This includes the built-in list of streams which come with Emms by 52 | ;; default. Emms has no affiliation of any kind with the streaming 53 | ;; audio stations listed below, nor is this an endorsement of these 54 | ;; stations. Instead, this is a collection of stations submitted to 55 | ;; the project over the years by people who enjoy Emms. We hope you 56 | ;; will enjoy them too." 57 | "Explaining the relationship between Emms and these streams.") 58 | 59 | (defvar emms-streams-built-in-list 60 | '((*track* (type . streamlist) 61 | (name . "http://www.somafm.com/beatblender.pls") 62 | (metadata "SomaFM: Beatblender" "http://www.somafm.com/beatblender.pls" 1 streamlist)) 63 | (*track* (type . streamlist) 64 | (name . "http://www.somafm.com/secretagent.pls") 65 | (metadata "SomaFM: Secret Agent" "http://www.somafm.com/secretagent.pls" 1 streamlist)) 66 | (*track* (type . streamlist) 67 | (name . "http://www.somafm.com/groovesalad.pls") 68 | (metadata "SomaFM: Groove Salad" "http://www.somafm.com/groovesalad.pls" 1 streamlist)) 69 | (*track* (type . streamlist) 70 | (name . "http://www.somafm.com/dronezone.pls") 71 | (metadata "SomaFM: Drone Zone" "http://www.somafm.com/dronezone.pls" 1 streamlist)) 72 | (*track* (type . streamlist) 73 | (name . "http://www.somafm.com/thetrip.pls") 74 | (metadata "SomaFM: The Trip" "http://www.somafm.com/thetrip.pls" 1 streamlist)) 75 | (*track* (type . streamlist) 76 | (name . "http://www.somafm.com/indiepop.pls") 77 | (metadata "SomaFM: Indie Pop Rocks" "http://www.somafm.com/indiepop.pls" 1 streamlist)) 78 | (*track* (type . url) (name . "http://listen.radionomy.com:80/-PHILOSOMATIKAPROGRESSIVE-") 79 | (metadata "P H I L O S O M A T I K A - Progressive Psytrance" "http://listen.radionomy.com:80/-PHILOSOMATIKAPROGRESSIVE-" 1 url)) 80 | (*track* (type . streamlist) 81 | (name . "http://www.bassdrive.com/BassDrive.m3u") 82 | (metadata "Drum and Bass Radio, BassDrive" "http://www.bassdrive.com/BassDrive.m3u" 1 streamlist)) 83 | (*track* (type . streamlist) 84 | (name . "http://www.ibiblio.org/wcpe/wcpe.pls") 85 | (metadata "WCPE, Classical Music" "http://www.ibiblio.org/wcpe/wcpe.pls" 1 streamlist)) 86 | (*track* (type . streamlist) 87 | (name . "http://stream.nute.net/kohina/stream.ogg.m3u") 88 | (metadata "Kohina - Old school game and demo music" "http://stream.nute.net/kohina/stream.ogg.m3u" 1 streamlist)) 89 | (*track* (type . streamlist) 90 | (name . "http://privat.is-by.us:8000/necta192.mp3.m3u") 91 | (metadata "Nectarine, Demoscene Radio, DE Continuum's relay 192 mp3" "http://privat.is-by.us:8000/necta192.mp3.m3u" 1 streamlist)) 92 | (*track* (type . streamlist) 93 | (name . "http://nectarine.from-de.com/necta192.m3u") 94 | (metadata "Nectarine, Demoscene Radio, DE stream (High Bitrate)" "http://nectarine.from-de.com/necta192.m3u" 1 streamlist)) 95 | (*track* (type . streamlist) 96 | (name . "http://www.wfmu.org/wfmu.pls") 97 | (metadata "WFMU, Freeform radio" "http://www.wfmu.org/wfmu.pls" 1 streamlist)) 98 | (*track* (type . streamlist) 99 | (name . "http://wfmu.org/wfmu_rock.pls") 100 | (metadata "WFMU, Rock'n'Soul Ichiban!" "http://www.wfmu.org/wfmu.pls" 1 streamlist)) 101 | (*track* (type . streamlist) 102 | (name . "http://wfmu.org/wfmu_drummer.pls") 103 | (metadata "WFMU, Give the Drummer Radio" "http://www.wfmu.org/wfmu.pls" 1 streamlist)) 104 | (*track* (type . streamlist) 105 | (name . "http://wfmu.org/wfmu_sheena.pls") 106 | (metadata "WFMU, Sheena's Jungle Room" "http://www.wfmu.org/wfmu.pls" 1 streamlist)) 107 | (*track* (type . streamlist) 108 | (name . "http://nyc01.egihosting.com:6232/listen.pls") 109 | (metadata "WBCR-LP - Berkshire Community Radio" "http://nyc01.egihosting.com:6232/listen.pls" 1 streamlist)) 110 | (*track* (type . streamlist) 111 | (name . "http://199.244.85.125:8000/wxhq1") 112 | (metadata "WXHQ-LP - Newport Radio" "http://199.244.85.125:8000/wxhq1" 1 streamlist)))) 113 | 114 | (defcustom emms-streams-file (concat (file-name-as-directory emms-directory) 115 | "streams.emms") 116 | "A file used to store the built-in streams." 117 | :group 'emms 118 | :type 'file) 119 | 120 | 121 | ;;; ------------------------------------------------------------------ 122 | ;;; private functions 123 | ;;; ------------------------------------------------------------------ 124 | 125 | (defun emms-streams-install-file (file) 126 | "Install FILE, containing streams." 127 | (when (not (file-directory-p (file-name-directory emms-streams-file))) 128 | (make-directory (file-name-directory emms-streams-file))) 129 | (if (or (not (file-exists-p file)) 130 | (and (file-exists-p file) 131 | (y-or-n-p (format "overwrite existing %s?" file)))) 132 | (progn 133 | (message "writing %s" file) 134 | (with-temp-buffer 135 | (insert emms-source-playlist-native-header-line) 136 | (insert (format "\n%s\n\n" emms-streams-built-in-disclaimer)) 137 | (insert 138 | (concat "(" 139 | (mapconcat 140 | #'(lambda (e) 141 | (format "%S" e)) 142 | emms-streams-built-in-list "\n") 143 | ")")) 144 | (write-region (point-min) (point-max) file)) 145 | (message "writing %s... done" file)) 146 | (message "aborting"))) 147 | 148 | 149 | ;;; ------------------------------------------------------------------ 150 | ;;; interface 151 | ;;; ------------------------------------------------------------------ 152 | 153 | (defun emms-streams-install () 154 | "Install the built-in streams file." 155 | (interactive) 156 | (emms-streams-install-file emms-streams-file)) 157 | 158 | ;;;###autoload 159 | (defun emms-streams () 160 | "Create or switch to the built-in streaming audio playlist." 161 | (interactive) 162 | (when (and (not (file-exists-p emms-streams-file)) 163 | (y-or-n-p "Emms' built-in streams file hasn't been installed yet. Install it now?")) 164 | (emms-streams-install)) 165 | (let ((buf (get-buffer emms-streams-buffer-name))) 166 | (when (not buf) 167 | (with-current-buffer (get-buffer-create emms-streams-buffer-name) 168 | (setq buf (current-buffer)) 169 | (emms-playlist-mode) 170 | (setq emms-playlist-buffer-p t) 171 | (emms-playlist-set-playlist-buffer (current-buffer)) 172 | (emms-add-native-playlist emms-streams-file))) 173 | (switch-to-buffer buf))) 174 | 175 | 176 | (provide 'emms-streams) 177 | 178 | ;;; emms-streams.el ends here 179 | -------------------------------------------------------------------------------- /emms-tag-tracktag.el: -------------------------------------------------------------------------------- 1 | ;;; emms-tag-tracktag.el --- EMMS interface for audiotools tracktag -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2021 Free Software Foundation, Inc. 4 | 5 | ;; Author: Grant Shoshin Shangreaux 6 | ;; Keywords: 7 | 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Commentary: 22 | 23 | ;; Provides a wrapper for audiotools tracktag executable 24 | ;; http://audiotools.sourceforge.net/tracktag.html 25 | ;; Given an EMMS TRACK structure, it will map the emms-info fields onto 26 | ;; arguments for tracktag. Then it calls the tracktag process to write the 27 | ;; info as metadata tags on the track's associated file. 28 | 29 | ;;; Code: 30 | 31 | (require 'emms) 32 | 33 | (defvar emms-tag-tracktag--info-fields 34 | '((info-artist . artist) 35 | (info-composer . composer) 36 | (info-performer . performer) 37 | (info-title . name) 38 | (info-album . album) 39 | (info-tracknumber . number) 40 | (info-discnumber . album-number) 41 | (info-year . year) 42 | (info-date . date) 43 | (info-note . comment)) 44 | "An alist mapping info-* fields to tracktag fields.") 45 | 46 | (defvar emms-tag-tracktag-log-buffer "*EMMS-LOG*" 47 | "Name of emms-tag-tracktag's log buffer. 48 | Defaults to the same value as emms-tag-editor-log-buffer") 49 | 50 | (defun emms-tag-tracktag--map-track-info (track) 51 | (seq-filter (lambda (cell) (cdr cell)) 52 | (mapcar (lambda (pair) 53 | (cons (cdr pair) (emms-track-get track (car pair)))) 54 | emms-tag-tracktag--info-fields))) 55 | 56 | (defun emms-tag-tracktag--build-args (track) 57 | (flatten-list 58 | (append 59 | (mapcar (lambda (pair) 60 | (let ((tag (car pair)) (value (cdr pair))) 61 | (when value 62 | ;; if we've deleted a tag value in the editor, remove the tag from file metadata. 63 | (if (string-equal "" value) (concat "--remove-" (format "%s" tag)) 64 | (concat "--" (format "%s" tag) "=" value))))) 65 | (emms-tag-tracktag--map-track-info track)) 66 | (list (emms-track-name track))))) 67 | 68 | (defun emms-tag-tracktag-file (track) 69 | (apply #'call-process 70 | "tracktag" nil 71 | (get-buffer-create emms-tag-tracktag-log-buffer) 72 | nil 73 | "-Vdebug" 74 | (emms-tag-tracktag--build-args track))) 75 | 76 | (provide 'emms-tag-tracktag) 77 | ;;; emms-tag-tracktag.el ends here 78 | -------------------------------------------------------------------------------- /emms-url.el: -------------------------------------------------------------------------------- 1 | ;;; emms-url.el --- Make URL and EMMS work together well -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. 4 | 5 | ;; This file is part of EMMS. 6 | 7 | ;; EMMS is free software; you can redistribute it and/or modify 8 | ;; it under the terms of the GNU General Public License as published by 9 | ;; the Free Software Foundation; either version 3, or (at your option) 10 | ;; any later version. 11 | ;; 12 | ;; EMMS is distributed in the hope that it will be useful, 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ;; GNU General Public License for more details. 16 | ;; 17 | ;; You should have received a copy of the GNU General Public License 18 | ;; along with EMMS; see the file COPYING. If not, write to the 19 | ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 20 | ;; Boston, MA 02110-1301, USA. 21 | 22 | ;;; Commentary: 23 | 24 | ;; These routines sanify input to URL and parse data returned by URL. 25 | 26 | ;;; Code: 27 | 28 | (require 'url) 29 | (require 'emms-compat) 30 | 31 | (defvar emms-url-specials-entire 32 | '((?\ . "%20") 33 | (?\n . "%0D%0A")) 34 | "*An alist of characters which must be represented specially in URLs. 35 | The transformation is the key of the pair. 36 | 37 | This is used by `emms-url-quote-entire'.") 38 | 39 | (defun emms-url-quote-entire (url) 40 | "Escape specials conservatively in an entire URL. 41 | 42 | The specials to escape are specified by the `emms-url-specials-entire' 43 | variable. 44 | 45 | If you want to escape parts of URLs thoroughly, then use 46 | `emms-url-quote' instead." 47 | (apply (function concat) 48 | (mapcar 49 | (lambda (ch) 50 | (let ((repl (assoc ch emms-url-specials-entire))) 51 | (if (null repl) 52 | (char-to-string ch) 53 | (cdr repl)))) 54 | (append url nil)))) 55 | 56 | (defun emms-url-quote (s &optional safe) 57 | "Replace special characters in S using the `%xx' escape. 58 | This is useful for escaping parts of URLs, but not entire URLs. 59 | 60 | Characters in [a-zA-Z_.-/] and SAFE(default is \"\") will never be 61 | quoted. 62 | e.g., 63 | (emms-url-quote \"abc def\") => \"abc%20def\"." 64 | (if (not (stringp s)) 65 | "" 66 | (or safe (setq safe "")) 67 | (save-match-data 68 | (let ((re (if (string-match "]" safe) 69 | ;; `]' should be placed at the beginning inside [] 70 | (format "[]a-zA-Z_.-/%s]" 71 | (emms-replace-regexp-in-string "]" "" safe)) 72 | (format "[a-zA-Z_.-/%s]" safe)))) 73 | (mapconcat 74 | (lambda (c) 75 | (let ((s1 (char-to-string c))) 76 | (if (string-match re s1) 77 | s1 78 | (format "%%%02x" c)))) 79 | (string-to-list (encode-coding-string s 'utf-8)) 80 | ""))))) 81 | 82 | (defun emms-url-quote-plus (s &optional safe) 83 | "Run (emms-url-quote s \" \"), then replace ` ' with `+'." 84 | (emms-replace-regexp-in-string 85 | " " "+" (emms-url-quote s (concat safe " ")))) 86 | 87 | (defun emms-url-quote-underscore (s &optional safe) 88 | "Run (emms-url-quote s \" \"), then replace ` ' with `_'." 89 | (emms-replace-regexp-in-string 90 | " " "_" (emms-url-quote s (concat safe " ")))) 91 | 92 | (defun emms-http-content-coding () 93 | (save-match-data 94 | (and (boundp 'url-http-content-type) 95 | (stringp url-http-content-type) 96 | (string-match ";\\s-*charset=\\([^;[:space:]]+\\)" 97 | url-http-content-type) 98 | (intern-soft (downcase (match-string 1 url-http-content-type)))))) 99 | 100 | (defun emms-http-decode-buffer (&optional buffer) 101 | "Recode the buffer with `url-retrieve's contents. Else the 102 | buffer would contain multibyte chars like \\123\\456." 103 | (with-current-buffer (or buffer (current-buffer)) 104 | (let* ((default (or (car default-process-coding-system) 'utf-8)) 105 | (coding (or (emms-http-content-coding) default))) 106 | (when coding 107 | ;; (pop-to-buffer (current-buffer)) 108 | ;; (message "content-type: %s" url-http-content-type) 109 | ;; (message "coding: %S [default: %S]" coding default) 110 | (set-buffer-multibyte t) 111 | (decode-coding-region (point-min) (point-max) coding))))) 112 | 113 | (provide 'emms-url) 114 | ;;; emms-url.el ends here 115 | -------------------------------------------------------------------------------- /emms-volume-amixer.el: -------------------------------------------------------------------------------- 1 | ;;; emms-volume-amixer.el --- a mode for changing volume using amixer -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2006, 2007, 2008, 2009, 2023 Free Software Foundation, Inc. 4 | 5 | ;; Author: Martin Schoenmakers 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | ;; 14 | ;; EMMS is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | ;; 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; see the file COPYING. If not, write to the 21 | ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 22 | ;; Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; This file defines a few simple functions to raise or lower the volume 27 | ;; using amixer. It can be used stand-alone, though it's meant for usage 28 | ;; with EMMS, particularly with emms-volume.el 29 | 30 | ;;; History: 31 | 32 | ;; May 30 2006: First cleanup and collation of amixer functions into a 33 | ;; separate file for releasability. 34 | 35 | 36 | ;;; Code: 37 | (defcustom emms-volume-amixer-control "Master" 38 | "The control to change the volume with. 39 | Controls includes \"Master\", \"PCM\", etc. For a full list of available 40 | controls, run `amixer controls' in a shell." 41 | :type '(choice (const :tag "Master" "Master") 42 | (const :tag "PCM" "PCM") 43 | (string :tag "Something else: ")) 44 | :group 'emms-volume) 45 | 46 | (defcustom emms-volume-amixer-card 0 47 | "The card number to change volume. 48 | The card is identified by a number. For a full list run `cat 49 | /proc/asound/cards' in a shell." 50 | :type 'integer 51 | :group 'emms-volume) 52 | 53 | (defvar emms-volume-amixer-volume-regexp 54 | "\\[\\([0-9]+\\)%\\]" 55 | "Regexp to capture the volume from amixer output.") 56 | 57 | ;;;###autoload 58 | (defun emms-volume-amixer-change (amount) 59 | "Change amixer master volume by AMOUNT." 60 | (message "Playback channels: %s" 61 | (with-temp-buffer 62 | (when (zerop 63 | (call-process "amixer" nil (current-buffer) nil 64 | "-c" 65 | (format "%d" emms-volume-amixer-card) 66 | "sset" emms-volume-amixer-control 67 | (format "%d%%%s" (abs amount) 68 | (if (< amount 0) "-" "+")))) 69 | (if (re-search-backward emms-volume-amixer-volume-regexp nil t) 70 | (match-string 1)))))) 71 | 72 | (defun emms-volume-amixer-get () 73 | "Return the amixer volume. 74 | 75 | Number is limited to the range [0-100]." 76 | (let ((v (with-temp-buffer 77 | (when (zerop 78 | (call-process "amixer" nil (current-buffer) nil 79 | "-c" 80 | (format "%d" emms-volume-amixer-card) 81 | "sget" emms-volume-amixer-control)) 82 | (if (re-search-backward 83 | emms-volume-amixer-volume-regexp nil t) 84 | (match-string 1) 85 | nil))))) 86 | (if v 87 | (max (min (string-to-number v) 100) 0) 88 | (error "could not get volume from amixer backend")))) 89 | 90 | 91 | 92 | (provide 'emms-volume-amixer) 93 | 94 | ;;; emms-volume-amixer.el ends here 95 | -------------------------------------------------------------------------------- /emms-volume-mixerctl.el: -------------------------------------------------------------------------------- 1 | ;;; emms-volume-mixerctl.el --- a mode for changing volume using mixerctl -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2006, 2007, 2008, 2009, 2019 Free Software Foundation, Inc. 4 | 5 | ;; Authors: Martin Schoenmakers 6 | ;; Bruno Félix Rezende Ribeiro 7 | 8 | ;; This file is part of EMMS. 9 | 10 | ;; EMMS is free software; you can redistribute it and/or modify 11 | ;; it under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation; either version 3, or (at your option) 13 | ;; any later version. 14 | ;; 15 | ;; EMMS is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | ;; 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with EMMS; see the file COPYING. If not, write to the 22 | ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 23 | ;; Boston, MA 02110-1301, USA. 24 | 25 | ;;; Commentary: 26 | 27 | ;; This file defines a few simple functions to raise or lower the volume 28 | ;; using mixerctl. It can be used stand-alone, though it's meant for usage 29 | ;; with EMMS, particularly with emms-volume.el 30 | 31 | ;;; History: 32 | 33 | ;; Jul 06 2019: Based on emms-volume-amixer.el by Martin Schoenmakers 34 | 35 | ;;; Todo: 36 | 37 | ;; There probably needs to be more configurability, which may in turn 38 | ;; mean adding some more functions. 39 | ;; Some of this could benefit from adding customize interfaces. 40 | 41 | ;;; Code: 42 | 43 | (defcustom emms-volume-mixerctl-control "master" 44 | "The control to change the volume with. 45 | Controls includes \"master\", \"mono\", etc. For a full list of available 46 | controls, run `mixerctl -a' in a shell." 47 | :type '(choice (const :tag "master" "master") 48 | (const :tag "mono" "mono") 49 | (string :tag "Something else: ")) 50 | :group 'emms-volume) 51 | 52 | (defcustom emms-volume-mixerctl-card 0 53 | "The card number to change volume. 54 | The card is identified by a number. For a full list run `ls 55 | /dev/mixer?*' in a shell." 56 | :type 'integer 57 | :group 'emms-volume) 58 | 59 | ;;;###autoload 60 | (defun emms-volume-mixerctl-change (amount) 61 | "Change mixerctl master volume by AMOUNT." 62 | (message "Playback channels: %s" 63 | (with-temp-buffer 64 | (when (zerop 65 | (call-process "mixerctl" nil (current-buffer) nil 66 | "-f" 67 | (format "/dev/mixer%d" emms-volume-mixerctl-card) 68 | (let ((amount-str 69 | (format "%s%d" (if (< amount 0) "-" "+") 70 | (abs amount)))) 71 | (format "outputs.%s=%s,%s" 72 | emms-volume-mixerctl-control 73 | amount-str amount-str)))) 74 | (if (and (forward-line -1) 75 | (re-search-forward "^\\(.*\\):.*->.*,\\(.*\\)$" nil t)) 76 | (format "%s -> %s" (match-string 1) (match-string 2))))))) 77 | 78 | (provide 'emms-volume-mixerctl) 79 | 80 | ;;; emms-volume-mixerctl.el ends here 81 | -------------------------------------------------------------------------------- /emms-volume-mpv.el: -------------------------------------------------------------------------------- 1 | ;;; emms-volume-mpv.el --- Volume function to adjust mpv volume easily -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2025 Free Software Foundation, Inc. 4 | 5 | ;; Author: Richard Sent 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | ;; 14 | ;; EMMS is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | ;; 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; see the file COPYING. If not, write to the 21 | ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 22 | ;; Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | ;; 26 | ;; This file defines a function to raise or lower the volume of mpv. 27 | ;; It can be used stand-alone by passing a process object, though it 28 | ;; is meant for usage with Emms, particularly with emms-volume.el and 29 | ;; emms-player-mpv.el. 30 | ;; 31 | ;; To use add the following to your Emms configuration 32 | ;; (setq emms-volume-change-function 'emms-volume-mpv-change) 33 | 34 | ;;; History: 35 | 36 | ;; January 2025: First release, partly based on emms-volume-pulse.el. 37 | 38 | ;;; Code: 39 | 40 | (require 'emms-player-mpv) 41 | 42 | (defcustom emms-volume-mpv-method 'native 43 | "How Emms should attempt to adjust mpv's volume. 44 | 45 | If `native', Emms will adjust mpv's volume property. This 46 | provides the same experience as adjusting the volume slider in 47 | mpv. 48 | 49 | If `system', Emms will adjust mpv's ao-volume property, which 50 | adjusts the volume using the system audio service, such as 51 | Pulseaudio. Depending on what audio service is being used this 52 | may either change the mpv application's volume or global volume. 53 | 54 | If `smart', Emms will adjust both mpv's volume and ao-volume 55 | properties. When raising volume, the native volume will be raised 56 | to 100. Emms will then switch to adjusting system volume to 100 57 | before raising the native volume again. When lowering volume, 58 | Emms will lower the software volume to 100, then lower system 59 | volume to 0. 60 | 61 | Both `system' and `smart' require mpv to expose the ao-volume 62 | property. This property is only available while mpv audio output 63 | is active. If audio output is not active, the volume will not be 64 | changed. 65 | 66 | Additionally, the percentage provided by and set for ao-volume 67 | and thus this module may not match what is reported by the system 68 | audio program." 69 | :type '(choice (const :tag "MPV Volume" native) 70 | (const :tag "System Volume" system) 71 | (const :tag "Smart" smart)) 72 | :group 'emms-volume) 73 | 74 | (defvar emms-volume-mpv--volume-sync (make-mutex "emms-volume-mpv--volume-sync") 75 | "Ensure only one volume-change function runs to completion at a 76 | time.") 77 | 78 | (defun emms-volume-mpv-synchronous-ipc (cmd &optional proc) 79 | "Run mpv command and get result synchronously for current thread. 80 | 81 | This must not be run by the main thread. The handler for 82 | emms-player-mpv-ipc-req-send runs in the main thread, potentially 83 | causing a deadlock." 84 | (when (eq main-thread (current-thread)) 85 | (error "This function cannot be invoked by the main thread")) 86 | (let* ((emms-volume-mpv--ipc-sync (make-mutex "emms-volume-mpv--ipc-sync")) 87 | (emms-volume-mpv--ipc-sync-check (make-condition-variable emms-volume-mpv--ipc-sync 88 | "emms-volume-mpv--ipc-sync-check")) 89 | (emms-volume-mpv--ipc-sync-reply nil)) 90 | (with-mutex emms-volume-mpv--ipc-sync 91 | (emms-player-mpv-ipc-req-send 92 | cmd 93 | #'(lambda (data err) 94 | (with-mutex emms-volume-mpv--ipc-sync 95 | (setq emms-volume-mpv--ipc-sync-reply (list data err)) 96 | (condition-notify emms-volume-mpv--ipc-sync-check))) 97 | proc) 98 | (while (not emms-volume-mpv--ipc-sync-reply) 99 | (condition-wait emms-volume-mpv--ipc-sync-check)) 100 | (cl-multiple-value-bind (data err) emms-volume-mpv--ipc-sync-reply 101 | (if err (error "Failed to run %s, %s" cmd err) data))))) 102 | 103 | (defun emms-volume-mpv-limit (vol volume-max &optional volume-min) 104 | "Limit VOL to the range [0 - volume-max]." 105 | (max (min vol volume-max) (or volume-min 0))) 106 | 107 | (defun emms-volume-mpv--smart-increment (native-old system-old amount native-max) 108 | (cond 109 | ((< native-old 100) 110 | (list (emms-volume-mpv-limit (+ native-old amount) 100) system-old)) 111 | ((< system-old 100) 112 | (list native-old (emms-volume-mpv-limit (+ system-old amount) 100))) 113 | (t (list (emms-volume-mpv-limit (+ native-old amount) native-max) system-old)))) 114 | 115 | (defun emms-volume-mpv--smart-decrement (native-old system-old amount native-max) 116 | (cond 117 | ((> native-old 100) 118 | (list (emms-volume-mpv-limit (+ native-old amount) native-max 100) system-old)) 119 | (t (list native-old (emms-volume-mpv-limit (+ system-old amount) 100))))) 120 | 121 | (defun emms-volume-mpv--smart-change (native-old system-old amount native-max) 122 | (if (>= amount 0) 123 | (emms-volume-mpv--smart-increment native-old system-old amount native-max) 124 | (emms-volume-mpv--smart-decrement native-old system-old amount native-max))) 125 | 126 | ;;;###autoload 127 | (defun emms-volume-mpv-change (amount &optional proc) 128 | "Change volume by AMOUNT using mpv process PROC." 129 | (unless (or emms-player-mpv-ipc-proc proc) 130 | (error "mpv is not currently running")) 131 | ;; mpv does not protect against storing volumes > volume-max. We 132 | ;; must retrieve volume-max and verify the target volume. 133 | (make-thread 134 | (lambda () 135 | (with-mutex emms-volume-mpv--volume-sync 136 | (with-demoted-errors "Failed to adjust the volume: %s" 137 | (let* ((native-max (emms-volume-mpv-synchronous-ipc '(get_property volume-max) proc)) 138 | (native-old (emms-volume-mpv-synchronous-ipc '(get_property volume) proc)) 139 | (system-old (emms-volume-mpv-synchronous-ipc '(get_property ao-volume) proc))) 140 | (pcase emms-volume-mpv-method 141 | ('native (let ((volume (emms-volume-mpv-limit (+ native-old amount) native-max))) 142 | (emms-volume-mpv-synchronous-ipc 143 | `(set_property volume ,volume) proc) 144 | (message "Native volume is %d%%" volume))) 145 | ('system (let ((volume (emms-volume-mpv-limit (+ system-old amount) 100))) 146 | (emms-volume-mpv-synchronous-ipc 147 | `(set_property ao-volume ,volume) proc) 148 | (message "System volume is %d%%" volume))) 149 | ('smart (cl-multiple-value-bind (native system) 150 | (emms-volume-mpv--smart-change native-old system-old 151 | amount native-max) 152 | (emms-volume-mpv-synchronous-ipc 153 | `(set_property volume ,native) proc) 154 | (emms-volume-mpv-synchronous-ipc 155 | `(set_property ao-volume ,system) proc) 156 | (message "Native volume is %d%% and system volume is %d%%" 157 | native system)))))))))) 158 | 159 | (provide 'emms-volume-mpv) 160 | 161 | ;;; emms-volume-mpv.el ends here 162 | -------------------------------------------------------------------------------- /emms-volume-pulse.el: -------------------------------------------------------------------------------- 1 | ;;; emms-volume-pulse.el --- a mode for changing volume using PulseAudio pactl -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2015-2023 Free Software Foundation, Inc. 4 | 5 | ;; Author: Rasmus Pank Roulund 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | ;; 14 | ;; EMMS is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | ;; 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; see the file COPYING. If not, write to the 21 | ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 22 | ;; Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; This file defines a few simple functions to raise or lower the volume 27 | ;; using pactl. It can be used stand-alone, though it's meant for usage 28 | ;; with EMMS, particularly with emms-volume.el. 29 | ;; 30 | ;; To use add the following to your EMMS configuration 31 | ;; (setq emms-volume-change-function 'emms-volume-pulse-change) 32 | 33 | ;;; History: 34 | 35 | ;; Marts 2015: First release. Partly based on emms-volume-amixer.el 36 | 37 | ;;; Todo: 38 | 39 | ;; There probably needs to be more configurability, which may in turn 40 | ;; mean adding some more functions. 41 | ;; Some of this could benefit from adding customize interfaces. 42 | 43 | ;;; Code: 44 | 45 | (require 'cl-lib) 46 | (require 'subr-x) 47 | 48 | ;; TODO: it would be great if custom could have 49 | ;; choices based on pactl list short sinks | cut -f1-2 50 | 51 | (defcustom emms-volume-pulse-sink nil 52 | "The sink to use for volume adjustment. 53 | 54 | If nil try to use the default sink. 55 | 56 | See full list of devices on your system by running 57 | pactl list short sinks" 58 | :type '(choice (number :tag "Sink number") 59 | (string :tag "Sink symbolic name") 60 | (const :tag "Default sink" nil)) 61 | :group 'emms-volume) 62 | 63 | ;; 'pactl get-sink-volume' was only added recently (version 14.1). 64 | ;; When that version is more widespread this function can be 65 | ;; simplified 66 | (defun emms-volume--pulse-get-volume () 67 | "Return `emms-volume-pulse-sink' volume." 68 | (let* ((emms-volume-pulse-sink 69 | (if emms-volume-pulse-sink 70 | emms-volume-pulse-sink 71 | (string-trim 72 | (shell-command-to-string 73 | "LC_ALL=C pactl info | grep 'Default Sink: ' | cut -d ' ' -f3-")))) 74 | (sink-number-p (numberp emms-volume-pulse-sink)) 75 | (output 76 | (shell-command-to-string 77 | (concat "LC_ALL=C pactl list sinks" "|" 78 | "grep -E -e 'Sink' -e 'Name' -e '^[^a-zA-Z]*Volume'"))) 79 | (volume-string 80 | (car 81 | (reverse 82 | (funcall 83 | (if sink-number-p #'assq #'assoc) 84 | emms-volume-pulse-sink 85 | (mapcar (if sink-number-p 'identity 'cdr) 86 | (cl-loop while 87 | (string-match 88 | (mapconcat #'identity 89 | '(".*Sink[ \t]+\\#\\([0-9]+\\)" 90 | ".*Name:[ \t]\\([^\n]+\\)" 91 | ".*Volume:.*?\\([0-9]+\\)%.*\n?") 92 | "\n") 93 | output) 94 | collect (list (string-to-number (match-string 1 output)) 95 | (match-string 2 output) 96 | (match-string 3 output)) 97 | do (setq output (replace-match "" nil nil output))))))))) 98 | (if volume-string 99 | (string-to-number volume-string) 100 | (error "cannot get volume from sink, check `emms-volume-pulse-sink'")))) 101 | 102 | (defun emms-volume-pulse-limit (v) 103 | "Limit V to the range [0-100]" 104 | (max (min v 100) 0)) 105 | 106 | (defun emms-volume-pulse-get () 107 | "Return the pulse volume." 108 | (emms-volume-pulse-limit 109 | (emms-volume--pulse-get-volume))) 110 | 111 | ;;;###autoload 112 | (defun emms-volume-pulse-change (amount) 113 | "Change PulseAudio volume by AMOUNT." 114 | (message "Volume is %s%%" 115 | (let ((pactl (or (executable-find "pactl") 116 | (error "pactl is not in PATH"))) 117 | (next-vol (emms-volume-pulse-limit 118 | (+ (emms-volume--pulse-get-volume) amount)))) 119 | (when (zerop (call-process 120 | pactl nil nil nil 121 | "set-sink-volume" 122 | (or emms-volume-pulse-sink "@DEFAULT_SINK@") 123 | (format "%d%%" next-vol))) 124 | next-vol)))) 125 | 126 | (provide 'emms-volume-pulse) 127 | 128 | ;;; emms-volume-pulse.el ends here 129 | -------------------------------------------------------------------------------- /emms-volume-sndioctl.el: -------------------------------------------------------------------------------- 1 | ;;; emms-volume-sndioctl.el --- a mode for changing volume using sndioctl -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2006, 2007, 2008, 2009, 2019 Free Software Foundation, Inc. 4 | 5 | ;; Authors: Omar Polo 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | ;; 14 | ;; EMMS is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | ;; 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; see the file COPYING. If not, write to the 21 | ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 22 | ;; Boston, MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; This file defines a few simple functions to raise or lower the 27 | ;; volume using sndioctl. It can be used stand-alone, though it's 28 | ;; meant for usage with EMMS, particularly with emms-volume.el 29 | 30 | ;;; History: 31 | 32 | ;; Sep 09 2021: Based on emms-volume-mixerctl.el by Omar Polo 33 | 34 | ;;; Code: 35 | (require 'subr-x) 36 | 37 | (defcustom emms-volume-sndioctl-stream "output" 38 | "The stream to change the volume with. 39 | Usually it's the global \"output\". For a full list of available 40 | controls, run `sndioctl' in a shell." 41 | :type '(choice (const :tag "output" "output") 42 | (string :tag "Something else: ")) 43 | :group 'emms-volume) 44 | 45 | (defcustom emms-volume-sndioctl-device nil 46 | "The card number to change volume. 47 | The card is identified by a number. For a full list run `ls 48 | /dev/mixer?*' in a shell." 49 | :type '(choice (const :tag "none" nil) 50 | (string :tag "Device: ")) 51 | :group 'emms-volume) 52 | 53 | ;;;###autoload 54 | (defun emms-volume-sndioctl-change (amount) 55 | "Change sndioctl level by AMOUNT." 56 | (message "Playback channels: %s" 57 | (with-temp-buffer 58 | (when (zerop 59 | (apply #'call-process 60 | "sndioctl" nil (current-buffer) nil 61 | `("-n" 62 | ,@(when emms-volume-sndioctl-device 63 | `("-f" ,emms-volume-sndioctl-device)) 64 | ,(format "%s.level=%s%f" 65 | emms-volume-sndioctl-stream 66 | (if (> amount 0) "+" "") 67 | (/ (float amount) 100))))) 68 | (string-trim-right (buffer-string)))))) 69 | 70 | (provide 'emms-volume-sndioctl) 71 | 72 | ;;; emms-volume-sndioctl.el ends here 73 | -------------------------------------------------------------------------------- /emms-volume.el: -------------------------------------------------------------------------------- 1 | ;;; emms-volume.el --- Volume functions and a minor mode to adjust volume easily -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2006-2023, 2025 Free Software Foundation, Inc. 4 | 5 | ;; Author: Martin Schoenmakers 6 | ;; Bruno Félix Rezende Ribeiro 7 | 8 | ;; This file is part of EMMS. 9 | 10 | ;; EMMS is free software; you can redistribute it and/or modify 11 | ;; it under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation; either version 3, or (at your option) 13 | ;; any later version. 14 | ;; 15 | ;; EMMS is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | ;; 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with EMMS; see the file COPYING. If not, write to the 22 | ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 23 | ;; Boston, MA 02110-1301, USA. 24 | 25 | ;;; Commentary: 26 | ;; 27 | ;; This file provides generally two things: 28 | ;; Generic volume setting functions and some appropriate bindings for EMMS 29 | ;; playlist buffers. These can also be bound to global keys,however, the 30 | ;; second part may be more useful for this. This part provides functions 31 | ;; meant to be bound to a global key (the author uses C-c e + and C-c e -), 32 | ;; which then temporarily activates a minor mode allowing you to change the 33 | ;; volume with just + and -. This mode deactivates a short (configurable) 34 | ;; amount of time after the last volume change. This allows for easier volume 35 | ;; adjustment without getting in the way. 36 | 37 | ;;; History: 38 | 39 | ;; May 2006: First stab at writing the minor mode. 40 | ;; 41 | ;; 30 May 2006: Cleanup and restructuring to fit with EMMS. 42 | 43 | ;;; Todo: 44 | 45 | ;; Some of this could benefit from adding customize interfaces. 46 | 47 | ;;; Code: 48 | 49 | 50 | (require 'emms) 51 | (require 'emms-playlist-mode) 52 | (require 'emms-volume-amixer) 53 | (require 'emms-volume-pulse) 54 | (require 'emms-volume-mixerctl) 55 | (require 'emms-volume-sndioctl) 56 | (require 'emms-volume-mpv) 57 | 58 | ;; Customize group 59 | (defgroup emms-volume nil 60 | "Volume setting for EMMS." 61 | :group 'emms) 62 | 63 | (defcustom emms-volume-change-function 64 | (cond 65 | ;; check for sndioctl first to avoid picking up mixerctl or pactl 66 | ;; on OpenBSD. 67 | ((executable-find "sndioctl") #'emms-volume-sndioctl-change) 68 | ((executable-find "amixer") #'emms-volume-amixer-change) 69 | ((executable-find "pactl") #'emms-volume-pulse-change) 70 | ((executable-find "mixerctl") #'emms-volume-mixerctl-change) 71 | (t #'(lambda (_amount) (user-error "%s" "No supported mixer found. Please, define ‘emms-volume-change-function’.")))) 72 | "The function to use to change the volume. 73 | If you have your own functions for changing volume, set this." 74 | :type '(choice (const :tag "Amixer" emms-volume-amixer-change) 75 | (const :tag "MPD" emms-volume-mpd-change) 76 | (const :tag "mpv" emms-volume-mpv-change) 77 | (const :tag "PulseAudio" emms-volume-pulse-change) 78 | (const :tag "Mixerctl" emms-volume-mixerctl-change) 79 | (const :tag "Sndioctl" emms-volume-sndioctl-change) 80 | (function :tag "Lisp function"))) 81 | 82 | (defcustom emms-volume-change-amount 2 83 | "The amount to use when raising or lowering the volume using the 84 | emms-volume interface. 85 | 86 | This should be a positive integer." 87 | :type 'integer) 88 | 89 | (defun emms-volume-select-get-function () 90 | "Return the corresponding get function." 91 | (cond ((not emms-volume-change-function) 92 | (error "`emms-volume-change-function' is not set")) 93 | ((eq emms-volume-change-function #'emms-volume-amixer-change) 94 | #'emms-volume-amixer-get) 95 | ((eq emms-volume-change-function #'emms-volume-pulse-change) 96 | #'emms-volume-pulse-get) 97 | (t (error "could not find corresponding volume getter function for %s" 98 | emms-volume-change-function)))) 99 | 100 | (defun emms-volume-get () 101 | "Return the volume as an integer in the range [0-100]." 102 | (funcall (emms-volume-select-get-function))) 103 | 104 | ;;;###autoload 105 | (defun emms-volume-raise () 106 | "Raise the volume." 107 | (interactive) 108 | (funcall emms-volume-change-function emms-volume-change-amount)) 109 | 110 | ;;;###autoload 111 | (defun emms-volume-lower () 112 | "Lower the volume." 113 | (interactive) 114 | (funcall emms-volume-change-function (- emms-volume-change-amount))) 115 | 116 | (define-key emms-playlist-mode-map (kbd "+") #'emms-volume-raise) 117 | (define-key emms-playlist-mode-map (kbd "-") #'emms-volume-lower) 118 | 119 | ;; Code specific to the minor mode. 120 | (define-minor-mode emms-volume-minor-mode 121 | "Allows volume setting with + and - after an initial key combo." 122 | :global t 123 | :init-value nil 124 | :lighter " (+/-)" 125 | :keymap '(("+" . emms-volume-mode-plus) 126 | ("-" . emms-volume-mode-minus))) 127 | 128 | (defvar emms-volume-mode-timeout 2 129 | "*The timeout in amount of seconds used by `emms-volume-minor-mode'.") 130 | 131 | (defvar emms-volume-mode-timer nil 132 | "The timer `emms-volume-minor-mode' uses.") 133 | 134 | ;;;###autoload 135 | (defun emms-volume-mode-plus () 136 | "Raise volume and enable or extend the `emms-volume-minor-mode' timeout." 137 | (interactive) 138 | (emms-volume-raise) 139 | (emms-volume-mode-start-or-extend)) 140 | 141 | ;;;###autoload 142 | (defun emms-volume-mode-minus () 143 | "Lower volume and enable or extend the `emms-volume-minor-mode' timeout." 144 | (interactive) 145 | (emms-volume-lower) 146 | (emms-volume-mode-start-or-extend)) 147 | 148 | (defun emms-volume-mode-disable-timer () 149 | "Disable `emms-volume-minor-mode' timer." 150 | (cancel-timer emms-volume-mode-timer) 151 | (setq emms-volume-mode-timer nil)) 152 | 153 | (defun emms-volume-mode-set-timer () 154 | "Set a new `emms-volume-minor-mode' timer." 155 | (when emms-volume-mode-timer 156 | (emms-volume-mode-disable-timer)) 157 | (setq emms-volume-mode-timer (run-at-time emms-volume-mode-timeout 158 | nil 159 | #'emms-volume-mode-timer-timeout))) 160 | 161 | (defun emms-volume-mode-timer-timeout () 162 | "Function to disable `emms-volume-minor-mode' at timeout." 163 | (setq emms-volume-mode-timer nil) 164 | (emms-volume-minor-mode -1)) 165 | 166 | (defun emms-volume-mode-start-or-extend () 167 | "Start `emms-volume-minor-mode' or extend its running time." 168 | (when (null emms-volume-minor-mode) 169 | (emms-volume-minor-mode 1)) 170 | (emms-volume-mode-set-timer)) 171 | 172 | (provide 'emms-volume) 173 | ;;; emms-volume.el ends here 174 | -------------------------------------------------------------------------------- /src/emms-print-metadata.c: -------------------------------------------------------------------------------- 1 | /* emms-print-metadata.c --- Info function for libtag 2 | Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. 3 | 4 | Author: Trent Buck 5 | 6 | This file is part of EMMS. 7 | 8 | EMMS is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 3, or (at your option) 11 | any later version. 12 | 13 | EMMS is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with EMMS; see the file COPYING. If not, write to 20 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 | Boston, MA 02110-1301, USA. */ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | int 28 | main (int argc, char **argv) 29 | { 30 | TagLib_File *file; 31 | TagLib_Tag *tag; 32 | const TagLib_AudioProperties *properties; 33 | 34 | if (argc != 2) 35 | { 36 | fprintf (stderr, "usage: emms-print-metadata file.{mp3,ogg,flac}\nother formats may work as well.\n"); 37 | exit (1); 38 | } 39 | 40 | file = taglib_file_new (argv[1]); 41 | 42 | if (!file) 43 | { 44 | fprintf (stderr, "%s: File does not exist or is of an unknown type\n", argv[1]); 45 | exit (1); 46 | } 47 | 48 | tag = taglib_file_tag (file); 49 | 50 | /* Apparently, if the file is named foo.mp3 or similar, the library 51 | still can open it, for whatever reason. 52 | */ 53 | if (!tag) 54 | { 55 | fprintf (stderr, "%s: File does not exist or is of an unknown type\n", argv[1]); 56 | exit (1); 57 | } 58 | 59 | printf ("info-artist=%s\n", taglib_tag_artist (tag)); 60 | printf ("info-title=%s\n", taglib_tag_title (tag)); 61 | printf ("info-album=%s\n", taglib_tag_album (tag)); 62 | printf ("info-tracknumber=%d\n", taglib_tag_track (tag)); 63 | printf ("info-year=%d\n", taglib_tag_year (tag)); 64 | printf ("info-genre=%s\n", taglib_tag_genre (tag)); 65 | printf ("info-note=%s\n", taglib_tag_comment (tag)); 66 | 67 | properties = taglib_file_audioproperties (file); 68 | printf ("info-playing-time=%d\n", 69 | properties ? taglib_audioproperties_length (properties) : 0); 70 | 71 | taglib_tag_free_strings (); 72 | taglib_file_free (file); 73 | 74 | return 0; 75 | } 76 | 77 | /* emms-print-metadata.c ends here. */ 78 | -------------------------------------------------------------------------------- /src/emms-print-metadata.cpp: -------------------------------------------------------------------------------- 1 | /* emms-print-metadata.cpp --- Info function for TagLib 2 | Copyright (C) 2016 Free Software Foundation, Inc. 3 | 4 | Author: Petteri Hintsanen 5 | 6 | This file is part of EMMS. 7 | 8 | EMMS is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 3, or (at your option) 11 | any later version. 12 | 13 | EMMS is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with EMMS; see the file COPYING. If not, write to 20 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 | Boston, MA 02110-1301, USA. */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | static const char* const tags_to_extract[] = { 29 | "album", 30 | "albumsort", 31 | "albumartist", 32 | "albumartistsort", 33 | "artist", 34 | "artistsort", 35 | "composer", 36 | "composersort", 37 | "performer", 38 | "year", 39 | "originalyear", 40 | "date", 41 | "originaldate", 42 | "genre", 43 | "label", 44 | "title", 45 | "titlesort", 46 | "tracknumber", 47 | "discnumber" 48 | }; 49 | 50 | void print_tag (const TagLib::PropertyMap& tags, const std::string& tag); 51 | 52 | int 53 | main (int argc, char* argv[]) 54 | { 55 | if (argc != 2) 56 | { 57 | std::cerr << argv[0] << ": " 58 | << "usage: emms-print-metadata FILENAME" 59 | << std::endl 60 | << "FILENAME must end to one of these extensions: " 61 | << TagLib::FileRef::defaultFileExtensions ().toString () 62 | << std::endl; 63 | return 1; 64 | } 65 | 66 | TagLib::FileRef file (argv[1]); 67 | if (file.isNull ()) { 68 | std::cerr << argv[0] << ": " 69 | << argv[1] << ": " 70 | << "file does not exist or is of an unknown type" 71 | << std::endl; 72 | return 1; 73 | } 74 | 75 | const TagLib::PropertyMap tags = file.file ()->properties (); 76 | if (tags.isEmpty ()) { 77 | std::cerr << argv[0] << ": " 78 | << argv[1] << ": " 79 | << "file does not have tags or is of an unknown type" 80 | << std::endl; 81 | return 1; 82 | } 83 | 84 | for (unsigned int i = 0; i < sizeof (tags_to_extract) / sizeof (char*); 85 | i++) 86 | { 87 | print_tag (tags, tags_to_extract[i]); 88 | } 89 | 90 | int length = 0; 91 | if (file.audioProperties ()) 92 | { 93 | const TagLib::AudioProperties* properties = file.audioProperties (); 94 | length = properties->length (); 95 | } 96 | std::cout << "info-playing-time=" << length << std::endl; 97 | 98 | return 0; 99 | } 100 | 101 | void 102 | print_tag (const TagLib::PropertyMap& tags, const std::string& tag) 103 | { 104 | TagLib::StringList values = tags[tag]; 105 | if (!values.isEmpty ()) 106 | { 107 | const TagLib::String& value = values.front (); 108 | std::cout << "info-" << tag << "=" << value.to8Bit (true) << std::endl; 109 | } 110 | } 111 | 112 | /* emms-print-taglib-metadata.cpp ends here. */ 113 | -------------------------------------------------------------------------------- /src/emms-print-metadata.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # emms-print-metadata.pl --- Info function for libtag 3 | # Copyright (C) 2012 Free Software Foundation, Inc. 4 | 5 | # Author: Lucas Bonnet 6 | 7 | # This file is part of EMMS. 8 | 9 | # EMMS is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 3, or (at your option) 12 | # any later version. 13 | 14 | # EMMS is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | 19 | # You should have received a copy of the GNU General Public License 20 | # along with EMMS; see the file COPYING. If not, write to 21 | # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | # Boston, MA 02110-1301, USA. 23 | 24 | use strict; 25 | use warnings; 26 | 27 | use Audio::Scan; 28 | use File::Basename; 29 | 30 | # enable UTF-8 output 31 | binmode(STDOUT, ":utf8"); 32 | 33 | my $file = $ARGV[0]; 34 | 35 | # Scan without reading (possibly large) artwork into memory. 36 | # Instead of binary artwork data, the size of the artwork will be returned instead. 37 | local $ENV{AUDIO_SCAN_NO_ARTWORK} = 1; 38 | my $data = Audio::Scan->scan($file); 39 | 40 | # determines the names of tags according to file type 41 | my %ext_map = ("mp3" => 42 | {'artist' => 'TPE1', 43 | 'title' => 'TIT2', 44 | 'album' => 'TALB', 45 | 'tracknumber' => 'TRCK', 46 | 'composer' => 'TCOM', 47 | 'performer' => 'TPE2', 48 | 'year' => 'TDRC', 49 | 'genre' => 'TCON', 50 | 'comment' => 'COMM'}, 51 | "flc" => 52 | {'artist' => 'ARTIST', 53 | 'title' => 'TITLE', 54 | 'album' => 'ALBUM', 55 | 'tracknumber' => 'TRACKNUMBER', 56 | 'composer' => 'COMPOSER', 57 | 'performer' => 'PERFORMER', 58 | 'year' => 'DATE', 59 | 'genre' => 'GENRE', 60 | 'comment' => 'COMMENT'}, 61 | "ogg" => 62 | {'artist' => 'ARTIST', 63 | 'title' => 'TITLE', 64 | 'album' => 'ALBUM', 65 | 'tracknumber' => 'TRACKNUMBER', 66 | 'composer' => 'COMPOSER', 67 | 'performer' => 'PERFORMER', 68 | 'year' => 'DATE', 69 | 'genre' => 'GENRE', 70 | 'comment' => 'COMMENT'}, 71 | ); 72 | 73 | # find out extension 74 | my ($filename, $directories, $extension) = fileparse($file, qr/[^.]*/); 75 | my $type = Audio::Scan->type_for(lc($extension)); 76 | my $tag_map = $ext_map{$type}; 77 | 78 | # print tag info 79 | print "info-artist="; safe_print('artist'); 80 | print "info-title="; safe_print('title'); 81 | print "info-album="; safe_print('album'); 82 | print "info-tracknumber="; safe_print('tracknumber'); 83 | print "info-composer="; safe_print('composer'); 84 | print "info-performer="; safe_print('performer'); 85 | print "info-year="; safe_print('year'); 86 | print "info-genre="; safe_print('genre'); 87 | print "info-note=" ; safe_print('comment'); 88 | 89 | print "info-playing-time=",int($data->{'info'}->{'song_length_ms'} / 1000),"\n"; 90 | 91 | sub safe_print { 92 | my $k = shift; 93 | 94 | if (defined $data->{'tags'}->{ $tag_map->{$k} }) { 95 | print $data->{'tags'}->{ $tag_map->{$k} }; 96 | } else { 97 | print ""; 98 | } 99 | print "\n"; 100 | } 101 | -------------------------------------------------------------------------------- /test/emms-info-native-flac-tests.el: -------------------------------------------------------------------------------- 1 | ;;; emms-info-native-flac-tests.el --- Test suite for emms-info-native-flac -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2023 Free Software Foundation, Inc. 4 | 5 | ;; Author: Petteri Hintsanen 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify it 10 | ;; under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; EMMS is distributed in the hope that it will be useful, but WITHOUT 15 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 17 | ;; License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; see the file COPYING. If not, write to the Free 21 | ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 | ;; MA 02110-1301, USA. 23 | 24 | ;;; Code: 25 | 26 | (require 'emms-info-native-flac) 27 | (require 'ert) 28 | 29 | (defmacro emms-test-flac-make-data-func (name bytes) 30 | "Macro for defining test data generator. 31 | This macro defines a suitable function with NAME that outputs 32 | BYTES after FLAC signature. The function NAME can then be passed 33 | for `emms-info-native-flac--decode-meta-blocks'." 34 | `(defun ,name (offset end) 35 | (let ((bytes (concat "fLaC" ,bytes))) 36 | (erase-buffer) 37 | (insert (substring bytes offset end))))) 38 | 39 | (emms-test-flac-make-data-func emms-test-invalid-flac-block-length "\x01\xff\xff\xff\x00\x01\x02\x03") 40 | (emms-test-flac-make-data-func emms-test-invalid-flac-block-type "\x09\x00\x00\x00\x00\x01\x02\x03") 41 | (emms-test-flac-make-data-func emms-test-valid-flac-block "\x00\x00\x00\x08\x10\x11\x12\x13\x14\x15\x16\x17\x84\x00\x00\x04\x01\x02\x03\x04") 42 | 43 | (ert-deftest emms-test-flac-meta-blocks () 44 | (should-error (emms-info-native-flac--decode-meta-blocks 45 | #'emms-test-invalid-flac-block-length)) 46 | (should-error (emms-info-native-flac--decode-meta-blocks 47 | #'emms-test-invalid-flac-block-type)) 48 | (should (equal (emms-info-native-flac--decode-meta-blocks 49 | #'emms-test-valid-flac-block) 50 | (list "\x01\x02\x03\x04" 51 | "\x10\x11\x12\x13\x14\x15\x16\x17")))) 52 | 53 | (ert-deftest emms-test-flac-decode-duration () 54 | ;; The corresponding sample metadata bytes are [10 196 66 240 1 8 36 0]. 55 | (should (= (emms-info-native-flac--decode-duration 775818634391462912) 392))) 56 | 57 | ;;; emms-info-native-flac-tests.el ends here 58 | -------------------------------------------------------------------------------- /test/emms-info-native-mp3-tests.el: -------------------------------------------------------------------------------- 1 | ;;; emms-info-native-mp3-tests.el --- Test suite for emms-info-native-mp3 -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2023 Free Software Foundation, Inc. 4 | 5 | ;; Author: Petteri Hintsanen 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify it 10 | ;; under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; EMMS is distributed in the hope that it will be useful, but WITHOUT 15 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 17 | ;; License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; see the file COPYING. If not, write to the Free 21 | ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 | ;; MA 02110-1301, USA. 23 | 24 | ;;; Code: 25 | 26 | (require 'emms-info-native-mp3) 27 | (require 'ert) 28 | 29 | (ert-deftest emms-test-id3v2-valid-frame-id-p () 30 | (let ((emms-info-native-id3v2--version 2)) 31 | (should (emms-info-native-id3v2--valid-frame-id-p "A1B")) 32 | (should (not (emms-info-native-id3v2--valid-frame-id-p "~B1"))) 33 | (should (not (emms-info-native-id3v2--valid-frame-id-p "XX"))) 34 | (should (not (emms-info-native-id3v2--valid-frame-id-p "XXXX")))) 35 | (let ((emms-info-native-id3v2--version 3)) 36 | (should (emms-info-native-id3v2--valid-frame-id-p "ABC9")) 37 | (should (not (emms-info-native-id3v2--valid-frame-id-p "~BCD"))) 38 | (should (not (emms-info-native-id3v2--valid-frame-id-p "XXX"))) 39 | (should (not (emms-info-native-id3v2--valid-frame-id-p "XXXXX"))))) 40 | 41 | (ert-deftest emms-test-id3v2-checked-size () 42 | (should (= (emms-info-native-id3v2--checked-size 'tag [0 0 2 1]) 257)) 43 | (should (= (emms-info-native-id3v2--checked-size 'tag [1 1 1 1]) 2113665)) 44 | (should (= (emms-info-native-id3v2--checked-size 'tag [#xff #xff #xff #xff]) 45 | (1- (* 256 1024 1024)))) 46 | (should (= (emms-info-native-id3v2--checked-size 'tag [#x7f #x7f #x7f #x7f]) 47 | (1- (* 256 1024 1024)))) 48 | (should (= (emms-info-native-id3v2--checked-size 'tag [#x12 #x34 #x56 #x78]) 49 | 38611832)) 50 | (let ((emms-info-native-id3v2--version 4)) 51 | (should (= (emms-info-native-id3v2--checked-size 'frame [#xff #xff #xff #xff]) 52 | (1- (* 256 1024 1024))))) 53 | (let ((emms-info-native-id3v2--version 3)) 54 | (should (= (emms-info-native-id3v2--checked-size 'frame [#xff #xff #xff #xff]) 55 | (1- (* 4 1024 1024 1024)))))) 56 | 57 | (ert-deftest emms-test-id3v2-decode-size () 58 | (should (= (emms-info-native-id3v2--decode-size [01 01 01 01] nil) 59 | 16843009)) 60 | (should (= (emms-info-native-id3v2--decode-size [01 01 01 01] t) 61 | 2113665)) 62 | (should (= (emms-info-native-id3v2--decode-size [00 00 02 01] nil) 63 | 513)) 64 | (should (= (emms-info-native-id3v2--decode-size [00 00 02 01] t) 65 | 257))) 66 | 67 | (ert-deftest emms-test-mp3-find-and-decode-frame-header () 68 | (with-temp-buffer 69 | (set-buffer-multibyte nil) 70 | (insert "\x00\x00\x00\x00\x00\x00\x00\xff\xfb\xb0\x04\x00\x00\x00\x00\x00\x69\x06\x00\x00\x00\x00\x00\x0d\x20\xc0\x00\x00\x00\x00\x01\xa4\x1c\x00\x00\x00\x00\x00\x34\x83\x80\x00\x00\x4c\x41\x4d\x45\x33\x2e\x39\x31\x55\x55") 71 | (should (equal (emms-info-native-mp3--find-and-decode-frame-header) 72 | '((version . mpeg1) 73 | (layer . layerIII) 74 | (crc . 1) 75 | (bit-rate . 192) 76 | (sample-rate . 44100) 77 | (samples-per-frame . 1152) 78 | (padding . 0) 79 | (private . 0) 80 | (channel-mode . stereo) 81 | (mode-extension . 0) 82 | (copyright . 0) 83 | (emphasis . 0) 84 | (original . 1)))))) 85 | 86 | (ert-deftest emms-test-mp3-find-and-decode-xing-header () 87 | (with-temp-buffer 88 | (set-buffer-multibyte nil) 89 | (insert "\xff\xea\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x58\x69\x6e\x67\x00\x00\x00\x0f\x00\x00\x21\x59\x00\x50\x1d\x79\x00\x03\x06\x08\x0b\x0e\x0f\x12\x15\x17\x1a\x1d\x1f\x22\x25\x27\x2a\x2d\x2f\x32\x35\x37\x39\x3c\x3e\x41\x44\x46\x49\x4c\x4e\x51\x54\x56\x59\x5c\x5e\x61\x64\x65\x68\x6b\x6d\x70\x73\x75\x78\x7a\x7b\x7d\x7f\x82\x85\x87\x8a\x8d\x8f\x92\x95\x97\x9a\x9d\x9f\xa2\xa5\xa7\xaa\xad\xae\xb1\xb4\xb6\xb9\xbc\xbe\xc1\xc4\xc6\xc9\xcc\xce\xd1\xd4\xd6\xd9\xdc\xde\xe1\xe4\xe6\xe9\xec\xee\xf1\xf4\xf6\xf9\xfb\xfd\xff\x00\x00\x00\x58\x4c\x41\x4d\x45\x33\x2e\x38\x38\x20\x28\x61\x6c\x70\x68\x61\x29\x00\x00") 90 | (should (= (emms-info-native-mp3--find-and-decode-xing-header) 8537)))) 91 | 92 | (ert-deftest emms-test-mp3-find-decode-xing-header-2 () 93 | (with-temp-buffer 94 | (set-buffer-multibyte nil) 95 | (insert "\xff\xfb\x50\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x49\x6e\x66\x6f\x00\x00\x00\x0f\x00\x00\x23\xd8\x00\x3a\x86\x09\x00\x02\x05\x08\x0a\x0d\x10\x12\x14\x17\x19\x1c\x1f\x21\x24\x26\x29\x2b\x2e\x31\x33\x36\x38\x3a\x3d\x40\x42\x45\x48\x4a\x4c\x4f\x52\x54\x57\x5a\x5b\x5e\x61\x63\x66\x69\x6c\x6d\x70\x73\x75\x78\x7b\x7d\x80\x82\x85\x87\x8a\x8d\x8f\x92\x94\x96\x99\x9c\x9e\xa1\xa4\xa6\xa8\xab\xae\xb0\xb3\xb6\xb7\xba\xbd\xbf\xc2\xc5\xc7\xc9\xcc\xcf\xd1\xd4\xd7\xd9\xdb\xde\xe0\xe3\xe6\xe9\xeb\xed\xf0\xf2\xf5\xf8\xfa\xfd\x00\x00\x00\x00\x4c\x61\x76\x63\x35\x39\x2e\x33\x37\x00\x00") 96 | (should (= (emms-info-native-mp3--find-and-decode-xing-header) 9176)))) 97 | 98 | (ert-deftest emms-test-mp3-find-and-decode-vbri-header () 99 | (with-temp-buffer 100 | (set-buffer-multibyte nil) 101 | (insert "\xff\xfb\xa1\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x56\x42\x52\x49\x00\x01\x0d\xb1\x00\x64\x00\x62\xdb\x91\x00\x00\x21\x3a\x00\x84\x00\x01\x00\x02\x00\x40\x98\xb1\xbd\xa8\xbb\x36\xba\xce\xbb\x37\xba\xcf\xba\x67\xbb\x37\xbc\xd7\xbb\x9f\xba\xcf\xb9\x2c\xbb\x35\xbb\x38\xbc\x08\xbb\x9f\xb9\x95\xbe\xe0\xbc\x08\xb9\xfa\xba\x63\xb8\x5a\xb6") 102 | (should (= (emms-info-native-mp3--find-and-decode-vbri-header) 8506)))) 103 | 104 | ;;; emms-info-native-mp3-tests.el ends here 105 | -------------------------------------------------------------------------------- /test/emms-info-native-ogg-tests.el: -------------------------------------------------------------------------------- 1 | ;;; emms-info-ogg-tests.el --- Test suite for emms-info-ogg -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2023 Free Software Foundation, Inc. 4 | 5 | ;; Author: Petteri Hintsanen 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify it 10 | ;; under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; EMMS is distributed in the hope that it will be useful, but WITHOUT 15 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 17 | ;; License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; see the file COPYING. If not, write to the Free 21 | ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 | ;; MA 02110-1301, USA. 23 | 24 | ;;; Code: 25 | 26 | (require 'emms) 27 | (require 'emms-info-native-ogg) 28 | (require 'ert) 29 | 30 | (ert-deftest emms-test-ogg-decode-page () 31 | (let* ((bytes "\x4f\x67\x67\x53\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x86\xd1\x9e\x17\x00\x00\x00\x00\x35\x52\xfb\x88\x01\x1e\x01\x76\x6f\x72\x62\x69\x73\x00\x00\x00\x00\x01\x44\xac\x00\x00\x00\x00\x00\x00\x80\x38\x01\x00\x00\x00\x00\x00\xb8\x01") 32 | (page (bindat-unpack emms-info-native-ogg--page-bindat-spec bytes))) 33 | (should (= (emms-info-native-ogg--num-packets page) 1)) 34 | (should (= (bindat-length emms-info-native-ogg--page-bindat-spec page) 58)) 35 | (should (equal (bindat-get-field page 'payload) 36 | "\x01\x76\x6f\x72\x62\x69\x73\x00\x00\x00\x00\x01\x44\xac\x00\x00\x00\x00\x00\x00\x80\x38\x01\x00\x00\x00\x00\x00\xb8\x01")))) 37 | 38 | (ert-deftest emms-test-ogg-decode-vorbis-headers () 39 | "Test `emms-info-ogg--decode-headers' with Vorbis data." 40 | (let ((bytes "\x01\x76\x6f\x72\x62\x69\x73\x00\x00\x00\x00\x01\x44\xac\x00\x00\x00\x00\x00\x00\x80\x38\x01\x00\x00\x00\x00\x00\xb8\x01\x03\x76\x6f\x72\x62\x69\x73\x34\x00\x00\x00\x58\x69\x70\x68\x2e\x4f\x72\x67\x20\x6c\x69\x62\x56\x6f\x72\x62\x69\x73\x20\x49\x20\x32\x30\x32\x30\x30\x37\x30\x34\x20\x28\x52\x65\x64\x75\x63\x69\x6e\x67\x20\x45\x6e\x76\x69\x72\x6f\x6e\x6d\x65\x6e\x74\x29\x02\x00\x00\x00\x07\x00\x00\x00\x66\x6f\x6f\x3d\x62\x61\x72\x1b\x00\x00\x00\x4b\x65\x79\x3d\xce\x9f\xe1\xbd\x90\xcf\x87\xe1\xbd\xb6\x20\xce\xa4\xce\xb1\xe1\xbd\x90\xcf\x84\xe1\xbd\xb0\x01")) 41 | (should 42 | (emms-equal-lists 43 | (emms-info-native-ogg--decode-headers bytes 'vorbis) 44 | '((identification-header 45 | (packet-type . 1) 46 | (vorbis . "vorbis") 47 | (vorbis-version . 0) 48 | (channel-count . 1) 49 | (sample-rate . 44100) 50 | (bitrate-maximum . 0) 51 | (bitrate-nominal . 80000) 52 | (bitrate-minimum . 0) 53 | (blocksize . 184) 54 | (framing-flag . 1)) 55 | (comment-header 56 | (packet-type . 3) 57 | (vorbis . "vorbis") 58 | (vendor-length . 52) 59 | (vendor-string . "Xiph.Org libVorbis I 20200704 (Reducing Environment)") 60 | (user-comments-list-length . 2) 61 | (user-comments 62 | ((length . 7) 63 | (user-comment . "foo=bar")) 64 | ((length . 27) 65 | (user-comment . "Key=\316\237\341\275\220\317\207\341\275\266 \316\244\316\261\341\275\220\317\204\341\275\260"))) 66 | (framing-bit . 1))))))) 67 | 68 | (ert-deftest emms-test-ogg-decode-opus-headers () 69 | "Test `emms-info-ogg--decode-headers' with Opus data." 70 | (let ((bytes "\x4f\x70\x75\x73\x48\x65\x61\x64\x01\x01\x38\x01\x44\xac\x00\x00\x00\x00\x00\x4f\x70\x75\x73\x54\x61\x67\x73\x0d\x00\x00\x00\x6c\x69\x62\x6f\x70\x75\x73\x20\x31\x2e\x33\x2e\x31\x03\x00\x00\x00\x26\x00\x00\x00\x45\x4e\x43\x4f\x44\x45\x52\x3d\x6f\x70\x75\x73\x65\x6e\x63\x20\x66\x72\x6f\x6d\x20\x6f\x70\x75\x73\x2d\x74\x6f\x6f\x6c\x73\x20\x30\x2e\x31\x2e\x31\x30\x07\x00\x00\x00\x66\x6f\x6f\x3d\x62\x61\x72\x1b\x00\x00\x00\x4b\x65\x79\x3d\xce\x9f\xe1\xbd\x90\xcf\x87\xe1\xbd\xb6\x20\xce\xa4\xce\xb1\xe1\xbd\x90\xcf\x84\xe1\xbd\xb0")) 71 | (emms-equal-lists 72 | (emms-info-native-ogg--decode-headers bytes 'opus) 73 | '((identification-header 74 | (opus-head . "OpusHead") 75 | (opus-version . 1) 76 | (channel-count . 1) 77 | (pre-skip . 312) 78 | (sample-rate . 44100) 79 | (output-gain . 0) 80 | (channel-mapping-family . 0)) 81 | (comment-header 82 | (opus-tags . "OpusTags") 83 | (vendor-length . 13) 84 | (vendor-string . "libopus 1.3.1") 85 | (user-comments-list-length . 3) 86 | (user-comments 87 | ((length . 38) 88 | (user-comment . "ENCODER=opusenc from opus-tools 0.1.10")) 89 | ((length . 7) 90 | (user-comment . "foo=bar")) 91 | ((length . 27) 92 | (user-comment . "Key=\316\237\341\275\220\317\207\341\275\266 \316\244\316\261\341\275\220\317\204\341\275\260")))))))) 93 | 94 | (defun emms-test-ogg--decode-last-page (bytes) 95 | "Call `emms-info-ogg--decode-last-page' with BYTES input. 96 | 97 | This is a helper function for `emms-test-ogg-decode-last-page'." 98 | (with-temp-buffer 99 | (set-buffer-multibyte nil) 100 | (insert (concat bytes)) 101 | (emms-info-native-ogg--decode-last-page))) 102 | 103 | (ert-deftest emms-test-ogg-decode-last-page() 104 | (let ((valid "\x01\x02\x03\x04\x4f\x67\x67\x53\x00\x04\x00\x24\x08\x01\x00\x00\x00\x00\x9c\x39\x6e\x47\x40\x08\x00\x00\x19\x4e\xac\xa3\x01\x0a\x4f\x67\x67\x53\x31\x32\x33\x34\x35\x36") 105 | (notlast "\x01\x02\x03\x04\x4f\x67\x67\x53\x00\x00\x00\x24\x08\x01\x00\x00\x00\x00\x9c\x39\x6e\x47\x40\x08\x00\x00\x19\x4e\xac\xa3\x01\x0a\x4f\x67\x67\x53\x31\x32\x33\x34\x35\x36") 106 | (invalid "\x01\x02\x03\x04\x4f\x67\x67\x53\x00\x04\x00\x24\x08\x01\x00\x00\x00\x00\x9c\x39\x6e\x47\x40\x08\x00\x00\x01\x02\x03\x04\x01\x0a\x4f\x67\x67\x53\x31\x32\x33\x34\x35\x36") 107 | (valid-result 108 | (quote 109 | ((capture-pattern . "OggS") 110 | (stream-structure-version . 0) 111 | (header-type-flag . 4) 112 | (granule-position . 17310720) 113 | (stream-serial-number . 1198406044) 114 | (page-sequence-no . 2112) 115 | (page-checksum . 2745978393) 116 | (page-segments . 1) 117 | (segment-table . [10]) 118 | (payload . "OggS123456"))))) 119 | (unless (eval-when-compile (fboundp 'bindat-type)) 120 | (push (cons 'granule-position-bytes [0 36 8 1 0 0 0 0]) valid-result)) 121 | (should (emms-equal-lists (emms-test-ogg--decode-last-page valid) 122 | valid-result)) 123 | (should (equal (emms-test-ogg--decode-last-page notlast) nil)) 124 | (should (equal (emms-test-ogg--decode-last-page invalid) nil)))) 125 | 126 | (ert-deftest emms-test-ogg-calculate-checksum () 127 | (let ((bytes "\x01\x02\x03\x04\x4f\x67\x67\x53\x00\x04\x00\x24\x08\x01\x00\x00\x00\x00\x9c\x39\x6e\x47\x40\x08\x00\x00\x19\x4e\xac\xa3\x01\x0a\x4f\x67\x67\x53\x31\x32\x33\x34\x35\x36")) 128 | (should (= (emms-info-native-ogg--checksum bytes) 445885580)))) 129 | 130 | ;;; emms-info-native-ogg-tests.el ends here 131 | -------------------------------------------------------------------------------- /test/emms-info-native-tests.el: -------------------------------------------------------------------------------- 1 | ;;; emms-info-native-tests.el --- Test suite for emms-info-native -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2023 Free Software Foundation, Inc. 4 | 5 | ;; Author: Petteri Hintsanen 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify it 10 | ;; under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; EMMS is distributed in the hope that it will be useful, but WITHOUT 15 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 17 | ;; License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; see the file COPYING. If not, write to the Free 21 | ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 | ;; MA 02110-1301, USA. 23 | 24 | ;;; Commentary: 25 | 26 | ;; This test suite exercises `emms-info-native' with various input 27 | ;; files. 28 | 29 | ;;; Code: 30 | 31 | (require 'emms-info-native) 32 | (require 'ert) 33 | 34 | (ert-deftest emms-test-info-native-mp3 () 35 | (should (equal (emms-info-native--decode-info-fields 36 | "resources/sine.mp3") 37 | '(("year" . "2023") 38 | ("album" . "Test Data ☺") 39 | ("artist" . "EMMS project") 40 | ("title" . "440 Hz sine wave") 41 | ("playing-time" . 5))))) 42 | 43 | (ert-deftest emms-test-info-native-ogg () 44 | (should (equal (emms-info-native--decode-info-fields 45 | "resources/sine.ogg") 46 | '(("artist" . "EMMS project") 47 | ("date" . "2023-09-02") 48 | ("title" . "440 Hz sine wave") 49 | ("album" . "Test Data ☺") 50 | ("playing-time" . 5))))) 51 | 52 | (ert-deftest emms-test-info-native-flac () 53 | (should (equal (emms-info-native--decode-info-fields 54 | "resources/sine.flac") 55 | '(("artist" . "EMMS project") 56 | ("date" . "2023-09-02") 57 | ("title" . "440 Hz sine wave") 58 | ("album" . "Test Data ☺") 59 | ("playing-time" . 5))))) 60 | 61 | (ert-deftest emms-test-info-native-opus () 62 | (should (equal (emms-info-native--decode-info-fields 63 | "resources/sine.opus") 64 | '(("artist" . "EMMS project") 65 | ("date" . "2023-09-02") 66 | ("title" . "440 Hz sine wave") 67 | ("album" . "Test Data ☺") 68 | ("playing-time" . 5))))) 69 | 70 | ;;; emms-info-native-tests.el ends here 71 | -------------------------------------------------------------------------------- /test/emms-info-native-vorbis-tests.el: -------------------------------------------------------------------------------- 1 | ;;; emms-info-native-vorbis-tests.el --- Test suite for emms-info-native-vorbis -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2023 Free Software Foundation, Inc. 4 | 5 | ;; Author: Petteri Hintsanen 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify it 10 | ;; under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; EMMS is distributed in the hope that it will be useful, but WITHOUT 15 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 17 | ;; License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; see the file COPYING. If not, write to the Free 21 | ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 | ;; MA 02110-1301, USA. 23 | 24 | ;;; Code: 25 | 26 | (require 'emms-info-native-vorbis) 27 | (require 'ert) 28 | 29 | (ert-deftest emms-test-vorbis-extract-comments () 30 | (let ((comments 31 | (quote (((user-comment . "MUSICBRAINZ_RELEASEGROUPID=9b307293-d2e6-34a9-a289-161c5baf187f") 32 | (length . 63)) 33 | ((user-comment . "ORIGINALDATE=1997-03-31") 34 | (length . 23)) 35 | ((user-comment . "ORIGINALYEAR=1997") 36 | (length . 17)) 37 | ((user-comment . "RELEASETYPE=album") 38 | (length . 17)) 39 | ((user-comment . "BARCODE=769233004727") 40 | (length . 20)) 41 | ((user-comment . "ALBUM=A toda Cuba le gusta") 42 | (length . 26)))))) 43 | (should (equal (emms-info-native-vorbis-extract-comments comments) 44 | (quote (("album" . "A toda Cuba le gusta") 45 | ("originalyear" . "1997") 46 | ("originaldate" . "1997-03-31"))))))) 47 | 48 | (ert-deftest emms-test-vorbis-split-comment () 49 | (should (equal (emms-info-native-vorbis--split-comment "") nil)) 50 | (should (equal (emms-info-native-vorbis--split-comment "x") nil)) 51 | (should (equal (emms-info-native-vorbis--split-comment "x=") nil)) 52 | (should (equal (emms-info-native-vorbis--split-comment "=x") nil)) 53 | (should (equal (emms-info-native-vorbis--split-comment "a=B") 54 | (cons "a" "B"))) 55 | (should (equal (emms-info-native-vorbis--split-comment "abc=ABC=123") 56 | (cons "abc" "ABC=123"))) 57 | (let ((comment "Key=\316\237\341\275\220\317\207\341\275\266 \316\244\316\261\341\275\220\317\204\341\275\260")) 58 | (should (equal (emms-info-native-vorbis--split-comment comment) 59 | (cons "key" "Οὐχὶ Ταὐτὰ"))))) 60 | 61 | ;;; emms-info-native-vorbis-tests.el ends here 62 | -------------------------------------------------------------------------------- /test/emms-tests.el: -------------------------------------------------------------------------------- 1 | ;;; emms-tests.el --- Test suite for EMMS core -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2023 Free Software Foundation, Inc. 4 | 5 | ;; Author: Petteri Hintsanen 6 | 7 | ;; This file is part of EMMS. 8 | 9 | ;; EMMS is free software; you can redistribute it and/or modify it 10 | ;; under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation; either version 3, or (at your option) 12 | ;; any later version. 13 | 14 | ;; EMMS is distributed in the hope that it will be useful, but WITHOUT 15 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 17 | ;; License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with EMMS; see the file COPYING. If not, write to the Free 21 | ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 | ;; MA 02110-1301, USA. 23 | 24 | ;;; Code: 25 | 26 | (require 'emms) 27 | (require 'ert) 28 | 29 | (ert-deftest emms-test-le-to-int () 30 | (should (= (emms-le-to-int nil) 0)) 31 | (should (= (emms-le-to-int [0]) 0)) 32 | (should (= (emms-le-to-int [127]) 127)) 33 | (should (= (emms-le-to-int [255]) 255)) 34 | (should (= (emms-le-to-int [0 1]) 256)) 35 | (should (= (emms-le-to-int [1 0]) 1)) 36 | (should (= (emms-le-to-int [0 128]) 32768)) 37 | (should (= (emms-le-to-int [1 2 4 8]) 134480385))) 38 | 39 | (ert-deftest emms-test-from-twos-complement () 40 | (should (= (emms-from-twos-complement 0 8) 0)) 41 | (should (= (emms-from-twos-complement 1 8) 1)) 42 | (should (= (emms-from-twos-complement 127 8) 127)) 43 | (should (= (emms-from-twos-complement 128 8) -128)) 44 | (should (= (emms-from-twos-complement 129 8) -127)) 45 | (should (= (emms-from-twos-complement 254 8) -2)) 46 | (should (= (emms-from-twos-complement 255 8) -1)) 47 | (should (= (emms-from-twos-complement 0 10) 0)) 48 | (should (= (emms-from-twos-complement 511 10) 511)) 49 | (should (= (emms-from-twos-complement 512 10) -512)) 50 | (should (= (emms-from-twos-complement 1023 10) -1))) 51 | 52 | (ert-deftest emms-test-extract-bits () 53 | (should (= (emms-extract-bits 128 7) 1)) 54 | (should (= (emms-extract-bits 64 6 7) 1)) 55 | (should (= (emms-extract-bits 128 6 7) 2)) 56 | (should (= (emms-extract-bits 192 6 7) 3)) 57 | (should (eq (emms-extract-bits 192 7 6) nil)) 58 | (should (= (emms-extract-bits 128 32) 0)) 59 | (should (= (emms-extract-bits 4294688772 21 31) 2047))) 60 | 61 | ;;; emms-tests.el ends here 62 | -------------------------------------------------------------------------------- /test/resources/sine.flac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacsmirror/emms/53db2bf1f0475c9404f56d67ab75edf1fdf18dfe/test/resources/sine.flac -------------------------------------------------------------------------------- /test/resources/sine.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacsmirror/emms/53db2bf1f0475c9404f56d67ab75edf1fdf18dfe/test/resources/sine.mp3 -------------------------------------------------------------------------------- /test/resources/sine.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacsmirror/emms/53db2bf1f0475c9404f56d67ab75edf1fdf18dfe/test/resources/sine.ogg -------------------------------------------------------------------------------- /test/resources/sine.opus: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacsmirror/emms/53db2bf1f0475c9404f56d67ab75edf1fdf18dfe/test/resources/sine.opus --------------------------------------------------------------------------------