add emms
authorJoerg Jaspert <joerg@ganneff.de>
Fri, 5 Jul 2013 15:37:38 +0000 (17:37 +0200)
committerJoerg Jaspert <joerg@ganneff.de>
Fri, 5 Jul 2013 15:37:38 +0000 (17:37 +0200)
63 files changed:
.emacs.d/config/emacs.org
.emacs.d/elisp/emms/.gitignore [new file with mode: 0644]
.emacs.d/elisp/emms/AUTHORS [new file with mode: 0644]
.emacs.d/elisp/emms/COPYING [new file with mode: 0644]
.emacs.d/elisp/emms/FAQ [new file with mode: 0644]
.emacs.d/elisp/emms/Makefile [new file with mode: 0644]
.emacs.d/elisp/emms/NEWS [new file with mode: 0644]
.emacs.d/elisp/emms/README [new file with mode: 0644]
.emacs.d/elisp/emms/RELEASE [new file with mode: 0644]
.emacs.d/elisp/emms/doc/Makefile [new file with mode: 0644]
.emacs.d/elisp/emms/doc/emms.texinfo [new file with mode: 0644]
.emacs.d/elisp/emms/doc/fdl.texi [new file with mode: 0644]
.emacs.d/elisp/emms/doc/gpl.texi [new file with mode: 0644]
.emacs.d/elisp/emms/emms-print-metadata.1 [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/Makefile [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-auto.in [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-bookmarks.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-browser.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-cache.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-compat.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-cue.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-history.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-i18n.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-info-libtag.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-info-metaflac.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-info-mp3info.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-info-ogginfo.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-info.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-last-played.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-lastfm-client.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-lastfm-scrobbler.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-lyrics.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-maint.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-mark.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-metaplaylist-mode.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-mode-line-icon.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-mode-line.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-player-mpd.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-player-mpg321-remote.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-player-mplayer.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-player-simple.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-player-vlc.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-player-xine.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-playing-time.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-playlist-limit.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-playlist-mode.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-playlist-sort.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-score.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-setup.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-source-file.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-source-playlist.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-stream-info.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-streams.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-tag-editor.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-url.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-volume-amixer.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms-volume.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/emms.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/jack.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/later-do.el [new file with mode: 0644]
.emacs.d/elisp/emms/lisp/tq.el [new file with mode: 0644]
.emacs.d/elisp/emms/src/emms-print-metadata.c [new file with mode: 0644]
.emacs.d/elisp/emms/src/emms-print-metadata.pl [new file with mode: 0755]

index a5aaa5c..745cdf4 100644 (file)
@@ -92,7 +92,7 @@ one variable and go over that in a loop.
 #+BEGIN_SRC emacs-lisp tangle:yes
 (defvar jj-elisp-subdirs '(local gnus icicle org/contrib tiny mo-git-blame cedet
                            cedet/eieio ecb jdee/lisp sunrise multiple-cursors
-                           auto-complete yasnippet magit mmm)
+                           auto-complete yasnippet magit mmm emms/lisp)
   "List of subdirectories in jj-elisp-dir to add to load-path")
 
 (let (dirval)
@@ -2847,6 +2847,93 @@ Programming in ruby...
   (mmm-add-mode-ext-class mode "\\.js\\.erb\\'" 'erb))
 
 #+END_SRC
+** emms
+EMMS is the Emacs Multimedia System.
+https://github.com/Corsair/my-configs/blob/master/.emacs-emms.el
+#+BEGIN_SRC emacs-lisp tangle:yes
+(require 'emms-setup)
+(emms-standard)
+(require 'emms-player-mpd)
+(eval-after-load "emms"
+  '(progn
+
+     (setq emms-player-mpd-server-name "localhost")
+     (setq emms-player-mpd-server-port "6600")
+     (add-to-list 'emms-info-functions 'emms-info-mpd)
+     (setq emms-volume-change-function 'emms-volume-mpd-change)
+     (add-to-list 'emms-(point)layer-list 'emms-player-mpd)
+     (setq emms-last-played-format-(and )list
+      '(((emms-last-played-seconds-today) . "%a %H:%M")
+        (604800                           . "%a %H:%M") ; this week
+        ((emms-last-played-seconds-month) . "%d")
+        ((emms-last-played-seconds-year)  . "%m/%d")
+        (t                                . "%Y/%m/%d")))
+
+     (setq xwl-emms-playlist-last-track nil)
+     (setq xwl-emms-playlist-last-indent "\\")
+
+     (defun xwl-emms-track-description-function (track)
+       "Return a description of the current track."
+       (let* ((name (emms-track-name track))
+              (type (emms-track-type track))
+              (short-name (file-name-nondirectory name))
+              (play-count (or (emms-track-get track 'play-count) 0))
+              (last-played (or (emms-track-get track 'last-played) '(0 0 0)))
+              (empty "..."))
+         (prog1
+             (case (emms-track-type track)
+               ((file url)
+                (let* ((artist (or (emms-track-get track 'info-artist) empty))
+                       (year (emms-track-get track 'info-year))
+                       (playing-time (or (emms-track-get track 'info-playing-time) 0))
+                       (min (/ playing-time 60))
+                       (sec (% playing-time 60))
+                       (album (or (emms-track-get track 'info-album) empty))
+                       (tracknumber (emms-track-get track 'info-tracknumber))
+                       (short-name (file-name-sans-extension
+                                    (file-name-nondirectory name)))
+                       (title (or (emms-track-get track 'info-title) short-name))
+
+                       ;; last track
+                       (ltrack xwl-emms-playlist-last-track)
+                       (lartist (or (and ltrack (emms-track-get ltrack 'info-artist))
+                                    empty))
+                       (lalbum (or (and ltrack (emms-track-get ltrack 'info-album))
+                                   empty))
+
+                       (same-album-p (and (not (string= lalbum empty))
+                                          (string= album lalbum))))
+                  (format "%10s  %3d   %-20s%6s %-55s%-35s%-15s%s"
+                          (emms-last-played-format-date last-played)
+                          play-count
+                          artist
+                          (if (and tracknumber ; tracknumber
+                                     (not (zerop (string-to-number tracknumber))))
+                                (format "%02d." (string-to-number tracknumber))
+                              "")
+                          title
+
+                          ;; album
+                          (cond ((string= album empty) empty)
+                                ;; (same-album-p "  ")
+                                (t (concat "«" album "»")))
+
+                          (or year empty)
+                          (if (or (> min 0)  (> sec 0))
+                              (format "%02d:%02d" min sec)
+                            empty))))
+               ((url)
+                (concat (symbol-name type) ":" name))
+               (t
+                (format "%-3d%s"
+                        play-count
+                        (concat (symbol-name type) ":" name))))
+           (setq xwl-emms-playlist-last-track track))))
+
+     (setq emms-track-description-function
+           'xwl-emms-track-description-function)
+     ))
+#+END_SRC
 * Thats it
 And thats it for this file, control passes "back" to [[file:../initjj.org][initjj.org/el]]
 which then may load more files.
diff --git a/.emacs.d/elisp/emms/.gitignore b/.emacs.d/elisp/emms/.gitignore
new file mode 100644 (file)
index 0000000..1590b35
--- /dev/null
@@ -0,0 +1,9 @@
+# Files that we ignore when using git.
+
+*~
+*.elc
+/ChangeLog
+/src/emms-print-metadata
+/doc/emms.info
+/doc/emms.html
+/lisp/emms-auto.el
diff --git a/.emacs.d/elisp/emms/AUTHORS b/.emacs.d/elisp/emms/AUTHORS
new file mode 100644 (file)
index 0000000..50e4c0f
--- /dev/null
@@ -0,0 +1,28 @@
+This file lists all people who contributed more than a few lines to
+emms.  This is necessary to keep track of people who have copyright
+claims on sources, so please don't be too humble and add yourself.
+
+Damien Elmes <emacs@repose.cx>
+Daniel Brockman <daniel@brockman.se>
+Jean-Philippe Theberge <jphiltheberge@videotron.ca>
+Jorgen Schaefer <forcer@forcix.cx>
+Lawrence Mitchell <wence@gmx.li>
+Lucas Bonnet <lucas@rincevent.net>
+Mario Domgörgen <kanaldrache@gmx.de>
+Mario Lang <mlang@delYsid.org>
+Martin Schoenmakers <aiviru@diamond-age.net>
+Matthew Kennedy <mkennedy@gentoo.org>
+Michael Olson <mwolson@gnu.org>
+Tassilo Horn <tassilo@member.fsf.org>
+Trent Buck <trentbuck@gmail.com>
+Ulrik Jensen <terryp@daimi.au.dk>
+William Xu <william.xwl@gmail.com>
+Ye Wenbin <wenbinye@gmail.com>
+Yoni Rabkin Katzenell <yoni-r@actcom.com>
+Jesse Weinstein <jessw@netwood.net>
+David Engster <deng@randomsample.de>
+Bram van der Kroef <bram@fortfrances.com>
+
+;; Local variables:
+;; coding: utf-8
+;; End:
diff --git a/.emacs.d/elisp/emms/COPYING b/.emacs.d/elisp/emms/COPYING
new file mode 100644 (file)
index 0000000..94a9ed0
--- /dev/null
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/.emacs.d/elisp/emms/FAQ b/.emacs.d/elisp/emms/FAQ
new file mode 100644 (file)
index 0000000..80324d9
--- /dev/null
@@ -0,0 +1,18 @@
+Frequently Asked Questions about emms.  Please read this before
+submitting a bug report.
+
+Q: I seem unable to play files with accents in them.  Why?
+A: Emacs doesn't know the coding system of your files, and it
+   apparently decodes them the wrong way.  Set
+   `default-file-name-coding-system' to the correct encoding of your
+   file names.  It might even work to set it to 'undecided and let
+   Emacs guess.
+
+Q: Emms skips some songs in the playlist for no apparent reason.  When
+   I select them manually, everything works.  Why?
+A: Increase `emms-player-delay' until it works.  The problem is that
+   emms is told by Emacs that a player finished, so it starts a new
+   one.  But in reality, the player has not yet freed the audio
+   device, so the next player gets an error when trying to play.  The
+   best way to fix this is by using ALSA or other sound systems which
+   allow concurrent access.
diff --git a/.emacs.d/elisp/emms/Makefile b/.emacs.d/elisp/emms/Makefile
new file mode 100644 (file)
index 0000000..b64b25c
--- /dev/null
@@ -0,0 +1,82 @@
+GZIP=gzip
+MAN1PAGES=emms-print-metadata.1
+DOCDIR=doc/
+LISPDIR=lisp
+SRCDIR=src
+
+ALLSOURCE=$(wildcard $(LISPDIR)/*.el)
+ALLCOMPILED=$(wildcard $(LISPDIR)/*.elc)
+
+DESTDIR=
+PREFIX=$(DESTDIR)/usr/local
+INFODIR=$(PREFIX)/info
+MAN1DIR=$(PREFIX)/share/man/man1
+SITELISP=$(PREFIX)/share/emacs/site-lisp/emms
+
+INSTALLINFO = /usr/sbin/install-info --info-dir=$(INFODIR)
+CHANGELOG_CMD = git log --pretty=medium --no-merges
+
+# The currently released version of EMMS
+VERSION=3.0
+
+.PHONY: all install lisp docs deb-install clean
+.PRECIOUS: %.elc
+all: lisp docs
+
+autoloads:
+       $(MAKE) -C $(LISPDIR) emms-auto.el
+
+lisp:
+       $(MAKE) -C $(LISPDIR)
+
+docs:
+       $(MAKE) -C $(DOCDIR)
+
+emms-print-metadata: $(SRCDIR)/emms-print-metadata.c
+       $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $(SRCDIR)/$@ $< `taglib-config --cflags --libs` -ltag_c
+
+install:
+       test -d $(SITELISP) || mkdir -p $(SITELISP)
+       test -d $(INFODIR) || install -d $(INFODIR)
+       install -m 644 $(ALLSOURCE) $(SITELISP)
+       install -m 644 $(ALLCOMPILED) $(SITELISP)
+       install -m 0644 $(DOCDIR)emms.info $(INFODIR)/emms
+       for p in $(MAN1PAGES) ; do $(GZIP) -9c $$p > $(MAN1DIR)/$$p.gz ; done
+       $(INSTALLINFO) emms.info
+
+remove-info:
+       $(INSTALLINFO) --remove emms.info
+
+ChangeLog:
+       $(CHANGELOG_CMD) > $@
+
+clean:
+       -rm -f *~ $(DOCDIR)emms.info $(DOCDIR)emms.html $(SRCDIR)/emms-print-metadata
+       $(MAKE) -C $(LISPDIR) clean
+
+dist: clean autoloads
+       git archive --format=tar --prefix=emms-$(VERSION)/ HEAD | \
+         (cd .. && tar xf -)
+       rm -f ../emms-$(VERSION)/.gitignore
+       cp lisp/emms-auto.el ../emms-$(VERSION)/lisp
+       $(CHANGELOG_CMD) > ../emms-$(VERSION)/ChangeLog
+
+release: dist
+       (cd .. && tar -czf emms-$(VERSION).tar.gz \
+           emms-$(VERSION) ; \
+         zip -r emms-$(VERSION).zip emms-$(VERSION) && \
+         gpg --detach emms-$(VERSION).tar.gz && \
+         gpg --detach emms-$(VERSION).zip)
+
+upload:
+       (cd .. && echo "Directory: emms" | gpg --clearsign > \
+           emms-$(VERSION).tar.gz.directive.asc && \
+         cp emms-$(VERSION).tar.gz.directive.asc \
+           emms-$(VERSION).zip.directive.asc && \
+         echo open ftp://ftp-upload.gnu.org > upload.lftp ; \
+         echo cd /incoming/ftp >> upload.lftp ; \
+         echo mput emms-$(VERSION).zip* >> upload.lftp ; \
+         echo mput emms-$(VERSION).tar.gz* >> upload.lftp ; \
+         echo close >> upload.lftp ; \
+         lftp -f upload.lftp ; \
+         rm -f upload.lftp)
diff --git a/.emacs.d/elisp/emms/NEWS b/.emacs.d/elisp/emms/NEWS
new file mode 100644 (file)
index 0000000..86f6971
--- /dev/null
@@ -0,0 +1,69 @@
+News since version 3.0:
+
+  - Drop support for emms-player-gstreamer.
+  - emms-player-mplayer: Remove code for loading movie subtitles
+    automatically by emms, since mplayer has an option
+    (`sub-fuzziness') controlling that.
+  - Support displaying lyrics in a dedicated buffer, to highlight playing lyric. 
+
+News since version 2.1:
+
+  - Saving bookmarks in individual tracks is now supported.
+  - Support deleting files from the cache.
+  - Add mms:// URL support for emms-player-mplayer.
+  - Implement pause, resume, and seek support for
+    emms-player-alsaplayer.
+  - A new player called emms-player-timidity has been implemented
+    which is capable of playing midi files.
+  - A new player called emms-player-fluidsynth is also capable of
+    playing midi files.
+  - A new player called emms-player-xine (in emms-player-xine.el) is
+    capable of using Xine to play tracks.
+  - Hitting "<" or ">" in emms-playlist-mode causes seeking backward
+    and forward, respectively.
+  - New file jack.el allows jackd to be started from within emacs, and
+    permits clients to be connected or disconnected.
+  - New file emms-tag-editor.el is capable of editing track
+    information in songs.
+  - New file emms-mark.el allows tracks in a playlist to be marked and
+    operated on, much like dired mode.
+  - New file emms-lastfm.el implements submitting track information to
+    Last.fm and playing Last.fm radio streams.  Support for playing
+    these streams has been added to emms-streams.el.
+  - New file emms-i18n.el adds support for handling coding systems
+    properly in process input/output.  This is currently only used by
+    emms-info-mp3info.el.
+  - New file emms-history.el causes playlists to be saved
+    automatically when exiting Emacs.
+  - emms-player-mpd:
+    - Using the emms-browser to insert and play tracks now works
+      properly with emms-player-mpd.
+    - Display the correct error message if an error occurs while
+      trying to play a stream.
+    - Handle any encoding errors that occur during playback.
+    - Integrate with emms-lastfm.el in `emms-player-mpd-show', if
+      emms-lastfm is loaded and active.
+  - emms-playing-time supports different display styles now.
+  - emms-lyrics: Now support lyrics auto-scrolling.
+  - emms-player-mplayer: Capable of loading movie subtitles
+    automatically now.
+
+News since version 2.0:
+
+  - A new player that uses mpg321's remote mode is now available
+    (emms-player-mpg321-remote)--this allows seeking and copes with
+    errors in files.
+  - A metadata browser has been added in emms-browser.el.
+  - Recording of the time a track was last played is now supported.
+  - emms-play-* and emms-add-* functions now toggle their play/add
+    behavior when a prefix argument is supplied.
+  - EMMS now caches tracks, which speeds up loading of large
+    collections with info tags considerably.
+  - EMMS now knows how to change the volume (emms-volume.el).
+  - When playing a playlist from emms-playlist-mode, EMMS now
+    optionally opens a new buffer for this playlist.
+
+User-visible bug fixes:
+
+  - The emms-playlist-mode is now much faster.
+  - MP3s with errors are now playable when using the remote player.
diff --git a/.emacs.d/elisp/emms/README b/.emacs.d/elisp/emms/README
new file mode 100644 (file)
index 0000000..24a6bba
--- /dev/null
@@ -0,0 +1,203 @@
+EMMS --- The Emacs Multi-Media System                -*-outline-*-
+=====================================
+
+
+* Introduction, Overview
+========================
+
+EMMS is the Emacs Multi-Media System.  It tries to be a clean and
+small application to play multimedia files from Emacs using external
+players.  Many of its ideas are derived from MpthreePlayer
+(http://www.nongnu.org/mp3player), but it tries to be more general and
+more clean.
+
+   The basic functionality of Emms consists of three parts: The core,
+the sources, and the players.
+
+   The core resides in `emms.el', and provides a simple playlist and
+the basic functionality to use all the other features of Emms.  It
+provides the common user commands and interfaces for other parts.  It
+thinks in tracks, where a track is the combination of a type and a
+name--e.g., the track type 'file has a name that is the file name.
+Other track types are possible.
+
+   To get to tracks, the core needs sources.  The file
+`emms-source-file.el' provides simple sources to interact with the
+file system.
+
+   When Emms finally has the sources in the playlist, it needs a
+player to play them.  `emms-player-simple.el' defines a few useful
+players, and allows you to define your own in a very simple way.
+
+   The way Emms works is easy to customize with your own code or by
+using `M-x customize RET'.
+
+
+* Installation
+==============
+
+You need to put all the .el files of EMMS in a directory in your
+load-path.  For example, if you put all those files into
+~/elisp/emms/, then in your ~/.emacs you should do:
+
+(add-to-list 'load-path "~/elisp/emms/")
+
+For information about compiling Emms into byte-code see the
+``Compiling Emms'' section in the Emms manual.
+
+** Setup
+--------
+After adding the location of the Emms code to the load-path variable,
+we invoke the following using the `emms-setup' feature which allows
+for quick and simple Emms setup.
+
+     (require 'emms-setup)
+     (emms-standard)
+     (emms-default-players)
+
+After which Emms is set up and ready to go!
+
+   For more information about different setup levels and features see
+the ``Simple Setup'' section of the Emms manual.
+
+** Usage
+--------
+The basic functionality of EMMS is just to play music without being
+noticed.  It provides a few commands to skip the current track and
+such, but otherwise, it doesn't show up.  EMMS provides the following
+basic user commands (that you might want to bind to keys):
+
+emms-start ...... Start playing the current playlist
+emms-stop ....... Stop playing
+emms-next ....... Go to the next track in the playlist
+emms-previous ... Go to the previous track in the playlist
+emms-shuffle .... Shuffle the playlist
+emms-show ....... What is playing?
+
+But before you can use these, you need a playlist to start with.  The
+following commands allow you to create a playlist from different
+sources:
+
+emms-play-file ............. Play a single file
+emms-play-directory ........ Play a whole directory
+emms-play-directory-tree ... Play a directory tree
+
+
+* The Interactive Playlist buffer
+=================================
+
+Emms provides a visual, interactive playlist mode as well as the
+ability to use playlists without ever looking at them.  This visual,
+interactive mode is called the `emms-playlist-mode' and is defined in
+`emms-playlist-mode.el'.
+
+   To use the interactive playlist invoke: `M-x emms-playlist-mode-go
+RET'
+
+   When in the interactive playlist mode we can perform different
+actions on the current playlist.  Here are some basic commands:
+
+`n'....Start playing the next track in the playlist.
+
+`p'....Start playing the previous track in the playlist.
+
+`s'....Stop playing.
+
+`f'....Describe the currently playing track in the minibuffer.
+
+`c'....Display the current track in the center of the screen.
+
+`RET'..Start playing the track under point.  Note that this is also
+       available with `Mouse-2'.
+
+`q'....Put the interactive playlist buffer at the end of the list of
+       all buffers (i.e., bury it).
+
+As always, for more commands see the mode documentation and the
+``Interactive Playlists'' section of the Emms manual.
+
+
+* Bare Bones Setup
+==================
+
+The following code fragment provides a minimal EMMS setup without
+using the layer of `emms-default'.  It can maybe be used to better
+understand the internals of EMMS.  You can see how EMMS needs to know
+about players (these are defined in `emms-player-simple') and about
+sources for tracks (trivial file system based sources, such as this
+`emms-directory-tree', are defined in `emms-source-file').
+
+(require 'emms-player-simple)
+(require 'emms-source-file)
+(require 'emms-source-playlist)
+(setq emms-player-list '(emms-player-mpg321
+                         emms-player-ogg123
+                         emms-player-mplayer))
+
+
+* Advanced configuration
+========================
+
+** Seeking
+----------
+In most multimedia players, you can seek forward or backward in a
+track.  EMMS supports this too.  If you're using mplayer, check that
+`emms-player-mplayer-parameters' contains ``slave''.  If you're using
+mpg321, there is a module called emms-player-mpg321-remote.el.  To use
+it, simply add the following lines to your configuration:
+
+(require 'emms-player-mpg321-remote)
+(push 'emms-player-mpg321-remote emms-player-list)
+
+Finally, if you are using mpd, no special config is needed.
+
+   Seeking works through the following functions:
+
+`emms-seek' which takes a negative or positive amount of seconds.
+`emms-seek-forward' which seeks ten seconds forward.
+`emms-seek-backward' which seeks ten seconds backward.
+
+
+* Using libtag for reading tags
+===============================
+
+There is a way to read tags using Libtag as your backend for
+emms-info.  However, since it requires a binary file (source code
+provided with EMMS), it isn't enabled by default.  To compile it, type
+``make emms-print-metadata''.  To install it, either put
+emms-print-metadata in your $PATH, or add EMMS' directory to Emacs'
+exec-path.  Since libtag overwrites most of the usual methods for
+providing info, it's best to keep it as the only function in
+`emms-info-functions'.  Here's a way to do so:
+
+(require 'emms-info-libtag)
+(setq emms-info-functions '(emms-info-libtag))
+
+
+* EMMS, Emms, emms, or what?
+============================
+
+In various contexts, this program is called EMMS, Emms or emms.  Those
+are all correct, and which one you use is a matter of personal
+preference.  EMMS highlights the acronym character of the name.  Emms
+is akin to Emacs and Gnus, ignoring that Emms is pronounced
+ee-em-em-es, and not a single name.  emms is highlighting that emms is
+a case-sensitive file name and Emacs Lisp command.
+
+
+* Getting help
+==============
+
+Emms has a mailing list at emms-help@gnu.org.  To subscribe to it,
+visit http://lists.gnu.org/mailman/listinfo/emms-help.  If you are
+familiar with the Gmane service, there is a Gmane newsgroup which
+mirrors this mailing address at gmane.emacs.emms.user.
+
+   Emms also has a website at <http://www.gnu.org/software/emms/>.
+
+
+* License
+=========
+
+EMMS is available under the terms of the GNU General Public License.
+Please see the file COPYING for details.
diff --git a/.emacs.d/elisp/emms/RELEASE b/.emacs.d/elisp/emms/RELEASE
new file mode 100644 (file)
index 0000000..55348e0
--- /dev/null
@@ -0,0 +1,8 @@
+This file tries to list the things people have to do before they do a
+release.
+
+1) Increase the version number in emms.el
+2) Update NEWS
+3) Run make release
+4) Run make upload
+5) Send announcement email
diff --git a/.emacs.d/elisp/emms/doc/Makefile b/.emacs.d/elisp/emms/doc/Makefile
new file mode 100644 (file)
index 0000000..cccdff3
--- /dev/null
@@ -0,0 +1,10 @@
+# Don't delete if make is interrupted
+.PRECIOUS: %.info %.html
+
+all: emms.info
+
+%.info: %.texinfo
+       makeinfo --no-split $<
+
+%.html: %.texinfo
+       makeinfo --html --no-split $<
diff --git a/.emacs.d/elisp/emms/doc/emms.texinfo b/.emacs.d/elisp/emms/doc/emms.texinfo
new file mode 100644 (file)
index 0000000..692ec8a
--- /dev/null
@@ -0,0 +1,2491 @@
+\input texinfo   @c -*-texinfo-*-
+@c %**start of header
+@setfilename emms.info
+@settitle The Emms Manual
+@c %**end of header
+
+@c History: The Emms manual was almost entirely rewritten for the
+@c release of Emms version 2.
+
+@c As a rule, modules which are stable enough to be included into the
+@c `emms-all' setup level should be documented. That is, any feature
+@c which is considered stable should be included.
+
+@dircategory Emacs
+@direntry
+* Emms: (emms).           The Emacs Multimedia System
+@end direntry
+
+@copying
+ @copyright{} 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
+   Free Software Foundation, Inc.
+@quotation
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.1 or
+any later version published by the Free Software Foundation; with no
+Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A
+copy of the license is included in the section entitled "GNU Free
+Documentation License".
+@end quotation
+@end copying
+
+@c For printed material
+@titlepage
+@title The Emms Manual
+
+@page
+@vskip 0pt plus 1filll
+@insertcopying
+@end titlepage
+
+@contents
+@c END For printed material
+
+@ifnottex
+@node Top, Introduction, (dir), (dir)
+@top Emms Manual
+
+This is the Manual for the Emacs Multimedia System.
+
+@insertcopying
+
+@menu
+Starting out
+* Introduction::        Introduction to Emms.
+* Installation::        How to install Emms on your System.
+* Simple Setup::        Quick, basic default Emms setup.
+* Configuration::       More detailed setup and configuration.
+* Quickstart Guide::    First steps with EMMS for new users.
+* Getting Help::        Where to get help with Emms and make suggestions.
+* Formats and Freedom:: File formats without restrictions.
+
+Emms basics
+* Basic Commands::      How to control Emms with ease.
+* The Core File::       The inner core of Emms.
+* Sources::             Sources for playlists-creation.
+* Simple Players::      Some simple players.
+* Playlists::           How Emms organizes media.
+
+Advanced Features
+* Track Information::        More narrative track descriptions.
+* Interactive Playlists::    Interactive Playlists.
+* Markable Playlists::       Allow tracks to be marked.
+
+Modules and Extensions
+* The Browser::          Advanced metadata browsing.
+* Sorting Playlists::    Sorting the order of the tracks.
+* Persistent Playlists:: Restoring playlists on emacs startup.
+* Editing Tracks::       Editing track information from within Emms.
+* Emms Mode Line::       Emms information on the mode line.
+* Music Player Daemon::  Interface to Music Player Daemon.
+* Streaming Audio::      Interface to streaming audio.
+* Lyrics::               Displaying lyrics synchronously.
+* Volume::               Changing the volume.
+* Last.fm::              Interact with the Last.fm service.
+* APE / FLAC Commands::  How to play next or previous track in these files.
+* Bookmarks::            Saving a place in a media file.
+* Extending Emms::       How to define new players and modules.
+
+Copying and license
+* Copying::             The GNU General Public License gives you permission to
+                            redistribute Emms on certain terms; it also explains
+                            that there is no warranty.
+* The GNU FDL::         The license for this documentation.
+
+Indices
+* Concept Index::
+* Function Index::
+* Variable Index::
+* Keybinding Index::
+
+@detailmenu
+--- The Detailed Node Listing ---
+
+Here are some other nodes which are really inferiors of the ones
+already listed, mentioned here so you can get to them in one step:
+
+Installation
+* Compiling Emms::      Compiling Emms into Byte-Code.
+
+The Core File
+* User Variables::     Variables for the user to tweak.
+* Hooks::              Hooks for important Emms functions.
+* Core Functions::     Providing the basic functionality of Emms.
+
+Track Information
+* Defining Info Methods::    Defining new info methods.
+
+Last.fm
+* Last.fm Setup::            Configuring Emms to use Last.fm.
+* Last.fm Radio::            Listening to music through Last.fm.
+* Last.fm Audioscrobbler::   Submitting music to Last.fm
+
+Extending Emms
+* New Player::               How to define a new player.
+* Simple Player for `play':: Example player using @command{play}.
+* More Complex Player::      Example of a complex player using @command{mpg321}.
+@end detailmenu
+@end menu
+
+@end ifnottex
+
+
+@node Introduction
+@chapter Introduction
+
+@cindex introduction
+
+Emms is the Emacs Multi-Media System. It tries to be a clean and small
+application to play multimedia files from Emacs using external
+players. Many of its ideas are derived from
+@uref{http://www.nongnu.org/mp3player, MpthreePlayer}, but it tries to
+be more general and more clean.
+
+This manual tries to be the definitive source of information about
+Emms, an online version of the manual is available at:
+@uref{http://www.gnu.org/software/emms/manual/}.
+
+The basic functionality of Emms consists of three parts: The core, the
+sources, and the players.
+
+The core resides in @file{emms.el}, and provides a simple playlist and the
+basic functionality to use all the other features of Emms. It provides
+the common user commands and interfaces for other parts. It thinks in
+tracks, where a track is the combination of a type and a name - e.g.
+the track type 'file has a name that is the file name. Other track
+types are possible.
+
+To get to tracks, the core needs sources. The file @file{emms-source-file.el}
+provides simple sources to interact with the file system.
+
+When Emms finally has the sources in the playlist, it needs a player
+to play them. @file{emms-player-simple.el} defines a few useful players, and
+allows you to define your own in a very simple way.
+
+The way Emms works is easy to customize with your own code or by using
+`M-x customize'.
+
+@node Installation
+@chapter Installation
+
+@cindex installation
+
+You need to put all the .el files of emms in a directory in your
+load-path. For example, if you put all those files into ~/elisp/emms/,
+then in your ~/.emacs, you should do:
+
+@lisp
+(add-to-list 'load-path "~/elisp/emms/lisp/")
+@end lisp
+@noindent
+
+@menu
+* Compiling Emms::      Compiling Emms into Byte-Code.
+@end menu
+
+@node Compiling Emms
+@section Compiling Emms
+
+@cindex compiling
+
+If you are using XEmacs, you will need to edit @file{Makefile} as
+follows before continuing.
+
+@example
+EMACS=xemacs
+SITEFLAG=-no-site-file
+@end example
+
+You can byte-compile Emms by first entering the directory containing the
+Emms source code, followed by invoking:
+
+@command{make}
+
+Which will byte compile Emms. You can then invoke:
+
+@command{make install}
+
+Which will install Emms into your Emacs directories (provided you have
+the appropriate permissions to do so on your system).
+
+Note that Emms is a light-weight and agile program, you can therefore
+run Emms just fine without byte compiling it.
+
+@node Simple Setup
+@chapter Simple Setup
+
+@cindex simple setup
+@cindex setting up Emms
+@cindex quick setup
+
+After adding the location of the Emms code to the @var{load-path}
+variable, see @xref{Installation}. We invoke the following using the
+`emms-setup' feature which allows for quick and simple Emms setup.
+
+@lisp
+(require 'emms-setup)
+(emms-standard)
+(emms-default-players)
+@end lisp
+
+After which Emms is set-up and ready to go!
+
+The above will setup Emms with standard features (interactive
+playlists, audio track tag reading etc.) and a default list of players
+(ogg, mp3, mplayer etc.).
+
+The `emms-setup' feature is provided by the file
+@file{emms-setup.el}. It is essentially a collection of shortcuts for
+setting up Emms quickly and simply. Everything you can do with
+`emms-setup' can also be done manually.
+
+We use `emms-setup' by calling one of the setup functions. Each of the
+functions sets up Emms to include a number of features.
+
+@defun emms-minimalistic
+An Emms setup script.
+Invisible playlists and all the basics for playing media.
+@end defun
+
+@defun emms-standard
+An Emms setup script.
+Everything included in the @code{emms-minimalistic} setup, the Emms
+interactive playlist mode and reading information from tagged
+audio files.
+@end defun
+
+@defun emms-all
+An Emms setup script.
+Everything included in the @code{emms-standard} setup and adds all the
+stable features which come with the Emms distribution.
+@end defun
+
+@defun emms-devel
+An Emms setup script.
+Everything included in the @code{emms-all} setup and adds all of the
+features which come with the Emms distribution regardless of if they
+are considered stable or not.  Use this if you like living on the
+edge.
+@end defun
+
+`emms-setup' also comes with a convenience function to set a default
+list of media players.
+
+@defun emms-default-players
+Set @var{emms-player-list} to @var{emms-setup-default-player-list}.
+@end defun
+
+It is also worth noting that you can write your own Emms setup
+functions like the above by looking at the existing function
+definitions in @file{emms-setup.el}.
+
+@node Configuration
+@chapter Configuration
+
+@cindex Configuration
+
+This chapter discusses the configuration of Emms in more detail.
+
+The following code fragment provides a minimal Emms setup without
+using the layer of `emms-setup'. It can maybe be used to better
+understand the internals of Emms. You can see how Emms needs to know
+about players (these are defined in `emms-player-simple') and about
+sources for tracks (trivial file system based sources, such as this
+`emms-directory-tree', are defined in `emms-source-file').
+
+@lisp
+(require 'emms-player-simple)
+(require 'emms-source-file)
+(require 'emms-source-playlist)
+(setq emms-player-list '(emms-player-mpg321
+                         emms-player-ogg123
+                         emms-player-mplayer))
+@end lisp
+
+For a discussion on how to define additional players, see @xref{Simple
+Players}.
+
+Much of the behaviour of Emms can be changed by setting
+variables. For example:
+
+@lisp
+(setq emms-info-asynchronously nil)
+(setq emms-playlist-buffer-name "*Music*")
+@end lisp
+
+The first @code{setq} turns off the asynchronous updating of info tags. The
+second sets the default name of the Emms playlist buffer.
+
+Another way to change Emms variables is to use the M-x
+@command{customize} mechanism provided by Emacs.
+
+@menu
+* Finding files and speed::     Finding files quickly or portably.
+@end menu
+
+@node Finding files and speed
+@section Finding files and speed
+
+@cindex files
+@cindex speed
+
+Emms needs to traverse directories in order to find playable
+media. The default method Emms uses to achive this is
+@code{emms-source-file-directory-tree-internal} as defined in
+@file{emms-source-file.el}.  The above method is written portably and
+will always work, but might be too slow if we want to load several
+hundred tracks (or more).
+
+@file{emms-source-file.el} defines another method for finding files,
+@code{emms-source-file-directory-tree-find} which uses
+GNU/find. @code{emms-source-file-directory-tree-find} is usually an
+order of magnitude faster, but of course will not work if you do not
+have GNU/find installed.
+
+The method Emms will use is defined in the customisable variable
+@var{emms-source-file-directory-tree-function}.
+
+@node Quickstart Guide
+@chapter Quickstart Guide
+
+This chapter demonstrates how to setup EMMS so that you can start
+listening to your music without having to read all of the documentation
+first.
+
+The first thing you have to do is telling Emacs where the sources of
+EMMS are located. Let's say you have them in @file{~/elisp/emms/}. So
+add this line to your @file{.emacs}.
+
+@lisp
+(add-to-list 'load-path "~/elisp/emms/lisp/")
+@end lisp
+
+Further informations about installing EMMS can be found in the
+installation chapter, @xref{Installation}.
+
+Let's say you want to enable all features which are considered stable by
+the EMMS developers. To achieve this you invoke the @code{emms-all}
+setup function by adding the following three lines to your @file{.emacs}.
+
+@lisp
+(require 'emms-setup)
+(emms-all)
+(emms-default-players)
+@end lisp
+
+The function @code{emms-default-players} in the last line sets up the
+list of default players. The list contains lightweight specialized
+players like ogg123 or mpg321 and we-play-everything-players like
+mplayer or xine. To be sure that emms can play all your music you should
+check that the needed players are installed.
+
+Further informations about the several setup scripts can be found in the
+simple setup chapter, @xref{Simple Setup}.
+
+Of course EMMS tries to display the tags of the music files you listen
+to. For this to work you have to make sure that the appropriate programs
+are installed. For mp3 files you need `mp3info', and for ogg files you
+need `ogginfo'.
+
+The last thing to do is to tell EMMS the root directory of our music
+collection. Let's say all your music is in @file{~/Music} or in
+subdirectories thereof.
+
+@lisp
+(setq emms-source-file-default-directory "~/Music/")
+@end lisp
+
+OK, now we've set up EMMS. Reload your @file{.emacs} or restart Emacs to
+let the changes have an effect.
+
+Now we will add all our music to a playlist by invoking @kbd{M-x
+emms-add-directory-tree RET ~/Music/ RET}. We do this because then EMMS
+will read the tags of all your music files and cache them. This is
+required for the browser, @xref{The Browser}.
+
+To switch to the playlist buffer, invoke @kbd{M-x emms-playlist-mode-go}
+or simply @kbd{M-x emms}. You can see that most tracks are displayed
+with their file name, but track by track the filename gets replaced with
+the artist and track name of the file's tag.
+
+Hit @kbd{RET} on a track to start playback.
+
+Now you can start exploring EMMS. It's probably best to begin with the
+basic commands (@pxref{Basic Commands}), the interactive playlists
+(@pxref{Interactive Playlists}), and the browser (@pxref{The Browser}).
+
+@node Getting Help
+@chapter Getting Help
+
+@cindex mailing list
+@cindex website
+
+If you have a bug to report, need help, or wish to suggest a feature,
+please feel free to use the Emms mailing list.  The address of the list
+is emms-help@@gnu.org.  To subscribe to it, visit
+@url{http://lists.gnu.org/mailman/listinfo/emms-help}.
+
+If you are familiar with the Gmane service, there is a Gmane newsgroup
+which mirrors this mailing address at gmane.emacs.emms.user.
+
+Emms also has a website at @url{http://www.gnu.org/software/emms/}.
+
+@node Formats and Freedom
+@chapter Formats and Freedom
+
+@cindex freedom
+@cindex format
+
+Emms is free software, but some of the file formats it can play carry
+restrictions, they are proprietary file formats. Proprietary software
+companies are pushing out audio and video formats which restrict when,
+where and how you can play them, and restrict developers from writing
+free software which interacts with them.
+
+Restrictive file formats put the corporate bottom-line before the
+public interest.
+
+Fortunately there are alternatives like Ogg. Ogg is a professional
+grade multimedia format. Ogg Vorbis is the compressed audio format
+(like MP3), and Ogg Theora is the video format. For more information,
+go to @url{http://www.xiph.org/}.
+
+If you want to transcode audio into a lossless format, you can try
+FLAC (Free Lossless Audio Codec). FLAC stands out as the fastest and
+most widely supported lossless audio codec, and the only one that at
+once is non-proprietary, is unencumbered by patents and has the source
+code for a reference implementation freely available. For more
+information about FLAC, go to @url{http://flac.sourceforge.net/}.
+
+@node Basic Commands
+@chapter Basic Commands
+
+@cindex basic commands
+@cindex commands, basic
+
+Before you can use the interface commands, you need a playlist to
+start with. The following commands allow you to add to the current
+playlist from different sources:
+
+Note that the commands with the ``emms-add-'' prefix add the source to
+the playlist but do not start playing it immediately. Conversely, the
+commands with the ``emms-play-'' prefix begin playing the track
+immediately.
+
+@defun emms-play-file file
+A source for a single file - either @var{file}, or queried from the
+user. If called with a prefix the file will be added like
+@command{emms-add-file}.
+@end defun
+@defun emms-add-file file
+A source for a single file - either @var{file}, or queried from
+the user. If called with a prefix the file will be played like
+@command{emms-play-file}.
+@end defun
+@defun emms-play-directory dir
+A source for a whole directory tree - either @var{dir}, or queried
+from the user.
+@end defun
+@defun emms-add-directory dir
+A source for a whole directory tree - either @var{dir}, or queried
+from the user.
+@end defun
+@defun emms-play-directory-tree dir
+A source for multiple directory trees - either @var{dir}, or the
+value of @var{emms-source-file-default-directory}.
+@end defun
+@defun emms-add-directory-tree dir
+A source for multiple directory trees - either @var{dir}, or the
+value of @var{emms-source-file-default-directory}.
+@end defun
+@defun emms-play-url url
+A source for an @var{url} - for example, for streaming.
+@end defun
+@defun emms-add-url url
+A source for an @var{url} - for example, for streaming.
+@end defun
+@defun emms-play-playlist playlist
+A source for the M3u or PLS playlist format from the file @var{playlist}.
+@end defun
+@defun emms-add-playlist playlist
+A source for the M3u or PLS playlist format from the file @var{playlist}.
+@end defun
+@defun emms-play-find dir regexp
+A source that will find files in @var{dir} or
+@var{emms-source-file-default-directory} which match @var{regexp}.
+@end defun
+@defun emms-add-find dir regexp
+A source that will find files in @var{dir} or
+@var{emms-source-file-default-directory} which match @var{regexp}.
+@end defun
+
+The basic functionality of Emms is just to play music without being
+noticed. It provides a few commands to skip the current track and
+such, but other than that it doesn't show up. Emms provides the
+following basic user commands (which you might want to bind to
+keystrokes):
+
+@defun emms-start
+Start playing the current playlist
+@end defun
+@defun emms-stop
+Stop playing
+@end defun
+@defun emms-next
+Start playing the next track in the playlist
+@end defun
+@defun emms-previous
+Start playing previous track in the playlist
+@end defun
+@defun emms-shuffle
+Shuffle the current playlist. This uses
+@var{emms-playlist-shuffle-function}.
+@end defun
+@defun emms-sort
+Sort the current playlist. This uses
+@var{emms-playlist-sort-function}.
+@end defun
+@defun emms-show &optional insertp
+Describe the current Emms track in the minibuffer. If @var{insertp} is
+non-nil, insert the description into the current buffer instead. This
+function uses @var{emms-show-format} to format the current track.
+@end defun
+
+@node The Core File
+@chapter The Core File
+
+@cindex core file
+@cindex heart of Emms
+@cindex primitive functions
+
+The core file @file{emms.el} provides the all basic functions for
+playing music, generating playlists and defining players.
+
+@menu
+* User Variables::     Variables for the user to tweak.
+* Hooks::              Hooks for important Emms functions.
+* Core Functions::     Providing the basic functionality of Emms.
+@end menu
+
+@node User Variables
+@section User Variables
+
+@cindex user variables
+@cindex options
+
+The core file defines a number of user variables.
+
+@defopt emms-player-list
+A list of players Emms can use. You need to set this in order to use
+Emms to play media.
+@end defopt
+@defopt emms-show-format
+The format to use for @command{emms-show}. Any "%s" is replaced by
+what @var{emms-track-description-function} returns for the currently
+playing track.
+@end defopt
+@defopt emms-repeat-playlist
+Non-nil if the Emms playlist should automatically repeat the playlist.
+If nil, playback will stop when the last track finishes playing.
+@end defopt
+@defopt emms-track-description-function
+Function for describing an Emms track in a user-friendly way.
+@end defopt
+@defopt emms-sort-lessp-function
+A function that compares two tracks, and returns non-nil if the first
+track should be sorted before the second (see also @code{sort}).
+@end defopt
+
+@node Hooks
+@section Hooks
+
+@cindex hooks
+@cindex adding functionality
+
+The core file provides hook variables for the basic functionality of
+Emms.
+
+@defopt emms-player-started-hook
+A hook run when an Emms player started playing.
+@end defopt
+@defopt emms-player-stopped-hook
+A hook run when an Emms player stopped playing. See also
+@var{emms-player-finished-hook}.
+@end defopt
+@defopt emms-playlist-source-inserted-hook
+Hook run when a source got inserted into the playlist. The buffer is
+narrowed to the new tracks.
+@end defopt
+@defopt emms-playlist-selection-changed-hook
+Hook run after another track is selected in the Emms playlist.
+@end defopt
+@defopt emms-playlist-cleared-hook
+Hook run after the current Emms playlist is cleared. This happens both
+when the playlist is cleared and when a new buffer is created for it.
+@end defopt
+@defopt emms-player-finished-hook
+Hook run when an Emms player finishes playing a track. Please pay
+attention to the differences between @var{emms-player-finished-hook}
+and @var{emms-player-stopped-hook}. The former is called only when the
+player is stopped interactively; the latter, only when the player
+actually finishes playing a track.
+@end defopt
+@defopt emms-player-paused-hook
+Hook run when a player is paused or resumed. Use
+@var{emms-player-paused-p} to find the current state.
+@end defopt
+
+@node Core Functions
+@section Core Functions
+
+@cindex core functions
+@cindex basic functions
+
+The core file also defines all the functions important to the basic
+use of Emms.
+
+There are functions which deal with movement in the playlist.
+
+@defun emms-next-noerror
+Start playing the next track in the Emms playlist. Unlike
+@code{emms-next}, this function doesn't signal an error when called at
+the end of the playlist. This function should only be called when no
+player is playing. This is a good function to put in
+@code{emms-player-finished-hook}.
+@end defun
+@defun emms-playlist-next
+Move to the previous track in the current buffer.
+@end defun
+@defun emms-playlist-previous
+Move to the previous track in the current buffer.
+@end defun
+@defun emms-random
+Jump to a random track.
+@end defun
+@defun emms-toggle-repeat-playlist
+Toggle whether emms repeats the playlist after it is done. See
+@var{emms-repeat-playlist}.
+@end defun
+@defun emms-toggle-repeat-track
+Toggle whether emms repeats the current track. See
+@var{emms-repeat-track}.
+@end defun
+@defun emms-toggle-random-playlist
+Toggle whether emms plays the tracks randomly or sequentially. See
+@var{emms-random-playlist}.
+
+Some functions deal with the getting and setting track information.
+
+@defun emms-track type name
+Create a track with type @var{type} and name @var{name}.
+@end defun
+@defun emms-track-type track
+Return the type of @var{track}.
+@end defun
+@defun emms-track-name track
+Return the name of @var{track}.
+@end defun
+@defun emms-track-get name track &optional inexistent
+Return the value of @var{name} for @var{track}. If there is no value,
+return @var{default} (or nil, if not given).
+@end defun
+@defun emms-track-set track name value
+Set the value of @var{name} for @var{track} to @var{value}.
+@end defun
+@defun emms-track-description track
+Return a description of @var{track}. This function uses
+@var{emms-track-description-function}.
+@end defun
+@defun emms-player-for track
+Return an Emms player capable of playing @var{track}. This will be the
+first player whose PLAYABLEP function returns non-nil, or nil if no
+such player exists.
+@end defun
+@defun emms-playlist-current-selected-track
+Return the currently selected track in the current playlist.
+@end defun
+
+There are also functions which deal with the playing itself.
+
+@defun emms-player-start track
+Start playing @var{track}.
+@end defun
+@defun emms-player-stop
+Stop the currently playing player.
+@end defun
+@defun emms-player-stopped
+Declare that the current Emms player is finished.
+This should only be done by the current player itself.
+@end defun
+@defun emms-seek seconds
+Seek the current player @var{seconds} seconds. This can be a floating
+point number for sub-second fractions. It can also be negative to
+seek backwards.
+@end defun
+@defun emms-seek-forward
+Seek ten seconds forward.
+@end defun
+@defun emms-seek-backward
+Seek ten seconds backward.
+@end defun
+
+For more basic commands defined in the core file see @xref{Basic
+Commands}.
+
+@node Sources
+@chapter Sources
+
+@cindex Sources
+
+Sources allow Emms to add and play tracks. Emms comes with a number of
+sources of its own. Sources are designed so that creating new ones
+will be easy.
+
+For examples of Emms sources for files and directories see
+@file{emms-source-file.el}.
+
+@defopt emms-source-file-default-directory
+The default directory to look for media files.
+@end defopt
+@defun emms-play-find
+Play all files in @var{emms-source-file-default-directory} that match
+a specific regular expression.
+@end defun
+@defun emms-source-file &optional file
+An Emms source for a single file - either @var{file}, or queried from the
+user.
+@end defun
+@defun emms-source-files files
+An Emms source for a list of @var{files}.
+@end defun
+@defun emms-source-directory &optional dir
+An Emms source for a whole directory tree - either @var{dir}, or queried
+from the user
+@end defun
+@defun emms-source-directory-tree & optional dir
+An Emms source for multiple directory trees - either @var{dir}, or the
+value of @var{emms-source-file-default-directory}.
+@end defun
+@defun emms-source-playlist file
+An EMMS source for playlists.  See @var{emms-source-playlist-formats}
+for a list of supported formats.
+@end defun
+@defun emms-source-playlist-native file
+An EMMS source for a native EMMS playlist file.
+@end defun
+@defun emms-source-playlist-m3u file
+An EMMS source for an m3u playlist file.
+@end defun
+@defun emms-source-playlist-pls file
+An EMMS source for a pls playlist file.
+@end defun
+@defun emms-source-find &optional dir regex
+An Emms source that will find files in @var{dir} or
+@var{emms-source-file-default-directory} that match @var{regexp}.
+@end defun
+@defun emms-source-file-directory-tree &optional dir
+Return a list of all files under @var{dir} which match @var{regex}.
+@end defun
+@defun emms-source-dired
+Play all marked files of a dired buffer
+@end defun
+@defun emms-source-file-regex
+Return a regexp that matches everything any player (that supports
+files) can play.
+@end defun
+@defun emms-locate regexp
+Search for @var{regexp} and display the results in a locate buffer
+@end defun
+
+@node Simple Players
+@chapter Simple Players
+
+@cindex players, simple
+
+@defmac define-emms-simple-player name types regex command &rest args
+Define a simple player with the use of `emms-define-player'.
+@var{name} is used to construct the name of the function like
+emms-player-@var{name}. @var{types} is a list of track types
+understood by this player. @var{regex} must be a regexp that matches
+the filenames the player can play. @var{command} specifies the command
+line argument to call the player and @var{args} are the command line
+arguments.
+@end defmac
+
+For a discussion on how to define new players see @xref{New Player}.
+
+@defun emms-player-simple-stop
+Stop the currently playing process, if indeed there is one.
+@end defun
+@defun emms-player-simple-start filename cmdname params
+Starts a process playing @var{filename} using the specified @var{cmdname} with
+the specified @var{params}.
+@end defun
+@defun emms-player-simple-sentinel proc str
+Sentinel for determining the end of process for the process @var{proc}
+and the sentinel string @var{str}.
+@end defun
+
+@node Playlists
+@chapter Playlists
+
+@cindex organizing tracks and media
+
+Emms uses Emacs buffers to store the media tracks for playing. We call
+one such buffer a ``playlist buffer'' or an ``Emms playlist
+buffer''. Emms then proceeds to play the media tracks in the buffer
+from top to bottom until the end of the playlist.
+
+The name of the playlist buffer is defined in the variable
+@var{emms-playlist-buffer-name} and is set to be an invisible Emacs
+buffer by default. You can change to any name you want. For an example
+configuration see @xref{Configuration}.
+
+You can create any number of playlist buffers you wish. At any time
+Emms has a single ``current'' buffer through which it proceeds track
+by track.
+
+@defun emms-playlist-new &optional name
+Create a new playlist buffer.
+The buffer is named @var{name}, but made unique. @var{name} defaults
+to `emms-playlist-buffer-name'. If called interactively, the new
+buffer is also selected.
+@end defun
+
+@defun emms-playlist-save &optional format file
+Store the current playlist to FILE as the type FORMAT.  The default
+format is specified by @var{emms-source-playlist-default-format}.
+@end defun
+
+The current Emms playlist buffer is stored in the variable
+@var{emms-playlist-buffer}.
+
+@node Track Information
+@chapter Track Information
+
+@cindex track information
+@cindex info tags
+
+Emms is distributed with three predefined methods for retrieving info,
+provided by @file{emms-info-mp3info.el}, @file{emms-info-ogginfo.el} and
+@file{emms-cue.el}. The first two packages are front-ends for
+command-line tools. Ogg track information is retrieved using the
+@uref{http://directory.fsf.org/audio/ogg/vorbistools.html, ogginfo}
+software. Likewise, mp3 track information is available using
+@uref{http://www.ibiblio.org/mp3info/, mp3info}. While,
+@file{emms-cue.el} retrieves tracks information for ape/flac files by
+parsing a cue sheet file, which is plain text.
+
+Automatic track information retrieval is enabled by default in the
+`emms-standard', `emms-all' and `emms-devel' setup levels provided by
+@file{emms-setup.el}. For more information about @file{emms-setup.el}
+see @xref{Simple Setup}.
+
+If you would like to know how Emms track retreival works and how we
+can define new methods for track retrieval see @xref{Defining Info
+Methods}.
+
+There are a number of user variables which control the behaviour of
+`emms-info'.
+
+@defopt emms-info-auto-update
+Non-nil when Emms should update track information if the file changes.
+This will cause hard drive activity on track loading. If this is too
+annoying for you, set this variable to nil.
+@end defopt
+@defopt emms-info-asynchronously
+Non-nil when track information should be loaded asynchronously. This
+requires the feature `later-do' which is provided by the file
+@file{later-do.el}, which should come with Emms.
+@end defopt
+@defopt emms-info-functions
+Functions which add information to tracks.  Each is called with a
+track as argument.
+@end defopt
+
+@menu
+* Defining Info Methods::    Defining new info methods.
+@end menu
+
+@node Defining Info Methods
+@section Defining Info Methods
+
+@cindex defining info methods
+
+An info method essentially consists of a function which given an Emms
+track returns the appropriate info for that track.
+
+We can for example look at the predefined method for retrieving
+information about audio tracks in the Ogg format.
+
+The function @command{emms-info-ogginfo} provided by
+@file{emms-info-ogginfo.el} accepts an Emms track as a single
+argument and returns the appropriate information string.
+
+We then register our info function with Emms by adding it to the
+@var{emms-info-functions} list. The function will then be called at
+the right time to provide track info.
+
+@lisp
+(add-to-list 'emms-info-functions 'emms-info-ogginfo)
+@end lisp
+
+@node Interactive Playlists
+@chapter Interactive Playlists
+
+@cindex Interactive Playlists
+
+Emms provides a visual, interactive playlist mode as well as the
+ability to use playlists without ever looking at then. This visual,
+interactive mode is called the `emms-playlist-mode' and is defined in
+@file{emms-playlist-mode.el}.
+
+The interactive playlist mode is enabled by default in the
+`emms-standard', `emms-all' and `emms-devel' setup levels. For more
+information about Emms setup levels see @xref{Simple Setup}.
+
+@defun emms-playlist-mode-go
+Switch to the current emms-playlist buffer and use emms-playlist-mode.
+@end defun
+
+If you wish to make this the default EMMS playlist mode, add the
+following to your @file{.emacs}.
+
+@lisp
+(setq emms-playlist-default-major-mode 'emms-playlist-mode)
+@end lisp
+
+The interactive playlist buffer shows the tracks in the current Emms
+playlist in the order in which they will be played. The current track
+will be highlighted.
+
+When in the interactive playlist mode we can perform different actions
+on the current playlist.
+
+@table @kbd
+@item a
+@findex emms-playlist-mode-add-contents
+Add files in the playlist at point to the current playlist buffer.
+If we are in the current playlist, make a new playlist buffer and
+set it as current.
+@item b
+@findex emms-playlist-set-playlist-buffer
+Set the current playlist buffer.
+@item n
+@findex emms-next
+Start playing the next track in the playlist.
+@item p
+@findex emms-next
+Start playing the previous track in the playlist.
+@item s
+@findex emms-stop
+Stop playing.
+@item P
+@findex emms-pause
+Pause.
+@item >
+@findex emms-seek-forward
+Seek ten seconds forward.
+@item <
+@findex emms-seek-backward
+Seek ten seconds backward.
+@item f
+@findex emms-show
+Describe the currently playing track in the minibuffer.
+@item c
+@findex emms-playlist-mode-center-current
+Display the current track in the center of the screen.
+@item RET
+@findex emms-playlist-mode-play-current-track
+Start playing the track under point. Note that this is also available
+with @kbd{<mouse-2>}.
+@item SPC
+@findex scroll-up
+Scroll up a near full page.
+@item M-<
+@findex emms-playlist-mode-first
+Go to the first track in the playlist.
+@item M->
+@findex emms-playlist-mode-last
+Go to the last track in the playlist.
+@item r
+@findex emms-random
+Go to a randomly selected track in the playlist.
+@item q
+@findex bury-buffer
+Put the interactive playlist buffer at the end of the list of all
+buffers.
+@item C-x C-s
+@findex emms-playlist-save
+Save the current playlist buffer to a file. By default, Emms will ask
+you for confirmation before overwriting an existing playlist. You can
+silently overwrite existing playlist by setting
+@var{emms-source-playlist-ask-before-overwrite} to nil.
+@item ?
+@findex describe-mode
+Describe the mode.
+@end table
+
+We can also edit the playlist using familiar GNU/Emacs commands:
+
+@table @kbd
+@item C-k
+@findex emms-playlist-mode-kill-track
+Remove the track under point from the playlist buffer. Also available
+using the @kbd{d} key.
+@item C-y
+@findex emms-playlist-mode-yank
+See the command @command{yank}
+@item C-w
+@findex emms-playlist-mode-kill
+See the command @command{kill-region}
+@item M-y
+@findex emms-playlist-mode-yank-pop
+See the command @command{yank-pop}.
+@item C-j
+@findex emms-playlist-mode-insert-newline
+Insert a newline at point.
+@end table
+
+We can use the regular GNU/Emacs killing and yanking commands to move
+and copy tracks in between playlist buffers. We can use the same
+commands to insert arbitrary text into the playlist buffers together
+with the playlist tracks. Text which is not a track is ignored by the
+program and can therefore be used to include titles and annotations
+within the playlist.
+
+@node Markable Playlists
+@chapter Markable Playlists
+
+@cindex Markable Playlists
+
+The Markable Playlists provided by the file @file{emms-mark.el} are an
+alternative to the default interactive playlists, @xref{Interactive
+Playlists}. They allow marking tracks with keybindings familiar to users
+of dired.
+
+To enable the Markable Playlists you have to add
+
+@lisp
+(require 'emms-mark)
+@end lisp
+
+to your @file{.emacs}. Then you can activate @command{emms-mark-mode} by
+executing @command{M-x emms-mark-mode} in a playlist buffer. You can
+return to the default interactive playlist mode with @command{M-x
+emms-mark-mode-disable}.
+
+If you wish to make this the default EMMS playlist mode, add the
+following to your @file{.emacs}.
+
+@lisp
+(setq emms-playlist-default-major-mode 'emms-mark-mode)
+@end lisp
+
+@table @kbd
+@item m
+@findex emms-mark-forward
+Marks the current track and sets point one line forward. If a prefix
+argument ARG is given, it will mark the next ARG tracks and set point
+accordingly. A negative argument marks backward.
+@item U
+@findex emms-mark-unmark-all
+Unmarks all tracks in the playlist.
+@item t
+@findex emms-mark-toggle
+Toggles mark on the current track.
+@item u
+@findex emms-mark-unmark-forward
+Unmarks same way as @command{emms-mark-forward} marks.
+@item % m
+@findex emms-mark-regexp
+Marks all tracks in the playlist matching the given regular
+expression. A prefix argument means to unmark them instead.
+@end table
+
+When tracks are marked you can operate on them:
+
+@table @kbd
+@item D
+@findex emms-mark-delete-marked-tracks
+Deletes the marked tracks from the playlist.
+@item K
+@findex  emms-mark-kill-marked-tracks
+Deletes the marked tracks from the playlist and places them in the
+kill-ring, so that you can @command{yank} in into another playlist.
+@item W
+@findex emms-mark-copy-marked-tracks
+Adds the marked tracks to the kill-ring, so that you can @command{yank}
+them into another playlist.
+@end table
+
+emms-mark is also intent to provide a way for user to select tracks
+for other command to operate on them. Currently,
+@file{emms-tag-editor.el} used the emms-mark to edit tags of selected
+tracks. Two function is useful for elisp programer to handle marked
+tracks.
+
+@defun emms-mark-do-with-marked-track
+This function take a function to perform on all marked tracks. A
+optional argument `move-flag' to tell the function to move forward
+line after calling given function. If the given function didn't change
+position, the second argument should set to non-nil.
+@end defun
+
+@defun emms-mark-mapcar-marked-track
+This function is very similar to `emms-mark-do-with-marked-track'
+except it collects result of given function (that's why named with
+`mapcar').
+@end defun
+
+@node APE / FLAC Commands
+@chapter APE / FLAC Commands
+
+Often, a single APE or FLAC file contains a complete ablum.  We can still
+play next or previous track in the ablum with the help of
+@file{emms-cue.el} package, provided there is a corresponding cue sheet
+file.  This package also defines @code{emms-info-cueinfo} for retreiving
+the track information for APE / FLAC itself.
+
+To load @file{emms-cue.el}:
+
+@lisp
+(require 'emms-cue)
+(add-to-list 'emms-info-functions 'emms-info-cueinfo)
+@end lisp
+
+@defun emms-cue-next
+Play next track from .cue file
+@end defun
+@defun emms-cue-previous
+Play previous track from .cue file
+@end defun
+
+@node Bookmarks
+@chapter Bookmarks
+
+Emms can save a ``temporal bookmark'' in a media file via
+emms-bookmarks. The file @file{emms-bookmarks.el} provides the package
+emms-bookmarks.
+
+While some media is playing, invoking @kbd{M-x emms-bookmarks-add}
+will first pause the playback and then prompt for a name describing
+the bookmark. Tracks can have multiple bookmarks associated with them.
+
+To jump to the next and previous bookmarks in the current track invoke
+@kbd{M-x emms-bookmarks-next} and @kbd{M-x emms-bookmarks-prev}
+respectively.
+
+To clear all of the bookmarks for the current track invoke @kbd{M-x
+emms-bookmarks-clear}.
+
+@node Extending Emms
+@chapter Extending Emms
+
+@cindex new players
+@cindex defining players
+@cindex new players, defining
+
+Emms introduces a high abstraction layer for playing music so you can
+customise it to your needs.
+
+@menu
+* New Player::               How to define a new player.
+* Simple Player for `play':: An example player using @command{play}.
+* More Complex Player::      Example of a complex player using @command{mpg321}.
+@end menu
+
+@node New Player
+@section New Player
+
+@cindex new player
+@cindex defining new players
+
+The file @file{emms-player-simple.el} defines some easy players to
+start with, but it shouldn't be hard to provide a function for your
+favourite player. We will start with an easy example that shows how
+we can use the @command{play} command under Unix to play our WAV files.
+
+@node Simple Player for `play'
+@section Simple Player for `play'
+
+@cindex simple player
+@cindex primitive player
+@cindex basic player
+
+Play is a very easy command line player for various format. If you
+want your emms to play WAV files just put the following lines in you
+@file{.emacs}:
+
+@lisp
+(require 'emms-player-simple)
+(define-emms-simple-player play '(file) "\\.wav$" "play")
+@end lisp
+@noindent
+
+Huh! Wasn't that easy?
+
+The macro function @command{define-emms-simple-player} takes a minimum
+of three arguments. The first argument (@emph{play} in our example)
+defines the name of the player. It's used to name the player
+functions. The second is a regexp, that defines which files to play
+with our player. @emph{\\.wav$} matches any filename ending with a dot
+and the string wav. The last argument is the actual command line
+command we use to play our files. You can also add the path but we
+just assume that the command is in your path. All arguments you add to
+these three are optional. They define the command line arguments you
+want to add to your argument. If you want to hear the wav file of your
+favourite artist in the most possible volume use the following line:
+
+@lisp
+(require 'emms-player-simple)
+
+(define-emms-simple-player play
+                           '(file)
+                           "\\artist-*.wav$"
+                           "play"
+                           "--volume=100")
+@end lisp
+@noindent
+
+Please notice that you have to add the arguments as strings!
+
+The command line tool you use for @command{define-emms-simple-player}
+has to take one song as argument and stop after playing that
+particular song. For any other concept you will need to customise
+emms a bit more...
+
+@node More Complex Player
+@section More Complex Player
+
+@cindex complex player
+@cindex advanced player
+
+The most players you use will be simple players so you don't need to
+read this chapter. But if you are curious how you can use (almost) every
+player in emms read further...
+
+In this chapter we will use mpg321 to construct a player that
+actually can pause a track, restart it and show rest time. We won't
+implement all of that, but after that chapter you will know how to
+define it.
+
+The command @command{define-emms-simple-player} is just a abstraction
+layer for @command{define-emms-player}, which is a little bit more
+complicated but much more powerful!
+
+@lisp
+(define-emms-player "emms-mpg321-remote"
+  :start 'emms-mpg321-remote-start
+  :stop 'emms-mpg321-remote-stop
+  :playablep 'emms-mpg321-remote-playable-p)
+@end lisp
+@noindent
+
+So, that is almost all! @command{define-emms-player} takes a minimum
+of three arguments. The first is the name of the player. The rest are
+methods with functions to call. Three methods are required: start,
+stop and playable. Start says Emms how to start a track (sic!), stop
+how to stop a player and playablep should return non-nil if the player
+can play the track.
+
+So we just need these three functions to get our mpg321-remote:
+
+First we code the start function. We will check if there's a open
+process and start one otherwise. Then we send a string to the process
+with the filename and set a filter.
+
+@lisp
+(defun emms-mpg321-remote-start ()
+  (unless (get-process ``mpg321-remote'')
+    (setq emms-mpg321-remote-process
+         (start-process "mpg321-remote-process"
+                        "*mpg321*" "mpg321" "-R" "abc"))
+  (process-send-string "mpg321-remote-process"
+                      (concat "l " (emms-track-name track)))
+  (set-process-filter emms-mpg321-remote-process 'emms-mpg321-remote-filter)))
+@end lisp
+@noindent
+
+We need the filter, as mpg321-remote won't quit after playing the
+track as the simple player do. We wait until the process sends the
+output ``(at-sign)P 0'' (the signal of mpg321 that the song ended) to the
+filter and call emms-mpg321-remote-stop.
+
+@lisp
+(defun emms-mpg321-remote-filter (process output)
+  (when (string-match "(at-sign)P 0" output)
+    (emms-mpg321-remote-stop)))
+@end lisp
+@noindent
+
+@command{emms-mpg321-remote-stop} won't do anything interesting. It
+just test if there are other files to play and close the process otherwise.
+
+@lisp
+(defun emms-mpg321-remote-stop ()
+  (unless emms-playlist
+    (process-send-string "mpg321-remote-process" "Q\n"))
+@end lisp
+@noindent
+
+And to make that a playable example I also added
+@command{emms-mpg321-remote-playablep}, which I really just steal
+from @file{emms-player-simple.el}
+
+@lisp
+(defun emms-mpg321-remote-playablep (track)
+       "Return non-nil when we can play this track."
+       (and (eq 'file (emms-track-type track))
+@end lisp
+@noindent
+
+Now we have a ready player and we could add commands like
+@command{emms-mpg321-remote-pause} for example.
+
+@node The Browser
+@chapter The Browser
+
+The Browser allows you to browse the metadata cache and add tracks to
+your playlist. It includes a powerful interactive mode.
+
+The Browser is defined in @file{emms-browser.el} and is included in
+the @command{emms-all} setup level. For more information about Emms
+setup levels see @xref{Simple Setup}.
+
+You can also manually add the Browser to your Emms setup by loading it
+explicitly with:
+
+@lisp
+(require 'emms-browser)
+@end lisp
+
+To be properly useful, you should do M-x
+@command{emms-add-directory-tree} to all the files you own at least
+once so that the cache is fully populated.
+
+@menu
+* Browser Interface::     The interactive browser interface.
+* Filtering Tracks::      Displaying a subset of the tracks.
+* Displaying Covers::     Displaying album covers in the browser interface.
+* Changing Looks::        Changing the tree structure, display format and faces.
+@end menu
+
+@node Browser Interface
+@section Browser Interface
+
+The browser interface allows you to display and interact with your
+tracks in many different ways. There are a number of ways to start the
+browser.
+
+@defun emms-smart-browse
+Display browser and playlist. Toggle between selecting browser,
+playlist or hiding both. Tries to behave sanely if the user has
+manually changed the window configuration.
+@end defun
+
+@defun emms-browse-by-artist
+Display the browser and order the tracks by artist.
+@end defun
+
+@defun emms-browse-by-album
+Display the browser and order the tracks by album.
+@end defun
+
+@defun emms-browse-by-genre
+Display the browser and order the tracks by genre.
+@end defun
+
+@defun emms-browse-by-year
+Display the browser and order the tracks by year.
+@end defun
+
+Once the Browser is displayed you can use it to managed your track
+collection and playlists. The Browser is interactive and has its own
+keybindings.
+
+@table @kbd
+
+@item C-j
+@kindex C-j (emms-browser)
+@findex emms-browser-add-tracks-and-play
+Add all tracks at point, and play the first added track.
+
+@item RET
+@kindex RET (emms-browser)
+@findex emms-browser-add-tracks
+Add all tracks at point.
+
+@item SPC
+@kindex SPC (emms-browser)
+@findex emms-browser-toggle-subitems
+Show or hide (kill) subitems under the current line.
+
+@item 1
+@kindex 1 (emms-browser)
+@findex emms-browser-collapse-all
+Collapse everything.
+
+@item 2
+@kindex 2 (emms-browser)
+@findex emms-browser-expand-to-level-2
+Expand all top level items one level.
+
+@item 3
+@kindex 3 (emms-browser)
+@findex emms-browser-expand-to-level-3
+Expand all top level items two levels.
+
+@item 4
+@kindex 4 (emms-browser)
+@findex emms-browser-expand-to-level-4
+Expand all top level items three levels.
+
+@item C
+@kindex C (emms-browser)
+@findex emms-browser-clear-playlist
+Clear the playlist.
+
+@item E
+@kindex E (emms-browser)
+@findex emms-browser-expand-all
+Expand everything.
+
+@item d
+@kindex d (emms-browser)
+@findex emms-browser-view-in-dired
+View the current directory in dired.
+
+@item q
+@kindex q (emms-browser)
+@findex emms-browser-bury-buffer
+Bury the browser buffer.
+
+@item r
+@kindex r (emms-browser)
+@findex emms-browser-goto-random
+Jump to a random track.
+
+@item /
+@kindex / (emms-browser)
+@findex emms-isearch-buffer
+Isearch through the buffer.
+
+@item <
+@kindex < (emms-browser)
+@findex emms-browser-previous-filter
+Redisplay with the previous filter.
+
+@item >
+@kindex > (emms-browser)
+@findex emms-browser-next-filter
+Redisplay with the next filter.
+
+@item ?
+@kindex ? (emms-browser)
+@findex describe-mode
+See the Emacs documentation for the function.
+
+@item C-/
+@kindex C-/ (emms-browser)
+@findex emms-playlist-mode-undo
+Undo the previous playlist action.
+
+@item <C-return>
+@kindex <C-return> (emms-browser)
+@findex emms-browser-add-tracks-and-play
+Add all tracks at point, and play the first added track.
+
+@item <backtab>
+@kindex <backtab> (emms-browser)
+@findex emms-browser-prev-non-track
+Jump to the previous non-track element.
+
+@item <tab>
+@kindex <tab> (emms-browser)
+@findex emms-browser-next-non-track
+Jump to the next non-track element.
+
+@item s A
+@kindex s A (emms-browser)
+@findex emms-browser-search-by-album
+Search the collection by album.
+
+@item s a
+@kindex s a (emms-browser)
+@findex emms-browser-search-by-artist
+Search the collection by artist.
+
+@item s s
+@kindex s s (emms-browser)
+@findex emms-browser-search-by-names
+Search the collection by names.
+
+@item s t
+@kindex s t (emms-browser)
+@findex emms-browser-search-by-title
+Search the collection by title.
+
+@item b 1
+@kindex b 1 (emms-browser)
+@findex emms-browse-by-artist
+Browse the collection by artist.
+
+@item b 2
+@kindex b 2 (emms-browser)
+@findex emms-browse-by-album
+Browse the collection by album.
+
+@item b 3
+@kindex b 3 (emms-browser)
+@findex emms-browse-by-genre
+Browse the collection by genre.
+
+@item b 4
+@kindex b 4 (emms-browser)
+@findex emms-browse-by-year
+Browse the collection by year.
+
+@item W a p
+@kindex W a p (emms-browser)
+@findex emms-browser-lookup-album-on-pitchfork
+Lookup the album using Pitchfork.
+
+@item W a w
+@kindex W a w (emms-browser)
+@findex emms-browser-lookup-album-on-wikipedia
+Lookup the album using Wikipedia.
+@end table
+
+@node Filtering Tracks
+@section Filtering Tracks
+
+If you want to display a subset of your collection (such as a
+directory of 80s music, only avi files, etc.) then you can extend the
+Browser by defining ``filters''.
+
+Show everything:
+
+@lisp
+(emms-browser-make-filter "all" 'ignore)
+@end lisp
+
+Set "all" as the default filter:
+
+@lisp
+(emms-browser-set-filter (assoc "all" emms-browser-filters))
+@end lisp
+
+Show all files (no streamlists, etc):
+
+@lisp
+(emms-browser-make-filter
+ "all-files" (emms-browser-filter-only-type 'file))
+@end lisp
+
+Show only tracks in one folder:
+
+@lisp
+(emms-browser-make-filter
+ "80s" (emms-browser-filter-only-dir "~/Mp3s/80s"))
+@end lisp
+
+Show all tracks played in the last month:
+
+@lisp
+(emms-browser-make-filter
+ "last-month" (emms-browser-filter-only-recent 30))
+@end lisp
+
+After executing the above commands, you can use M-x
+emms-browser-show-all, emms-browser-show-80s, etc to toggle between
+different collections. Alternatively you can use '<' and '>' to cycle
+through the available filters.
+
+The second argument to make-filter is a function which returns t if a
+single track should be filtered. You can write your own filter
+functions to check the type of a file, etc.
+
+Show only tracks not played in the last year:
+
+@lisp
+(emms-browser-make-filter "not-played"
+ (lambda (track)
+  (not (funcall (emms-browser-filter-only-recent 365) track))))
+@end lisp
+
+Show all files that are not in the pending directory:
+
+@lisp
+(emms-browser-make-filter
+ "all"
+ (lambda (track)
+   (or
+    (funcall (emms-browser-filter-only-type 'file) track)
+    (not (funcall
+          (emms-browser-filter-only-dir "~/Media/pending") track)))))
+@end lisp
+
+@node Displaying Covers
+@section Displaying Covers
+
+The browser will attempt to display cover images if they're
+available. By default it looks for images cover_small.jpg,
+cover_med.jpg, etc. Customize @var{emms-browser-covers} to use your
+own covers. Note that you'll probably want to resize your existing
+covers to particular sizes. Suggested sizes are 100x100 for small, and
+200x200 for medium.
+
+Also, Emacs by default will jump around a lot when scrolling a buffer
+with images. In order to prevent that, you can set
+@var{scroll-up-aggressively} and @var{scroll-down-aggressively} to the
+number ``0.0''.
+
+To show a 'no cover' image for albums which don't have a cover, add
+the following code to your .emacs:
+
+@lisp
+(setq emms-browser-default-covers
+  (list "/path/to/cover_small.jpg" nil nil)
+@end lisp
+
+The medium and large images can be set as well.
+
+You can download an example @uref{http://repose.cx/cover_small.jpg,
+`no cover' image}.
+
+@node Changing Looks
+@section Changing Looks
+
+The Browser's look can be customised. You can change the way the tree
+structure looks, the display format and display faces.
+
+@subheading Changing Tree Structure
+
+You can change the way the tree is displayed by modifying the function
+@command{emms-browser-next-mapping-type}.
+
+The following code displays artist->track instead of
+artist->album->track when you switch to the 'singles' filter:
+
+@lisp
+(defadvice emms-browser-next-mapping-type
+                                (after no-album (current-mapping))
+  (when (eq ad-return-value 'info-album)
+    (setq ad-return-value 'info-title)))
+@end lisp
+
+@lisp
+(defun toggle-album-display ()
+  (if (string= emms-browser-current-filter-name "singles")
+      (ad-activate 'emms-browser-next-mapping-type)
+    (ad-deactivate 'emms-browser-next-mapping-type)))
+
+(add-hook 'emms-browser-filter-changed-hook 'toggle-album-display)
+@end lisp
+
+@subheading Changing Display Format
+
+Format strings govern the way items are displayed in the browser and
+playlist. You can customize these if you wish.
+
+@var{emms-browser-default-format} controls the format to use when no
+other format has been explicitly defined. By default, only track and
+albums deviate from the default.
+
+To customise the format of a particular type, find the name of the
+field you want to use (eg `info-artist', `info-title', etc), and
+insert that into emms-browser-<type>-format or
+emms-browser-playlist-<type>-format. For example, if you wanted to
+remove track numbers from tracks in both the browser and playlist, you
+could do:
+
+@lisp
+(defvar emms-browser-info-title-format "%i%n")
+(defvar emms-browser-playlist-info-title-format
+  emms-browser-info-title-format)
+@end lisp
+
+The format specifiers available include:
+
+@itemize @w{}
+@item
+%i    indent relative to the current level
+@item
+%n    the value of the item - eg -info-artist might be ``pink floyd''
+@item
+%y    the album year
+@item
+%A    the album name
+@item
+%a    the artist name of the track
+@item
+%t    the title of the track
+@item
+%T    the track number
+@item
+%cS   a small album cover
+@item
+%cM   a medium album cover
+@item
+%cL   a big album cover
+@end itemize
+
+Note that if you use track-related items like %t, it will take the
+data from the first track.
+
+@subheading Changing Display Faces
+
+The faces used to display the various fields are also customizable.
+They are in the format emms-browser-<type>-face, where type is one of
+"year/genre", "artist", "album" or "track". Note that faces lack the
+initial "info-" part. For example, to change the artist face, type M-x
+@command{customize-face} @command{emms-browser-artist-face}.
+
+@node Sorting Playlists
+@chapter Sorting Playlists
+
+@cindex sort
+@cindex track order
+
+The `emms-playlist-sort' module, defined in the
+@file{emms-playlist-sort.el} package provides functions for sorting
+Emms playlists. `emms-playlist-sort' can be loaded by invoking:
+
+@lisp
+(require 'emms-playlist-sort)
+@end lisp
+
+@defun emms-playlist-sort-by-name
+Sort playlist by name in ascending order.
+@end defun
+
+@defun emms-playlist-sort-by-info-artist
+Sort playlist by artist in ascending order.
+@end defun
+
+@defun emms-playlist-sort-by-info-title
+Sort playlist by title in ascending order.
+@end defun
+
+@defun emms-playlist-sort-by-info-album
+Sort playlist by album in ascending order.
+@end defun
+
+@defun emms-playlist-sort-by-info-year
+Sort playlist by year in ascending order.
+@end defun
+
+@defun emms-playlist-sort-by-info-note
+Sort playlist by notes in ascending order.
+@end defun
+
+@node Persistent Playlists
+@chapter Persistent Playlists
+
+The Emms module @file{emms-history.el} makes playlists persistent over
+emacs sessions.  To make use of this feature put this into your
+~/.emacs.
+
+@lisp
+(require 'emms-history)
+@end lisp
+
+When you kill emacs all playlists will be saved in the file given by the
+variable:
+
+@defopt emms-history-file
+The file to save playlists in.  It defaults to
+"~/.emacs.d/emms-history".
+@end defopt
+
+After you started up emacs again, you can restore all saved playlists
+with this function.
+
+@defun emms-history-load
+Restore all playlists in `emms-history-file'.
+@end defun
+
+If that should be done automatically on each startup, put these lines
+into your ~/.emacs.
+
+@lisp
+(require 'emms-history)
+(emms-history-load)
+@end lisp
+
+Normally @code{emms-history} only restores playlists.  If you want it to
+start playback afterwards, you can tweak this variable.
+
+@defopt emms-history-start-playing
+If non-nil emms starts playing the current track after
+`emms-history-load' was invoked.  The default value is nil.
+@end defopt
+
+@node Editing Tracks
+@chapter Editing Tracks
+
+@cindex track editor
+
+Using @file{emms-tag-editor.el}, emms can set tag informations of tracks
+and write them back to the file with the help of external programs, such
+as `mp3info', `vorbiscomment'.
+
+Use the keybinding @kbd{E} to edit the tags of track under point in the
+playlist or all marked tracks (@pxref{Markable Playlists} for how to
+mark tracks).  The track's tag informations are listed in a special
+buffer `*EMMS-TAGS*' in text format.  Field names are marked in bold
+face and are not editable.  Any tag information is placed behind an
+equal sign and is changable.  A special field `name' is the track's file
+name.  If any change is made in this field, the track's file will be
+renamed to the new name.  When you finished editing the tag infos use
+@kbd{C-c C-c} (which calls @code{emms-tag-editor-submit-and-exit}) to
+submit the changes and close the `*EMMS-TAGS*' buffer.
+
+There are a few commands to perform changes on all tracks.
+
+@defun emms-tag-editor-set-all tag value
+Set TAG to VALUE in all tracks.
+
+If transient-mark-mode is turned on, you can apply the command to a
+selected region.
+
+If `transient-mark-mode' is on and the mark is active, the changes will
+only take effect on the tracks in the region.
+@end defun
+
+@defun emms-tag-editor-replace-in-tag tag from to
+Query and replace text in selected TAG.
+
+For example, if the info-title tag is selected, then only perform
+replacement in title tags.
+
+If `transient-mark-mode' is on and the mark is active, the changes will
+only take effect on the tracks in the region.
+@end defun
+
+@defun emms-tag-editor-transpose-tag tag1 tag2
+Transpose value of TAG1 and TAG2.
+
+If `transient-mark-mode' is on and the mark is active, the changes will
+only take effect on the tracks in the region.
+@end defun
+
+@defun emms-tag-editor-submit arg
+Make modified tags take affect.
+
+With prefix argument, bury the tag edit buffer.
+@end defun
+
+If you want to extend the tag editor to work with file formats other
+than `mp3' and `ogg', have a look at these variables.
+
+@defvr {Variable} emms-tag-editor-formats
+This variable determine how to insert track fields to
+`emms-tag-editor-edit-buffer'.  Emms tag info editable fields is usually
+determined by the extension of track name.  The variable
+`emms-tag-editor-tags' contains all tags that emms track may have.  A
+single charactar is assigned to the tag to make the
+`emms-tag-editor-formats' easier to generate.
+@end defvr
+
+@defvr {Variable} emms-tag-editor-tagfile-functions
+To write tags to track file, an extern program should specified in this
+variable.
+
+If the external program has an interface like `mp3info', you don't have
+to write a function.  Take `mp3' and `ogg' as example.
+@end defvr
+
+@heading Renaming Files
+
+The tag editor is also capable to rename the file of the track at point
+or all files of the marked tracks according to the value this variable.
+
+@defopt emms-tag-editor-rename-format
+When `emms-tag-editor-rename' is invoked the track's file will be
+renamed according this format specification.  The file extension will be
+added automatically.
+
+It uses the format specs defined in @code{emms-tag-editor-tags}.
+
+The default value is "%a - %l - %n - %t", so that files are named
+
+  <Artist> - <Album> - <Tracknumber> - <Title>.<extension>
+
+after renaming.
+@end defopt
+
+To perform the renaming put point on the track you want to rename or
+mark some tracks.  Then hit @kbd{R} which calls this function:
+
+@defun emms-tag-editor-rename
+Rename the file corresponding to track at point or all marked tracks
+according to the value of @code{emms-tag-editor-rename-format}.
+@end defun
+
+@node Emms Mode Line
+@chapter Emms Mode Line
+
+@cindex mode line
+@cindex display emms information
+
+We can display information about the currenty playing track on the
+Emacs mode line using the package `emms-mode-line' which is provided
+by the file @file{emms-mode-line.el}.
+
+To activate this feature invoke:
+
+@lisp
+(require 'emms-mode-line)
+(emms-mode-line 1)
+@end lisp
+
+It is also possible to display the amount of time a track has been
+playing. This feature is defined in the `emms-playing-time' package
+which is provided by the file @file{emms-playing-time.el}.
+
+To use this feature invoke:
+
+@lisp
+(require 'emms-playing-time)
+(emms-playing-time 1)
+@end lisp
+
+Note: `(emms-playing-time -1)' will disable emms-playing-time module
+completely, and is not recommended. (since some other emms modules may
+rely on it, such as `emms-lastfm.el')
+
+Instead, to toggle displaying playing time on mode line, one could call
+`emms-playing-time-enable-display' and
+`emms-playing-time-disable-display'."
+
+@defun emms-playing-time-enable-display
+Display playing time on mode line.
+@end defun
+
+@defun emms-playing-time-disable-display
+Remove playing time from mode line.
+@end defun
+
+@node Music Player Daemon
+@chapter Music Player Daemon
+
+@cindex music player daemon
+@cindex remote interface
+@cindex mpd
+
+Emms provides an interface to the @uref{http://www.musicpd.org/, Music
+Player Daemon}(MusicPD) software. The package is called `emms-player-mpd' and
+is provided by the file @file{emms-player-mpd.el}.
+
+The advantages of using MusicPD as an EMMS backend include the
+following.
+
+@itemize @bullet
+@item minimal CPU usage
+@item fast access of track information
+@item optional crossfade
+@end itemize
+
+@subheading Setup
+
+To load `emms-player-mpd' invoke:
+
+@lisp
+(require 'emms-player-mpd)
+@end lisp
+
+Set the variables @var{emms-player-mpd-server-name} and
+@var{emms-player-mpd-server-port} to the location and port
+(respectively) of your MusicPD server. For example:
+
+@lisp
+(setq emms-player-mpd-server-name "localhost")
+(setq emms-player-mpd-server-port "6600")
+@end lisp
+
+If your MusicPD setup requires a password, you will to set
+@var{emms-player-mpd-server-password} as follows.
+
+@lisp
+(setq emms-player-mpd-server-password "mypassword")
+@end lisp
+
+To get track information from MusicPD, invoke the following:
+
+@lisp
+(add-to-list 'emms-info-functions 'emms-info-mpd)
+@end lisp
+
+Adding `emms-player-mpd' to your Emms player list is accomplished by
+invoking:
+
+@lisp
+(add-to-list 'emms-player-list 'emms-player-mpd)
+@end lisp
+
+If you use absolute file names in your m3u playlists (which is most
+likely), make sure you set @var{emms-player-mpd-music-directory} to
+the value of "music_directory" from your MusicPD config.  There are
+additional options available as well, but the defaults should be
+sufficient for most uses.
+
+You can set @var{emms-player-mpd-sync-playlist} to nil if your master
+EMMS playlist contains only stored playlists.
+
+@subheading Commands provided
+
+@defun emms-player-mpd-connect
+Connect to MusicPD and retrieve its current playlist. Afterward, the
+status of MusicPD will be tracked.
+@end defun
+
+@defun emms-player-mpd-disconnect
+Terminate the MusicPD client process and disconnect from MusicPD.
+@end defun
+
+@defun emms-player-mpd-show &optional insertp
+Describe the current EMMS track in the minibuffer. If INSERTP is
+non-nil, insert the description into the current buffer instead. This
+function uses @var{emms-show-format} to format the current track. It
+differs from @command{emms-show} in that it asks MusicPD for the
+current track, rather than Emms.
+@end defun
+
+@subsubheading Updating the MusicPD database
+
+@defun emms-player-mpd-update-directory dir
+Cause the tracks in DIR to be updated in the MusicPD database.
+@end defun
+
+@defun emms-player-mpd-update-all
+Cause all tracks in the MusicPD music directory to be updated in
+the MusicPD database.
+@end defun
+
+@subsubheading emms-cache.el integration
+
+@defun emms-cache-set-from-mpd-directory dir
+Dump all MusicPD data from DIR into the EMMS cache.
+This is useful to do when you have recently acquired new music.
+@end defun
+
+@defun emms-cache-set-from-mpd-all
+Dump all MusicPD data into the EMMS cache.
+This is useful to do once, just before using emms-browser.el, in
+order to prime the cache.
+@end defun
+
+@subsubheading emms-volume.el integration
+
+To activate this, add the following to your .emacs.
+
+@lisp
+(require 'emms-volume)
+(setq emms-volume-change-function 'emms-volume-mpd-change)
+@end lisp
+
+@node Lyrics
+@chapter Lyrics
+
+@cindex lyrics
+
+We can display the lyrics of a song in time with the music using the
+`emms-lyrics' package provided by the file @file{emms-lyrics.el}.
+
+The lyrics files should have the extention ``.lrc'', and can be placed
+under either the same directory as the music files or
+@var{emms-lyrics-dir}.
+
+To add this feature we invoke:
+
+@lisp
+(require 'emms-lyrics)
+(emms-lyrics 1)
+@end lisp
+
+There are a number of variables we can set to define the way that
+`emms-lyrics' behaves, we can set these directly or by using the
+Customize feature in Emacs.
+
+@defvr {User Option} emms-lyrics-display-on-minibuffer
+If non-nil, display lyrics on minibuffer.
+@end defvr
+
+@defvr {User Option} emms-lyrics-display-on-modeline
+If non-nil, display lyrics on modeline.
+@end defvr
+
+@defvr {User Option} emms-lyrics-dir
+Local lyrics repository.
+@command{emms-lyrics-find-lyric} will look for lyrics in current
+directory(i.e., same as the music file) and this directory.
+@end defvr
+
+@defvr {User Option} emms-lyrics-display-format
+Format for displaying lyrics. "%s" will be replaced by the lyrics
+string.
+@end defvr
+
+@defvr {User Option} emms-lyrics-coding-system
+Coding system used in the output of lyrics.
+@end defvr
+
+@defvr {User Option} emms-lyrics-scroll-p
+Non-nil value will enable lyrics scrolling.
+@end defvr
+
+@defvr {User Option} emms-lyrics-scroll-timer-interval
+Interval between scroller timers. The shorter, the faster.
+@end defvr
+
+We can control `emms-lyrics' with the help of the following functions:
+
+@defun emms-lyrics-start
+Start displaying lyrics.
+@end defun
+
+@defun emms-lyrics-stop
+Stop displaying lyrics.
+@end defun
+
+@defun emms-lyrics-toggle-display-on-minibuffer
+Toggle display lyrics on minibufer.
+@end defun
+
+@defun emms-lyrics-toggle-display-on-modeline
+Toggle display lyrics on mode line.
+@end defun
+
+@defun emms-lyrics-enable
+Enable displaying Emms lyrics.
+@end defun
+
+@defun emms-lyrics-disable
+Disable displaying Emms lyrics.
+@end defun
+
+@defun emms-lyrics-toggle
+Toggle displaying Emms lyrics.
+@end defun
+
+@node Volume
+@chapter Volume
+
+@cindex volume
+
+We can use the `emms-volume' package, as provided by the
+@file{emms-volume.el} file, to manipulate the volume.
+
+@defopt emms-volume-change-amount
+The amount to use when raising or lowering the volume using the
+emms-volume interface.
+
+This should be a positive integer.
+@end defopt
+
+@defun emms-volume-raise
+Increase the volume.
+@end defun
+
+@defun emms-volume-lower
+Decrease the volume.
+@end defun
+
+If you feel like binding those two functions to global keys --- don't do
+it or you'll miss the convenience of `emms-volume-minor-mode'. Instead,
+bind the following two commands to some keys that you like.
+
+@defun emms-volume-mode-plus
+Raise volume and enable or extend the `emms-volume-minor-mode' timeout.
+@end defun
+
+@defun emms-volume-mode-minus
+Lower volume and enable or extend the `emms-volume-minor-mode' timeout.
+@end defun
+
+Example:
+
+@lisp
+(global-set-key (kbd "C-c +") 'emms-volume-mode-plus)
+(global-set-key (kbd "C-c -") 'emms-volume-mode-minus)
+@end lisp
+
+Whenever you use one of these keys or call these functions with
+@kbd{M-x}, Emms will be put into `emms-volume-minor-mode' for a short
+period defined by `emms-volume-mode-timeout'.
+
+@defopt emms-volume-mode-timeout
+The timeout in amount of seconds used by `emms-volume-minor-mode'.
+@end defopt
+
+In this interval you can raise/lower the volume simply by pressing
+@kbd{+} or @kbd{-}, which will also reset the timer to its initial
+value. So instead of pressing @kbd{C-c +} six times to increase volume
+by six steps of @code{emms-volume-change-amount}, you would simply type
+@kbd{C-c + + + + + +}.
+
+
+@node Last.fm
+@chapter Last.fm
+
+@cindex last.fm
+
+Last.fm is a popular commercial music streaming service. Last.fm
+allows a subscriber to listen to streaming music. Last.fm can also
+accept data as to what music is played locally. This information can
+be used by the Last.fm service. As usual the responsibility is on the
+user to decide how much information to share with third-parties.
+
+The emms-lastfm-client package, provided the file
+@file{emms-lastfm-client.el} provides native Last.fm support from
+within Emms.
+
+Emms does not provide a subscription to the Last.fm service, nor is it
+affiliated with the service in any way. There are restrictions on the
+use of this service. Quoting from @uref{http://www.last.fm/api/radio}:
+``Who can I stream radio to? Any API account can only stream radio to
+Last.fm's paid subscribers''.
+
+@menu
+* Last.fm Setup::            Configuring Emms to use Last.fm.
+* Last.fm Radio::            Listening to music through Last.fm
+* Last.fm Audioscrobbler::   Submitting music to Last.fm
+@end menu
+
+@node Last.fm Setup
+@section Last.fm Setup
+
+We've spoken to representatives from Last.fm and arrived at the
+following agreement: In order to be able to use the service while
+preserving the essential freedoms of the GPL each client must apply
+for their own API key from Last.fm.
+
+Here are the steps for getting authorization from Last.fm to stream
+music. Thankfully this only needs to be done once:
+
+@enumerate
+
+@item
+Complete steps 1 and 2 from
+@uref{http://www.last.fm/api/authentication} to get an API key and a
+secret key. Set the variables @var{emms-lastfm-client-api-key} and
+@var{emms-lastfm-client-api-secret-key} respectively.
+
+@item
+Invoke @kbd{M-x emms-lastfm-client-user-authorization}. On successful
+completion a browser window will open asking for confirmation to allow
+this application access to your Last.fm account. Confirm and close the
+browser.
+
+@item
+Invoke @kbd{M-x emms-lastfm-client-get-session}. On successful
+completion your permanent session key will be stored in
+@var{emms-lastfm-client-session-key-file}. As long as this value is
+accessible the authentication process need not be repeated.
+
+@end enumerate
+
+After successfully completing the above Emms should be authorized to
+access your Last.fm account.
+
+@node Last.fm Radio
+@section Last.fm Radio
+
+To show the currently streaming track invoke:
+@kbd{M-x emms-lastfm-client-show}.
+
+To display information and a photo of currently streaming artist:
+@kbd{M-x emms-lastfm-client-info}.
+
+There are three ratings you can submit while streaming audio from
+Last.fm: ``love''-ing a track, skipping to the next song (simply
+skipping also counts as a form of ``scrobbing'') and ``ban''-ing
+(which also skips).
+
+@kbd{M-x emms-lastfm-client-love-track}: ``love'' the currently
+streaming track.
+
+@kbd{M-x emms-lastfm-client-track-advance}: Skip to the next streaming
+track.
+
+@kbd{M-x emms-lastfm-client-ban-track}: ``ban'' the currently
+streaming track.
+
+Note that Last.fm streams cannot be paused or replayed. Doing so may
+cause Last.fm to suspend your account.
+
+There are a number of stations you can tune into:
+
+@kbd{M-x emms-lastfm-client-play-similar-artists}: Play
+Similar-Artists stream. You will be prompted to enter the name of the
+artist. The input will auto-complete from the Emms cache. If an artist
+is not in the Emms cache and has a name with spaces, use @kbd{C-q
+Space} to enter literal spaces.
+
+There are personal streams you can tune into:
+
+(at the time of writing these are in a state of flux as Last.fm
+depreciate old stations and bring in new ones; If a station which used
+to work doesn't anymore that is probably the reason. As Last.fm add
+and remove stations emms-lastfm-client will be updated accordingly)
+
+@kbd{M-x emms-lastfm-client-play-library}: Your Last.fm Library.
+
+@kbd{M-x emms-lastfm-client-play-loved}: Your ``loved'' tracks.
+
+@kbd{M-x emms-lastfm-client-play-recommended}: Your ``recommended'' tracks.
+
+@kbd{M-x emms-lastfm-client-play-mix}: Your ``mix'' radio.
+
+@kbd{M-x emms-lastfm-client-play-neighborhood}: Your ``neighborhood''.
+
+You can use similar commands to tune into other people's streams. For
+each of these commands you will be prompted for the Last.fm username
+of the person whose radio you wish to hear.
+
+@kbd{M-x emms-lastfm-client-play-user-library}: A Last.fm user's
+Library.
+
+@kbd{M-x emms-lastfm-client-play-user-loved}: A Last.fm user's
+``loved'' tracks.
+
+@kbd{M-x emms-lastfm-client-play-user-neighborhood}: A Last.fm user's
+``neighborhood''.
+
+@node Last.fm Audioscrobbler
+@section Last.fm Audioscrobbler
+
+Emms can submit the tracks you play to your Last.fm profile. Assuming
+you have obtained a Last.fm api key, as explained in the chapter
+@xref{Last.fm Setup}, all the audioscrobbler needs is your username in
+@var{emms-lastfm-client-username}. You can enter it with @kbd{M-x
+customize-group RET emms-lastfm}.
+
+@kbd{M-x emms-lastfm-scrobbler-enable} turns on audioscrobbling.
+
+To turn it off use @kbd{M-x emms-lastfm-scrobbler-disable}.
+
+To turn on Emms' audioscrobber in your .emacs file add:
+@lisp
+(require 'emms-lastfm-client)
+
+(setq emms-lastfm-client-username "your-lastfm-username")
+(setq emms-lastfm-client-api-key "your-lastfm-api-key")
+(setq emms-lastfm-client-api-secret-key "your-lastfm-api-secret-key")
+
+(emms-lastfm-scrobbler-enable)
+@end lisp
+
+
+@node Streaming Audio
+@chapter Streaming Audio
+
+@cindex streaming audio
+@cindex internet radio
+
+Emms provides a friendly interface for managing and playing streaming
+audio in addition to the Emms playlist interface. The interface is
+defined in the @file{emms-streams.el} package and can be loaded by
+invoking:
+
+@lisp
+(require 'emms-streams)
+@end lisp
+
+The Emms interface for streaming audio is enabled by default in the
+`emms-all' and `emms-devel' setup levels. For more information about
+Emms setup levels see @xref{Simple Setup}.
+
+Enter the emms-streams interface by invoking @kbd{M-x}
+@command{emms-streams}. The emms-streams interface comes with a
+built-in, eclectic list of streaming audio channels from throughout the
+Web. Emms can of-course play other streams than the ones listed by
+default, you are free to remove any or all of them and add your
+own.@footnote{If you enjoy a particular streaming audio station on the
+Web and think that it belongs in the default list, please send us a
+link and we will gladly add it!}
+
+If you want to play Last.fm streams, invoke the following and use the
+``lastfm'' type when adding a bookmark to a Last.fm stream.
+
+@lisp
+(require 'emms-lastfm)
+@end lisp
+
+The following is a list of the key-bindings for the emms-streams
+interface:
+
+@table @kbd
+@item RET
+@kindex RET (emms-streams)
+@vindex emms-stream-default-action
+Perform the default action when you press RET in the Emms Stream
+interface. Can be either ``add'' or ``play''. The default is ``add'',
+which adds the station under point to the Emms playlist. When
+@var{emms-stream-default-action} is ``play'' then Emms will play the
+streaming audio channel under point.
+@item q
+@kindex q (emms-streams)
+@findex emms-stream-quit
+Quit the emms-streams interface.
+@item a
+@kindex a (emms-streams)
+@findex emms-stream-add-bookmark
+Add a bookmark to a streaming audio URL to the list.
+@item d
+@kindex d (emms-streams)
+@findex emms-stream-delete-bookmark
+Remove a bookmark to a streaming audio URL from the list.
+@item e
+@kindex e (emms-streams)
+@findex emms-stream-edit-bookmark
+Edit the details of the bookmark under point.
+@item h
+@kindex h (emms-streams)
+@findex describe-mode
+Describe the emms-streams mode.
+@item n
+@kindex n (emms-streams)
+@findex emms-stream-next-line
+Move to the next line in the emms-streams buffer (same as C-n).
+@item p
+@kindex p (emms-streams)
+@findex emms-stream-previous-line
+Move to the previous line in the emms-streams buffer (same as C-p).
+@item s
+@kindex s (emms-streams)
+@findex emms-stream-save-bookmarks-file
+Save the bookmarks in the emms-streams interface to disk. The
+bookmarks will be to the location designated in the variable
+@var{emms-stream-bookmarks-file}.
+@item i
+@kindex i (emms-streams)
+@findex emms-stream-info-bookmark
+Return information about the streaming audio at the URL of the
+bookmark under point. Note that this will only work if the
+`emms-stream-info' has already been loaded.
+@end table
+
+@c including the relevant licenses
+@include gpl.texi
+@include fdl.texi
+
+@node Concept Index
+@unnumbered Concept Index
+@printindex cp
+
+@node Function Index
+@unnumbered Function Index
+@printindex fn
+
+@node Variable Index
+@unnumbered Variable Index
+@printindex vr
+
+@node Keybinding Index
+@unnumbered Keybinding Index
+@printindex ky
+
+@bye
diff --git a/.emacs.d/elisp/emms/doc/fdl.texi b/.emacs.d/elisp/emms/doc/fdl.texi
new file mode 100644 (file)
index 0000000..1276677
--- /dev/null
@@ -0,0 +1,451 @@
+@node The GNU FDL, Concept Index, Copying, Top
+@chapter GNU Free Documentation License
+
+@cindex FDL, GNU Free Documentation License
+@center Version 1.2, November 2002
+
+@display
+Copyright @copyright{} 2000,2001,2002 Free Software Foundation, Inc.
+51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+@end display
+
+@enumerate 0
+@item
+PREAMBLE
+
+The purpose of this License is to make a manual, textbook, or other
+functional and useful document @dfn{free} in the sense of freedom: to
+assure everyone the effective freedom to copy and redistribute it,
+with or without modifying it, either commercially or noncommercially.
+Secondarily, this License preserves for the author and publisher a way
+to get credit for their work, while not being considered responsible
+for modifications made by others.
+
+This License is a kind of ``copyleft'', which means that derivative
+works of the document must themselves be free in the same sense.  It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does.  But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book.  We recommend this License
+principally for works whose purpose is instruction or reference.
+
+@item
+APPLICABILITY AND DEFINITIONS
+
+This License applies to any manual or other work, in any medium, that
+contains a notice placed by the copyright holder saying it can be
+distributed under the terms of this License.  Such a notice grants a
+world-wide, royalty-free license, unlimited in duration, to use that
+work under the conditions stated herein.  The ``Document'', below,
+refers to any such manual or work.  Any member of the public is a
+licensee, and is addressed as ``you''.  You accept the license if you
+copy, modify or distribute the work in a way requiring permission
+under copyright law.
+
+A ``Modified Version'' of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A ``Secondary Section'' is a named appendix or a front-matter section
+of the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall
+subject (or to related matters) and contains nothing that could fall
+directly within that overall subject.  (Thus, if the Document is in
+part a textbook of mathematics, a Secondary Section may not explain
+any mathematics.)  The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The ``Invariant Sections'' are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License.  If a
+section does not fit the above definition of Secondary then it is not
+allowed to be designated as Invariant.  The Document may contain zero
+Invariant Sections.  If the Document does not identify any Invariant
+Sections then there are none.
+
+The ``Cover Texts'' are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License.  A Front-Cover Text may
+be at most 5 words, and a Back-Cover Text may be at most 25 words.
+
+A ``Transparent'' copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, that is suitable for revising the document
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters.  A copy made in an otherwise Transparent file
+format whose markup, or absence of markup, has been arranged to thwart
+or discourage subsequent modification by readers is not Transparent.
+An image format is not Transparent if used for any substantial amount
+of text.  A copy that is not ``Transparent'' is called ``Opaque''.
+
+Examples of suitable formats for Transparent copies include plain
+@sc{ascii} without markup, Texinfo input format, La@TeX{} input
+format, @acronym{SGML} or @acronym{XML} using a publicly available
+@acronym{DTD}, and standard-conforming simple @acronym{HTML},
+PostScript or @acronym{PDF} designed for human modification.  Examples
+of transparent image formats include @acronym{PNG}, @acronym{XCF} and
+@acronym{JPG}.  Opaque formats include proprietary formats that can be
+read and edited only by proprietary word processors, @acronym{SGML} or
+@acronym{XML} for which the @acronym{DTD} and/or processing tools are
+not generally available, and the machine-generated @acronym{HTML},
+PostScript or @acronym{PDF} produced by some word processors for
+output purposes only.
+
+The ``Title Page'' means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page.  For works in
+formats which do not have any title page as such, ``Title Page'' means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+A section ``Entitled XYZ'' means a named subunit of the Document whose
+title either is precisely XYZ or contains XYZ in parentheses following
+text that translates XYZ in another language.  (Here XYZ stands for a
+specific section name mentioned below, such as ``Acknowledgements'',
+``Dedications'', ``Endorsements'', or ``History''.)  To ``Preserve the Title''
+of such a section when you modify the Document means that it remains a
+section ``Entitled XYZ'' according to this definition.
+
+The Document may include Warranty Disclaimers next to the notice which
+states that this License applies to the Document.  These Warranty
+Disclaimers are considered to be included by reference in this
+License, but only as regards disclaiming warranties: any other
+implication that these Warranty Disclaimers may have is void and has
+no effect on the meaning of this License.
+
+@item
+VERBATIM COPYING
+
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no other
+conditions whatsoever to those of this License.  You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute.  However, you may accept
+compensation in exchange for copies.  If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+@item
+COPYING IN QUANTITY
+
+If you publish printed copies (or copies in media that commonly have
+printed covers) of the Document, numbering more than 100, and the
+Document's license notice requires Cover Texts, you must enclose the
+copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover.  Both covers must also clearly and legibly identify
+you as the publisher of these copies.  The front cover must present
+the full title with all words of the title equally prominent and
+visible.  You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a computer-network location from which the general network-using
+public has access to download using public-standard network protocols
+a complete Transparent copy of the Document, free of added material.
+If you use the latter option, you must take reasonably prudent steps,
+when you begin distribution of Opaque copies in quantity, to ensure
+that this Transparent copy will remain thus accessible at the stated
+location until at least one year after the last time you distribute an
+Opaque copy (directly or through your agents or retailers) of that
+edition to the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to give
+them a chance to provide you with an updated version of the Document.
+
+@item
+MODIFICATIONS
+
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it.  In addition, you must do these things in the Modified Version:
+
+@enumerate A
+@item
+Use in the Title Page (and on the covers, if any) a title distinct
+from that of the Document, and from those of previous versions
+(which should, if there were any, be listed in the History section
+of the Document).  You may use the same title as a previous version
+if the original publisher of that version gives permission.
+
+@item
+List on the Title Page, as authors, one or more persons or entities
+responsible for authorship of the modifications in the Modified
+Version, together with at least five of the principal authors of the
+Document (all of its principal authors, if it has fewer than five),
+unless they release you from this requirement.
+
+@item
+State on the Title page the name of the publisher of the
+Modified Version, as the publisher.
+
+@item
+Preserve all the copyright notices of the Document.
+
+@item
+Add an appropriate copyright notice for your modifications
+adjacent to the other copyright notices.
+
+@item
+Include, immediately after the copyright notices, a license notice
+giving the public permission to use the Modified Version under the
+terms of this License, in the form shown in the Addendum below.
+
+@item
+Preserve in that license notice the full lists of Invariant Sections
+and required Cover Texts given in the Document's license notice.
+
+@item
+Include an unaltered copy of this License.
+
+@item
+Preserve the section Entitled ``History'', Preserve its Title, and add
+to it an item stating at least the title, year, new authors, and
+publisher of the Modified Version as given on the Title Page.  If
+there is no section Entitled ``History'' in the Document, create one
+stating the title, year, authors, and publisher of the Document as
+given on its Title Page, then add an item describing the Modified
+Version as stated in the previous sentence.
+
+@item
+Preserve the network location, if any, given in the Document for
+public access to a Transparent copy of the Document, and likewise
+the network locations given in the Document for previous versions
+it was based on.  These may be placed in the ``History'' section.
+You may omit a network location for a work that was published at
+least four years before the Document itself, or if the original
+publisher of the version it refers to gives permission.
+
+@item
+For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve
+the Title of the section, and preserve in the section all the
+substance and tone of each of the contributor acknowledgements and/or
+dedications given therein.
+
+@item
+Preserve all the Invariant Sections of the Document,
+unaltered in their text and in their titles.  Section numbers
+or the equivalent are not considered part of the section titles.
+
+@item
+Delete any section Entitled ``Endorsements''.  Such a section
+may not be included in the Modified Version.
+
+@item
+Do not retitle any existing section to be Entitled ``Endorsements'' or
+to conflict in title with any Invariant Section.
+
+@item
+Preserve any Warranty Disclaimers.
+@end enumerate
+
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant.  To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section Entitled ``Endorsements'', provided it contains
+nothing but endorsements of your Modified Version by various
+parties---for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version.  Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity.  If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+@item
+COMBINING DOCUMENTS
+
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice, and that you preserve all their Warranty Disclaimers.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy.  If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections Entitled ``History''
+in the various original documents, forming one section Entitled
+``History''; likewise combine any sections Entitled ``Acknowledgements'',
+and any sections Entitled ``Dedications''.  You must delete all
+sections Entitled ``Endorsements.''
+
+@item
+COLLECTIONS OF DOCUMENTS
+
+You may make a collection consisting of the Document and other documents
+released under this License, and replace the individual copies of this
+License in the various documents with a single copy that is included in
+the collection, provided that you follow the rules of this License for
+verbatim copying of each of the documents in all other respects.
+
+You may extract a single document from such a collection, and distribute
+it individually under this License, provided you insert a copy of this
+License into the extracted document, and follow this License in all
+other respects regarding verbatim copying of that document.
+
+@item
+AGGREGATION WITH INDEPENDENT WORKS
+
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, is called an ``aggregate'' if the copyright
+resulting from the compilation is not used to limit the legal rights
+of the compilation's users beyond what the individual works permit.
+When the Document is included in an aggregate, this License does not
+apply to the other works in the aggregate which are not themselves
+derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one half of
+the entire aggregate, the Document's Cover Texts may be placed on
+covers that bracket the Document within the aggregate, or the
+electronic equivalent of covers if the Document is in electronic form.
+Otherwise they must appear on printed covers that bracket the whole
+aggregate.
+
+@item
+TRANSLATION
+
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections.  You may include a
+translation of this License, and all the license notices in the
+Document, and any Warranty Disclaimers, provided that you also include
+the original English version of this License and the original versions
+of those notices and disclaimers.  In case of a disagreement between
+the translation and the original version of this License or a notice
+or disclaimer, the original version will prevail.
+
+If a section in the Document is Entitled ``Acknowledgements'',
+``Dedications'', or ``History'', the requirement (section 4) to Preserve
+its Title (section 1) will typically require changing the actual
+title.
+
+@item
+TERMINATION
+
+You may not copy, modify, sublicense, or distribute the Document except
+as expressly provided for under this License.  Any other attempt to
+copy, modify, sublicense or distribute the Document is void, and will
+automatically terminate your rights under this License.  However,
+parties who have received copies, or rights, from you under this
+License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+@item
+FUTURE REVISIONS OF THIS LICENSE
+
+The Free Software Foundation may publish new, revised versions
+of the GNU Free Documentation License from time to time.  Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.  See
+@uref{http://www.gnu.org/copyleft/}.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License ``or any later version'' applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation.  If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation.
+@end enumerate
+
+@page
+@section ADDENDUM: How to use this License for your documents
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+@smallexample
+@group
+  Copyright (C)  @var{year}  @var{your name}.
+  Permission is granted to copy, distribute and/or modify this document
+  under the terms of the GNU Free Documentation License, Version 1.2
+  or any later version published by the Free Software Foundation;
+  with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+  Texts.  A copy of the license is included in the section entitled ``GNU
+  Free Documentation License''.
+@end group
+@end smallexample
+
+If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
+replace the ``with...Texts.'' line with this:
+
+@smallexample
+@group
+    with the Invariant Sections being @var{list their titles}, with
+    the Front-Cover Texts being @var{list}, and with the Back-Cover Texts
+    being @var{list}.
+@end group
+@end smallexample
+
+If you have Invariant Sections without Cover Texts, or some other
+combination of the three, merge those two alternatives to suit the
+situation.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
+
+@c Local Variables:
+@c ispell-local-pdict: "ispell-dict"
+@c End:
+
diff --git a/.emacs.d/elisp/emms/doc/gpl.texi b/.emacs.d/elisp/emms/doc/gpl.texi
new file mode 100644 (file)
index 0000000..877994e
--- /dev/null
@@ -0,0 +1,725 @@
+@node Copying, The GNU FDL, Extending Emms, Top
+
+@unnumbered GNU General Public License
+@center Version 3, 29 June 2007
+
+@c This file is intended to be included in another file.
+
+@display
+Copyright @copyright{} 2007 Free Software Foundation, Inc. @url{http://fsf.org/}
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+@end display
+
+@unnumberedsec Preamble
+
+The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom
+to share and change all versions of a program--to make sure it remains
+free software for all its users.  We, the Free Software Foundation,
+use the GNU General Public License for most of our software; it
+applies also to any other work released this way by its authors.  You
+can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you
+have certain responsibilities if you distribute copies of the
+software, or if you modify it: responsibilities to respect the freedom
+of others.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too,
+receive or can get the source code.  And you must show them these
+terms so they know their rights.
+
+Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the
+manufacturer can do so.  This is fundamentally incompatible with the
+aim of protecting users' freedom to change the software.  The
+systematic pattern of such abuse occurs in the area of products for
+individuals to use, which is precisely where it is most unacceptable.
+Therefore, we have designed this version of the GPL to prohibit the
+practice for those products.  If such problems arise substantially in
+other domains, we stand ready to extend this provision to those
+domains in future versions of the GPL, as needed to protect the
+freedom of users.
+
+Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish
+to avoid the special danger that patents applied to a free program
+could make it effectively proprietary.  To prevent this, the GPL
+assures that patents cannot be used to render the program non-free.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+@iftex
+@unnumberedsec TERMS AND CONDITIONS
+@end iftex
+@ifinfo
+@center TERMS AND CONDITIONS
+@end ifinfo
+
+@enumerate 0
+@item Definitions.
+
+``This License'' refers to version 3 of the GNU General Public License.
+
+``Copyright'' also means copyright-like laws that apply to other kinds
+of works, such as semiconductor masks.
+
+``The Program'' refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as ``you''.  ``Licensees'' and
+``recipients'' may be individuals or organizations.
+
+To ``modify'' a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of
+an exact copy.  The resulting work is called a ``modified version'' of
+the earlier work or a work ``based on'' the earlier work.
+
+A ``covered work'' means either the unmodified Program or a work based
+on the Program.
+
+To ``propagate'' a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+To ``convey'' a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user
+through a computer network, with no transfer of a copy, is not
+conveying.
+
+An interactive user interface displays ``Appropriate Legal Notices'' to
+the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+@item Source Code.
+
+The ``source code'' for a work means the preferred form of the work for
+making modifications to it.  ``Object code'' means any non-source form
+of a work.
+
+A ``Standard Interface'' means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+The ``System Libraries'' of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+``Major Component'', in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+The ``Corresponding Source'' for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+The Corresponding Source need not include anything that users can
+regenerate automatically from other parts of the Corresponding Source.
+
+The Corresponding Source for a work in source code form is that same
+work.
+
+@item Basic Permissions.
+
+All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+You may make, run and propagate covered works that you do not convey,
+without conditions so long as your license otherwise remains in force.
+You may convey covered works to others for the sole purpose of having
+them make modifications exclusively for you, or provide you with
+facilities for running those works, provided that you comply with the
+terms of this License in conveying all material for which you do not
+control copyright.  Those thus making or running the covered works for
+you must do so exclusively on your behalf, under your direction and
+control, on terms that prohibit them from making any copies of your
+copyrighted material outside their relationship with you.
+
+Conveying under any other circumstances is permitted solely under the
+conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+@item Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such
+circumvention is effected by exercising rights under this License with
+respect to the covered work, and you disclaim any intention to limit
+operation or modification of the work as a means of enforcing, against
+the work's users, your or third parties' legal rights to forbid
+circumvention of technological measures.
+
+@item Conveying Verbatim Copies.
+
+You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+@item Conveying Modified Source Versions.
+
+You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these
+conditions:
+
+@enumerate a
+@item
+The work must carry prominent notices stating that you modified it,
+and giving a relevant date.
+
+@item
+The work must carry prominent notices stating that it is released
+under this License and any conditions added under section 7.  This
+requirement modifies the requirement in section 4 to ``keep intact all
+notices''.
+
+@item
+You must license the entire work, as a whole, under this License to
+anyone who comes into possession of a copy.  This License will
+therefore apply, along with any applicable section 7 additional terms,
+to the whole of the work, and all its parts, regardless of how they
+are packaged.  This License gives no permission to license the work in
+any other way, but it does not invalidate such permission if you have
+separately received it.
+
+@item
+If the work has interactive user interfaces, each must display
+Appropriate Legal Notices; however, if the Program has interactive
+interfaces that do not display Appropriate Legal Notices, your work
+need not make them do so.
+@end enumerate
+
+A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+``aggregate'' if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+@item  Conveying Non-Source Forms.
+
+You may convey a covered work in object code form under the terms of
+sections 4 and 5, provided that you also convey the machine-readable
+Corresponding Source under the terms of this License, in one of these
+ways:
+
+@enumerate a
+@item
+Convey the object code in, or embodied in, a physical product
+(including a physical distribution medium), accompanied by the
+Corresponding Source fixed on a durable physical medium customarily
+used for software interchange.
+
+@item
+Convey the object code in, or embodied in, a physical product
+(including a physical distribution medium), accompanied by a written
+offer, valid for at least three years and valid for as long as you
+offer spare parts or customer support for that product model, to give
+anyone who possesses the object code either (1) a copy of the
+Corresponding Source for all the software in the product that is
+covered by this License, on a durable physical medium customarily used
+for software interchange, for a price no more than your reasonable
+cost of physically performing this conveying of source, or (2) access
+to copy the Corresponding Source from a network server at no charge.
+
+@item
+Convey individual copies of the object code with a copy of the written
+offer to provide the Corresponding Source.  This alternative is
+allowed only occasionally and noncommercially, and only if you
+received the object code with such an offer, in accord with subsection
+6b.
+
+@item
+Convey the object code by offering access from a designated place
+(gratis or for a charge), and offer equivalent access to the
+Corresponding Source in the same way through the same place at no
+further charge.  You need not require recipients to copy the
+Corresponding Source along with the object code.  If the place to copy
+the object code is a network server, the Corresponding Source may be
+on a different server (operated by you or a third party) that supports
+equivalent copying facilities, provided you maintain clear directions
+next to the object code saying where to find the Corresponding Source.
+Regardless of what server hosts the Corresponding Source, you remain
+obligated to ensure that it is available for as long as needed to
+satisfy these requirements.
+
+@item
+Convey the object code using peer-to-peer transmission, provided you
+inform other peers where the object code and Corresponding Source of
+the work are being offered to the general public at no charge under
+subsection 6d.
+
+@end enumerate
+
+A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+A ``User Product'' is either (1) a ``consumer product'', which means any
+tangible personal property which is normally used for personal,
+family, or household purposes, or (2) anything designed or sold for
+incorporation into a dwelling.  In determining whether a product is a
+consumer product, doubtful cases shall be resolved in favor of
+coverage.  For a particular product received by a particular user,
+``normally used'' refers to a typical or common use of that class of
+product, regardless of the status of the particular user or of the way
+in which the particular user actually uses, or expects or is expected
+to use, the product.  A product is a consumer product regardless of
+whether the product has substantial commercial, industrial or
+non-consumer uses, unless such uses represent the only significant
+mode of use of the product.
+
+``Installation Information'' for a User Product means any methods,
+procedures, authorization keys, or other information required to
+install and execute modified versions of a covered work in that User
+Product from a modified version of its Corresponding Source.  The
+information must suffice to ensure that the continued functioning of
+the modified object code is in no case prevented or interfered with
+solely because modification has been made.
+
+If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or
+updates for a work that has been modified or installed by the
+recipient, or for the User Product in which it has been modified or
+installed.  Access to a network may be denied when the modification
+itself materially and adversely affects the operation of the network
+or violates the rules and protocols for communication across the
+network.
+
+Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+@item Additional Terms.
+
+``Additional permissions'' are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders
+of that material) supplement the terms of this License with terms:
+
+@enumerate a
+@item
+Disclaiming warranty or limiting liability differently from the terms
+of sections 15 and 16 of this License; or
+
+@item
+Requiring preservation of specified reasonable legal notices or author
+attributions in that material or in the Appropriate Legal Notices
+displayed by works containing it; or
+
+@item
+Prohibiting misrepresentation of the origin of that material, or
+requiring that modified versions of such material be marked in
+reasonable ways as different from the original version; or
+
+@item
+Limiting the use for publicity purposes of names of licensors or
+authors of the material; or
+
+@item
+Declining to grant rights under trademark law for use of some trade
+names, trademarks, or service marks; or
+
+@item
+Requiring indemnification of licensors and authors of that material by
+anyone who conveys the material (or modified versions of it) with
+contractual assumptions of liability to the recipient, for any
+liability that these contractual assumptions directly impose on those
+licensors and authors.
+@end enumerate
+
+All other non-permissive additional terms are considered ``further
+restrictions'' within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions; the
+above requirements apply either way.
+
+@item Termination.
+
+You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+However, if you cease all violation of this License, then your license
+from a particular copyright holder is reinstated (a) provisionally,
+unless and until the copyright holder explicitly and finally
+terminates your license, and (b) permanently, if the copyright holder
+fails to notify you of the violation by some reasonable means prior to
+60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+@item Acceptance Not Required for Having Copies.
+
+You are not required to accept this License in order to receive or run
+a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+@item Automatic Licensing of Downstream Recipients.
+
+Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+An ``entity transaction'' is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+@item Patents.
+
+A ``contributor'' is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's ``contributor version''.
+
+A contributor's ``essential patent claims'' are all patent claims owned
+or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, ``control'' includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+In the following three paragraphs, a ``patent license'' is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To ``grant'' such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  ``Knowingly relying'' means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+A patent license is ``discriminatory'' if it does not include within the
+scope of its coverage, prohibits the exercise of, or is conditioned on
+the non-exercise of one or more of the rights that are specifically
+granted under this License.  You may not convey a covered work if you
+are a party to an arrangement with a third party that is in the
+business of distributing software, under which you make payment to the
+third party based on the extent of your activity of conveying the
+work, and under which the third party grants, to any of the parties
+who would receive the covered work from you, a discriminatory patent
+license (a) in connection with copies of the covered work conveyed by
+you (or copies made from those copies), or (b) primarily for and in
+connection with specific products or compilations that contain the
+covered work, unless you entered into that arrangement, or that patent
+license was granted, prior to 28 March 2007.
+
+Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+@item No Surrender of Others' Freedom.
+
+If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey
+a covered work so as to satisfy simultaneously your obligations under
+this License and any other pertinent obligations, then as a
+consequence you may not convey it at all.  For example, if you agree
+to terms that obligate you to collect a royalty for further conveying
+from those to whom you convey the Program, the only way you could
+satisfy both those terms and this License would be to refrain entirely
+from conveying the Program.
+
+@item Use with the GNU Affero General Public License.
+
+Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+@item Revised Versions of this License.
+
+The Free Software Foundation may publish revised and/or new versions
+of the GNU General Public License from time to time.  Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies that a certain numbered version of the GNU General Public
+License ``or any later version'' applies to it, you have the option of
+following the terms and conditions either of that numbered version or
+of any later version published by the Free Software Foundation.  If
+the Program does not specify a version number of the GNU General
+Public License, you may choose any version ever published by the Free
+Software Foundation.
+
+If the Program specifies that a proxy can decide which future versions
+of the GNU General Public License can be used, that proxy's public
+statement of acceptance of a version permanently authorizes you to
+choose that version for the Program.
+
+Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+@item Disclaimer of Warranty.
+
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM ``AS IS'' WITHOUT
+WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND
+PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
+DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+@item Limitation of Liability.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR
+CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT
+NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR
+LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM
+TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
+PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+@item Interpretation of Sections 15 and 16.
+
+If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+@iftex
+@heading END OF TERMS AND CONDITIONS
+@end iftex
+@ifinfo
+@center END OF TERMS AND CONDITIONS
+@end ifinfo
+@unnumberedsec How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the ``copyright'' line and a pointer to where the full notice is found.
+@smallexample
+@var{one line to give the program's name and a brief idea of what it does.}
+Copyright (C) @var{year} @var{name of author}
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see @url{http://www.gnu.org/licenses/}.
+@end smallexample
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+@smallexample
+@var{program} Copyright (C) @var{year} @var{name of author}
+This program comes with ABSOLUTELY NO WARRANTY; for details type @samp{show w}.
+This is free software, and you are welcome to redistribute it under certain conditions; type @samp{show c} for details.
+@end smallexample
+
+The hypothetical commands @samp{show w} and @samp{show c} should show
+the appropriate parts of the General Public License.  Of course, your
+program's commands might be different; for a GUI interface, you would
+use an ``about box''.
+
+You should also get your employer (if you work as a programmer) or school,
+if any, to sign a ``copyright disclaimer'' for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+@url{http://www.gnu.org/licenses/}.
+
+The GNU General Public License does not permit incorporating your
+program into proprietary programs.  If your program is a subroutine
+library, you may consider it more useful to permit linking proprietary
+applications with the library.  If this is what you want to do, use
+the GNU Lesser General Public License instead of this License.  But
+first, please read @url{http://www.gnu.org/philosophy/why-not-lgpl.html}.
+
+@end enumerate
diff --git a/.emacs.d/elisp/emms/emms-print-metadata.1 b/.emacs.d/elisp/emms/emms-print-metadata.1
new file mode 100644 (file)
index 0000000..cd1149f
--- /dev/null
@@ -0,0 +1,34 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH EMMS-PRINT-METADATA 1 "April 29, 2006" EMMS
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh        disable hyphenation
+.\" .hy        enable hyphenation
+.\" .ad l      left justify
+.\" .ad b      justify to both left and right margins
+.\" .nf        disable filling
+.\" .fi        enable filling
+.\" .br        insert line break
+.\" .sp <n>    insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+emms-print-metadata \- Print information about music files
+.SH SYNOPSIS
+.B emms-print-metadata
+.RI file.ext
+.br
+.SH DESCRIPTION
+.\" TeX users may be more comfortable with the \fB<whatever>\fP and
+.\" \fI<whatever>\fP escape sequences to invoke bold face and italics,
+.\" respectively.
+\fBemms-print-metadata\fP will print metadata about music files to
+stdout, to be used primarily by EMMS, the Emacs MultiMedia System.
+.SH SEE ALSO
+.BR emms (info)
+.br
+.SH AUTHOR
+emms-print-metadata was written by Trent Buck.
diff --git a/.emacs.d/elisp/emms/lisp/Makefile b/.emacs.d/elisp/emms/lisp/Makefile
new file mode 100644 (file)
index 0000000..85ff3e1
--- /dev/null
@@ -0,0 +1,28 @@
+EMACS=emacs
+SITEFLAG=--no-site-file
+ALLSOURCE=$(wildcard *.el)
+ALLCOMPILED=$(wildcard *.elc)
+SPECIAL=emms-auto.el emms-maint.el
+SOURCE=$(filter-out $(SPECIAL),$(ALLSOURCE))
+TARGET=$(patsubst %.el,%.elc,$(SOURCE))
+
+.PHONY: all clean
+.PRECIOUS: %.elc
+all: emms-auto.el $(TARGET)
+
+emms-auto.el: emms-auto.in $(SOURCE)
+       cp emms-auto.in emms-auto.el
+       -rm -f emms-auto.elc
+       @$(EMACS) -q $(SITEFLAG) -batch \
+               -l emms-maint.el \
+               -l emms-auto.el \
+               -f emms-generate-autoloads \
+               $(shell pwd)/emms-auto.el .
+
+%.elc: %.el
+       @$(EMACS) -q $(SITEFLAG) -batch \
+               -l emms-maint.el \
+               -f batch-byte-compile $<
+
+clean:
+       -rm -f *~ *.elc emms-auto.el
diff --git a/.emacs.d/elisp/emms/lisp/emms-auto.in b/.emacs.d/elisp/emms/lisp/emms-auto.in
new file mode 100644 (file)
index 0000000..9426862
--- /dev/null
@@ -0,0 +1,13 @@
+;;; -*-emacs-lisp-*-
+
+(defvar generated-autoload-file)
+(defvar command-line-args-left)
+(defun emms-generate-autoloads ()
+  (interactive)
+  (require 'autoload)
+  (setq generated-autoload-file (car command-line-args-left))
+  (setq command-line-args-left (cdr command-line-args-left))
+  (batch-update-autoloads))
+
+(provide 'emms-auto)
+;;; Generated autoloads follow (made by autoload.el).
diff --git a/.emacs.d/elisp/emms/lisp/emms-bookmarks.el b/.emacs.d/elisp/emms/lisp/emms-bookmarks.el
new file mode 100644 (file)
index 0000000..e3b1747
--- /dev/null
@@ -0,0 +1,153 @@
+;;; emms-bookmarks.el --- Bookmarks for Emms.
+
+;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+
+;; Author: Yoni Rabkin <yonirabkin@member.fsf.org>
+;; Keywords: emms, bookmark
+
+;; This file is part of EMMS.
+
+;; EMMS is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+;;
+;; EMMS is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with EMMS; if not, write to the Free Software Foundation,
+;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+;;
+;; You can use this to add ``temporal bookmarks'' (term by Lucas
+;; Bonnet) to your media files.  The interesting functions here are
+;; `emms-bookmarks-next', `emms-bookmarks-prev', `emms-bookmarks-add'
+;; (which pauses the player while you describe the bookmark) and
+;; `emms-bookmarks-clear'.  All of which do exactly what you think
+;; they do.
+
+;;; Code:
+
+\f
+;; dependencies
+(require 'emms)
+(require 'emms-playing-time)
+
+(defvar emms-bookmarks-prev-overshoot 5
+  "Time in seconds for skipping a previous bookmark.")
+
+(defun emms-bookmarks-reset (track)
+  "Remove all the bookmarks from TRACK."
+  (emms-track-set track 'bookmarks nil))
+
+(defun emms-bookmarks-straight-insertion-sort (item l acc)
+  "Insert ITEM into the already sorted L, ACC should be nil."
+  (if (null l)
+      (append acc (list item))
+    (cond ((< (cdr item) (cdr (car l))) (append acc (list item (car l)) (cdr l)))
+         (t (emms-bookmarks-straight-insertion-sort item (cdr l) (append acc (list (car l))))))))
+
+(defun emms-bookmarks-get (track)
+  "Return the bookmark property from TRACK."
+  (emms-track-get track 'bookmarks))
+
+(defun emms-bookmarks-set (track desc time)
+  "Set bookmark property for TRACK, text DESC at TIME seconds."
+  (let ((old-bookmarks (emms-track-get track 'bookmarks))
+       (new-bookmarks nil))
+    (setq new-bookmarks (emms-bookmarks-straight-insertion-sort (cons desc time) old-bookmarks nil))
+    (emms-track-set track 'bookmarks new-bookmarks)))
+
+(defun emms-bookmarks-set-current (desc)
+  "Set bookmark property for the current track with text DESC."
+  (emms-bookmarks-set (emms-playlist-current-selected-track) desc emms-playing-time))
+
+(defun emms-bookmarks-search (time track test)
+  "Return a bookmark based on heuristics.
+
+TIME should be a reference point in seconds.
+TRACK should be an Emms track.
+TEST should be a numerical comparator predicate."
+  (let ((s (append (list (cons "time" time)) (copy-sequence (emms-bookmarks-get track)))))
+    (sort s #'(lambda (a b) (funcall test (cdr a) (cdr b))))
+    (while (not (= time (cdar s)))
+      (setq s (cdr s)))
+    (when (cdr s)
+      (car (cdr s)))))
+
+(defun emms-bookmarks-next-1 (time track)
+  "Return the bookmark after TIME for TRACK, otherwise return nil."
+  (emms-bookmarks-search time track #'<))
+
+(defun emms-bookmarks-prev-1 (time track)
+  "Return the bookmark before TIME for TRACK, otherwise return nil."
+  (emms-bookmarks-search (- time emms-bookmarks-prev-overshoot) track #'>))
+
+(defun emms-bookmarks-goto (search-f track failure-message)
+  "Seek the player to a bookmark.
+
+SEARCH-F should be a function which returns a bookmark.
+TRACK should be an Emms track.
+FAILURE-MESSAGE should be a string."
+  ;; note that when emms is paused then `emms-player-playing-p' => t
+  (when (not emms-player-playing-p)
+    (emms-start))
+  (let ((m (funcall search-f emms-playing-time track)))
+    (if m
+       (progn
+         (emms-player-seek-to (cdr m))
+         (message "%s" (car m)))
+      (message "%s" failure-message))))
+
+\f
+;; entry points
+
+(defun emms-bookmarks-next ()
+  "Seek to the next bookmark in the current track."
+  (interactive)
+  (emms-bookmarks-goto #'emms-bookmarks-next-1
+                      (emms-playlist-current-selected-track)
+                      "No next bookmark"))
+
+(defun emms-bookmarks-prev ()
+  "Seek to the previous bookmark in the current track."
+  (interactive)
+  (emms-bookmarks-goto #'emms-bookmarks-prev-1
+                      (emms-playlist-current-selected-track)
+                      "No previous bookmark"))
+
+(defmacro emms-bookmarks-with-paused-player (&rest body)
+  "Eval BODY with player paused."
+  `(progn
+     (when (not emms-player-paused-p) (emms-pause))
+     ,@body
+     (when emms-player-paused-p (emms-pause))))
+
+;; can't use `interactive' to promt the user here because we want to
+;; pause the player before the prompt appears.
+(defun emms-bookmarks-add ()
+  "Add a new bookmark to the current track.
+
+This function pauses the player while prompting the user for a
+description of the bookmark.  The function resumes the player
+after the prompt."
+  (interactive)
+  (emms-bookmarks-with-paused-player
+   (let ((desc (read-string "Description: ")))
+     (if (emms-playlist-current-selected-track)
+        (emms-bookmarks-set-current desc)
+       (error "No current track to bookmark")))))
+
+(defun emms-bookmarks-clear ()
+  "Remove all the bookmarks from the current track."
+  (interactive)
+  (let ((this (emms-playlist-current-selected-track)))
+    (when this (emms-bookmarks-reset this))))
+
+(provide 'emms-bookmarks)
+
+;;; emms-bookmarks.el ends here
diff --git a/.emacs.d/elisp/emms/lisp/emms-browser.el b/.emacs.d/elisp/emms/lisp/emms-browser.el
new file mode 100644 (file)
index 0000000..271ac73
--- /dev/null
@@ -0,0 +1,1982 @@
+;;; emms-browser.el --- a track browser supporting covers and filtering
+
+;; Copyright (C) 2006, 2007, 2008, 2009  Free Software Foundation, Inc.
+
+;; Author: Damien Elmes <emacs@repose.cx>
+;; Keywords: emms, mp3, mpeg, multimedia
+
+;; This file is part of EMMS.
+
+;; EMMS is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; EMMS is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with EMMS; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; This code allows you to browse the metadata cache and add tracks to
+;; your playlist. To be properly useful, you should M-x
+;; emms-add-directory-tree to all the files you own at least once so
+;; that the cache is fully populated.
+
+;; Usage
+;; -------------------------------------------------------------------
+
+;; To use, run (emms-all) and then bind `emms-smart-browse' to a key,
+;; like:
+
+;; (global-set-key (kbd "<f2>") 'emms-smart-browse)
+
+;; The 'smart browsing' code attempts to link the browser and playlist
+;; windows together, so that closing one will close both. Activating
+;; it will toggle between three states:
+
+;; a) both windows displayed, with the browser focused
+;; b) focus switched to the playlist window
+;; c) the extra window closed, and both buffers buried
+
+;; If you just want access to the browser, try M-x
+;; emms-browse-by-TYPE, where TYPE is one of artist, album, composer,
+;; genre or year. These commands can also be used while smart browsing to
+;; change the browsing category.
+
+;; If you don't want to activate the code with (emms-devel), you can
+;; activate it manually with:
+
+;; (require 'emms-browser)
+
+;; Key bindings
+;; -------------------------------------------------------------------
+
+;; C-j             emms-browser-add-tracks-and-play
+;; RET             emms-browser-add-tracks
+;; SPC             emms-browser-toggle-subitems
+;; /               emms-isearch-buffer
+;; 1               emms-browser-collapse-all
+;; 2               emms-browser-expand-to-level-2
+;; 3               emms-browser-expand-to-level-3
+;; 4               emms-browser-expand-to-level-4
+;; <               emms-browser-previous-filter
+;; >               emms-browser-next-filter
+;; ?               describe-mode
+;; C               emms-browser-clear-playlist
+;; E               emms-browser-expand-all
+;; d               emms-browser-view-in-dired
+;; D               emms-browser-delete-files
+;; q               emms-browser-bury-buffer
+;; r               emms-browser-goto-random
+;; n               next-line
+;; p               previous-line
+;; C-/             emms-playlist-mode-undo
+;; <C-return>      emms-browser-add-tracks-and-play
+;; <backtab>       emms-browser-prev-non-track
+;; <tab>           emms-browser-next-non-track
+
+;; s A             emms-browser-search-by-album
+;; s a             emms-browser-search-by-artist
+;; s c             emms-browser-search-by-composer
+;; s s             emms-browser-search-by-names
+;; s t             emms-browser-search-by-title
+;; s p             emms-browser-search-by-performer
+
+;; b 1             emms-browse-by-artist
+;; b 2             emms-browse-by-album
+;; b 3             emms-browse-by-genre
+;; b 4             emms-browse-by-year
+;; b 5             emms-browse-by-composer
+;; b 6             emms-browse-by-performer
+
+;; W a p           emms-browser-lookup-album-on-pitchfork
+;; W a w           emms-browser-lookup-album-on-wikipedia
+
+;; W A p           emms-browser-lookup-artist-on-pitchfork
+;; W A w           emms-browser-lookup-artist-on-wikipedia
+
+;; W C p           emms-browser-lookup-composer-on-pitchfork
+;; W C w           emms-browser-lookup-composer-on-wikipedia
+
+;; W P p           emms-browser-lookup-performer-on-pitchfork
+;; W P w           emms-browser-lookup-performer-on-wikipedia
+
+;; Displaying covers
+;; -------------------------------------------------------------------
+
+;; The browser will attempt to display cover images if they're
+;; available. By default it looks for images cover_small.jpg,
+;; cover_med.jpg, etc. Customize emms-browser-covers to use your own
+;; covers. Note that you'll probably want to resize your existing
+;; covers to particular sizes. Suggested sizes are 100x100 for small,
+;; and 200x200 for medium.
+
+;; Also emacs by default will jump around a lot when scrolling a
+;; buffer with images. Set the following variables to prevent that:
+
+;;   scroll-up-aggressively 0.0
+;;   scroll-down-aggressively 0.0
+
+;; To show a 'no cover' image for albums which don't have a cover, add
+;; the following code to your .emacs:
+
+;; (setq emms-browser-default-covers
+;;   (list "/path/to/cover_small.jpg" nil nil)
+
+;; (the medium and large images can be set too, if you want)
+
+;; You can download an example 'no cover' image from:
+;; http://repose.cx/cover_small.jpg
+
+;; Filtering tracks
+;; -------------------------------------------------------------------
+
+;; If you want to display a subset of your collection (such as a
+;; directory of 80s music, only avi files, etc), then you can make
+;; some filters using code like this:
+
+;; ;; show everything
+;; (emms-browser-make-filter "all" 'ignore)
+
+;; ;; Set "all" as the default filter
+;; (emms-browser-set-filter (assoc "all" emms-browser-filters))
+
+;; ;; show all files (no streamlists, etc)
+;; (emms-browser-make-filter
+;;  "all-files" (emms-browser-filter-only-type 'file))
+
+;; ;; show only tracks in one folder
+;; (emms-browser-make-filter
+;;  "80s" (emms-browser-filter-only-dir "~/Mp3s/80s"))
+
+;; ;; show all tracks played in the last month
+;; (emms-browser-make-filter
+;;  "last-month" (emms-browser-filter-only-recent 30))
+
+;; After executing the above commands, you can use M-x
+;; emms-browser-show-all, emms-browser-show-80s, etc to toggle
+;; between different collections. Alternatively you can use '<' and
+;; '>' to cycle through the available filters.
+
+;; The second argument to make-filter is a function which returns t if
+;; a single track should be filtered. You can write your own filter
+;; functions to check the type of a file, etc.
+
+;; Some more examples:
+
+;; ;; show only tracks not played in the last year
+;; (emms-browser-make-filter "not-played"
+;;  (lambda (track)
+;;   (not (funcall (emms-browser-filter-only-recent 365) track))))
+
+;; ;; show all files that are not in the pending directory
+;; (emms-browser-make-filter
+;;  "all"
+;;  (lambda (track)
+;;    (or
+;;     (funcall (emms-browser-filter-only-type 'file) track)
+;;     (not (funcall
+;;           (emms-browser-filter-only-dir "~/Media/pending") track)))))
+
+;; Changing tree structure
+;; -------------------------------------------------------------------
+
+;; You can change the way the tree is displayed by modifying
+;; `emms-browser-next-mapping-type'. The following code displays
+;; artist->track instead of artist->album->track when you switch to
+;; the 'singles' filter.
+
+;; (defadvice emms-browser-next-mapping-type
+;;                                 (after no-album (current-mapping))
+;;   (when (eq ad-return-value 'info-album)
+;;     (setq ad-return-value 'info-title)))
+
+;; (defun toggle-album-display ()
+;;   (if (string= emms-browser-current-filter-name "singles")
+;;       (ad-activate 'emms-browser-next-mapping-type)
+;;     (ad-deactivate 'emms-browser-next-mapping-type)))
+
+;; (add-hook 'emms-browser-filter-changed-hook 'toggle-album-display)
+
+;; Changing display format
+;; -------------------------------------------------------------------
+
+;; Format strings govern the way items are displayed in the browser
+;; and playlist. You can customize these if you wish.
+
+;; `emms-browser-default-format' controls the format to use when no
+;; other format has been explicitly defined. By default, only track and
+;; albums deviate from the default.
+
+;; To customise the format of a particular type, find the name of the
+;; field you want to use (eg `info-artist', `info-title', etc), and
+;; insert that into emms-browser-<type>-format or
+;; emms-browser-playlist-<type>-format. For example, if you wanted to
+;; remove track numbers from tracks in both the browser and playlist,
+;; you could do:
+
+;; (defvar emms-browser-info-title-format "%i%n")
+;; (defvar emms-browser-playlist-info-title-format
+;;   emms-browser-info-title-format)
+
+;; The format specifiers available include:
+
+;; %i    indent relative to the current level
+;; %n    the value of the item - eg -info-artist might be "pink floyd"
+;; %y    the album year
+;; %A    the album name
+;; %a    the artist name of the track
+;; %C    the composer name of the track
+;; %p    the performer name of the track
+;; %t    the title of the track
+;; %T    the track number
+;; %cS   a small album cover
+;; %cM   a medium album cover
+;; %cL   a big album cover
+
+;; Note that if you use track-related items like %t, it will take the
+;; data from the first track.
+
+;; Changing display faces
+;; -------------------------------------------------------------------
+
+;; The faces used to display the various fields are also customizable.
+;; They are in the format emms-browser-<type>-face, where type is one
+;; of "year/genre", "artist", "composer", "performer", "album" or
+;; "track". Note that faces lack the initial "info-" part. For example,
+;; to change the artist face, type
+;; M-x customize-face emms-browser-artist-face.
+
+;; Deleting files
+;; -------------------------------------------------------------------
+
+;; You can use the browser to delete tracks from your hard disk.
+;; Because this is dangerous, it is disabled by default.
+
+;; The following code will delete covers at the same time, and remove
+;; parent directories if they're now empty.
+
+;; (defun de-kill-covers-and-parents (dir tracks)
+;;   (when (> (length tracks) 1)
+;;     ;; if we're not deleting an individual file, delete covers too
+;;     (dolist (cover '("cover.jpg"
+;;                      "cover_med.jpg"
+;;                      "cover_small.jpg"
+;;                      "folder.jpg"))
+;;       (condition-case nil
+;;           (delete-file (concat dir cover))
+;;         (error nil)))
+;;     ;; try and delete empty parents - we actually do the work of the
+;;     ;; calling function here, too
+;;     (let (failed)
+;;       (while (and (not (string= dir "/"))
+;;                   (not failed))
+;;         (condition-case nil
+;;             (delete-directory dir)
+;;           (error (setq failed t)))
+;;         (setq dir (file-name-directory (directory-file-name dir)))))))
+;; (add-hook 'emms-browser-delete-files-hook 'de-kill-covers-and-parents)
+
+;;; Code:
+
+(require 'emms)
+(require 'emms-cache)
+(require 'emms-source-file)
+(require 'emms-playlist-sort)
+(require 'sort)
+
+(eval-when-compile
+  (require 'cl))
+
+;; --------------------------------------------------
+;; Variables and configuration
+;; --------------------------------------------------
+
+(defgroup emms-browser nil
+  "*The Emacs Multimedia System browser"
+  :prefix "emms-browser-"
+  :group 'multimedia
+  :group 'applications)
+
+(defcustom emms-browser-default-browse-type
+  'info-artist
+  "*The default browsing mode."
+  :group 'emms-browser
+  :type 'function)
+
+(defcustom emms-browser-make-name-function
+  'emms-browser-make-name-standard
+  "*A function to make names for entries and subentries.
+Overriding this function allows you to customise how various elements
+are displayed. It is called with two arguments - track and type."
+  :group 'emms-browser
+  :type 'function)
+
+(defcustom emms-browser-get-track-field-function
+  'emms-browser-get-track-field-simple
+  "*A function to get an element from a track.
+Change this to customize the way data is organized in the
+browser. For example,
+`emms-browser-get-track-field-use-directory-name' uses the
+directory name to determine the artist. This means that
+soundtracks, compilations and so on don't populate the artist
+view with lots of 1-track elements."
+  :group 'emms-browser
+  :type 'function)
+
+(defcustom emms-browser-covers
+  '("cover_small.jpg" "cover_med.jpg" "cover_large.jpg")
+  "*Control how cover images are found.
+Can be either a list of small, medium and large images (large
+currently not used), a function which takes a directory and one
+of the symbols `small', `medium' or `large', and should return a
+path to the cover, or nil to turn off cover loading."
+  :group 'emms-browser
+  :type '(choice list function boolean))
+
+(defcustom emms-browser-default-covers nil
+  "*A list of default images to use if a cover isn't found."
+  :group 'emms-browser
+  :type 'list)
+
+(defcustom emms-browser-comparison-test
+  (if (fboundp 'define-hash-table-test)
+      'case-fold
+    'equal)
+  "*A method for comparing entries in the cache.
+The default is to compare case-insensitively."
+  :group 'emms-browser
+  :type 'symbol)
+
+(defcustom emms-browser-track-sort-function
+  'emms-sort-natural-order-less-p
+  "*How to sort tracks in the browser.
+Ues nil for no sorting."
+  :group 'emms-browser
+  :type 'function)
+
+(defcustom emms-browser-alpha-sort-function
+  'string<
+  "*How to sort artists/albums/etc. in the browser.
+Use nil for no sorting."
+  :group 'emms-browser
+  :type 'function)
+
+(defcustom emms-browser-album-sort-function
+  'emms-browser-sort-by-year-or-name
+  "*How to sort artists/albums/etc. in the browser.
+Use nil for no sorting."
+  :group 'emms-browser
+  :type 'function)
+
+(defcustom emms-browser-show-display-hook nil
+  "*Hooks to run when starting or switching to a browser buffer."
+  :group 'emms-browser
+  :type 'hook)
+
+(defcustom emms-browser-hide-display-hook nil
+  "*Hooks to run when burying or removing a browser buffer."
+  :group 'emms-browser
+  :type 'hook)
+
+(defcustom emms-browser-tracks-added-hook nil
+  "*Hooks to run when tracks are added to the playlist."
+  :group 'emms-browser
+  :type 'hook)
+
+(defcustom emms-browser-filter-tracks-hook nil
+  "*Given a track, return t if the track should be ignored."
+  :group 'emms-browser
+  :type 'hook)
+
+(defcustom emms-browser-filter-changed-hook nil
+  "*Hook run after the filter has changed."
+  :group 'emms-browser
+  :type 'hook)
+
+(defcustom emms-browser-delete-files-hook nil
+  "*Hook run after files have been deleted.
+This hook can be used to clean up extra files, such as album covers.
+Called once for each directory."
+  :group 'emms-browser
+  :type 'hook)
+
+(defvar emms-browser-buffer nil
+  "The current browser buffer, if any.")
+
+(defvar emms-browser-buffer-name "*EMMS Browser*"
+  "The default buffer name.")
+
+(defvar emms-browser-top-level-hash nil
+  "The current mapping db, eg. artist -> track.")
+(make-variable-buffer-local 'emms-browser-top-level-hash)
+
+(defvar emms-browser-top-level-type nil
+  "The current mapping type, eg. 'info-artist.")
+(make-variable-buffer-local 'emms-browser-top-level-type)
+
+(defvar emms-browser-current-indent nil
+  "Used to override the current indent, for the playlist, etc.")
+
+(defvar emms-browser-current-filter-name nil
+  "The name of the current filter in place, if any.")
+
+(defconst emms-browser-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map (kbd "q") 'emms-browser-bury-buffer)
+    (define-key map (kbd "/") 'emms-isearch-buffer)
+    (define-key map (kbd "r") 'emms-browser-goto-random)
+    (define-key map (kbd "n") 'next-line)
+    (define-key map (kbd "p") 'previous-line)
+    (define-key map (kbd "C") 'emms-browser-clear-playlist)
+    (define-key map (kbd "?") 'describe-mode)
+    (define-key map (kbd "C-/") 'emms-playlist-mode-undo)
+    (define-key map (kbd "SPC") 'emms-browser-toggle-subitems)
+    (define-key map (kbd "RET") 'emms-browser-add-tracks)
+    (define-key map (kbd "<C-return>") 'emms-browser-add-tracks-and-play)
+    (define-key map (kbd "C-j") 'emms-browser-add-tracks-and-play)
+    (define-key map (kbd "<tab>") 'emms-browser-next-non-track)
+    (define-key map (kbd "<backtab>") 'emms-browser-prev-non-track)
+    (define-key map (kbd "d") 'emms-browser-view-in-dired)
+    (define-key map (kbd "D") 'emms-browser-delete-files)
+    (define-key map (kbd "E") 'emms-browser-expand-all)
+    (define-key map (kbd "1") 'emms-browser-collapse-all)
+    (define-key map (kbd "2") 'emms-browser-expand-to-level-2)
+    (define-key map (kbd "3") 'emms-browser-expand-to-level-3)
+    (define-key map (kbd "4") 'emms-browser-expand-to-level-4)
+    (define-key map (kbd "b 1") 'emms-browse-by-artist)
+    (define-key map (kbd "b 2") 'emms-browse-by-album)
+    (define-key map (kbd "b 3") 'emms-browse-by-genre)
+    (define-key map (kbd "b 4") 'emms-browse-by-year)
+    (define-key map (kbd "b 5") 'emms-browse-by-composer)
+    (define-key map (kbd "b 6") 'emms-browse-by-performer)
+    (define-key map (kbd "s a") 'emms-browser-search-by-artist)
+    (define-key map (kbd "s c") 'emms-browser-search-by-composer)
+    (define-key map (kbd "s p") 'emms-browser-search-by-performer)
+    (define-key map (kbd "s A") 'emms-browser-search-by-album)
+    (define-key map (kbd "s t") 'emms-browser-search-by-title)
+    (define-key map (kbd "s s") 'emms-browser-search-by-names)
+    (define-key map (kbd "W A w") 'emms-browser-lookup-artist-on-wikipedia)
+    (define-key map (kbd "W A p") 'emms-browser-lookup-artist-on-pitchfork)
+    (define-key map (kbd "W C w") 'emms-browser-lookup-composer-on-wikipedia)
+    (define-key map (kbd "W C p") 'emms-browser-lookup-composer-on-pitchfork)
+    (define-key map (kbd "W P w") 'emms-browser-lookup-performer-on-wikipedia)
+    (define-key map (kbd "W P p") 'emms-browser-lookup-performer-on-pitchfork)
+    (define-key map (kbd "W a w") 'emms-browser-lookup-album-on-wikipedia)
+    (define-key map (kbd "W a p") 'emms-browser-lookup-album-on-pitchfork)
+    (define-key map (kbd ">") 'emms-browser-next-filter)
+    (define-key map (kbd "<") 'emms-browser-previous-filter)
+    map)
+  "Keymap for `emms-browser-mode'.")
+
+(defconst emms-browser-search-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map emms-browser-mode-map)
+    (define-key map (kbd "q") 'emms-browser-kill-search)
+    map)
+  "Keymap for `emms-browser-mode'.")
+
+;; --------------------------------------------------
+;; Compatability functions
+;; --------------------------------------------------
+
+(eval-and-compile
+  (if (fboundp 'with-selected-window)
+      (defalias 'emms-browser-with-selected-window 'with-selected-window)
+    (defmacro emms-browser-with-selected-window (window &rest body)
+      ;; this emulates the behavior introduced earlier, though it
+      ;; might be best to do something with `window'
+      `(save-selected-window ,body)))
+  (put 'emms-browser-with-selected-window 'lisp-indent-function 1)
+  (put 'emms-browser-with-selected-window 'edebug-form-spec '(form body))
+
+  (if (fboundp 'run-mode-hooks)
+      (defalias 'emms-browser-run-mode-hooks 'run-mode-hooks)
+    (defalias 'emms-browser-run-mode-hooks 'run-hooks)))
+
+;; --------------------------------------------------
+;; General mode setup
+;; --------------------------------------------------
+
+(defun emms-browser ()
+  "Launch or switch to the EMMS Browser."
+  (interactive)
+  (emms-browser-create-or-focus
+   emms-browser-default-browse-type))
+
+(defun emms-browser-create-or-focus (type)
+  "Create a new browser buffer with BROWSE-FUNC, or switch.
+BROWSE-FUNC should fill the buffer with something of interest. An
+example function is `emms-browse-by-artist'."
+  (let ((buf (emms-browser-get-buffer))
+        wind)
+    (if buf
+        ;; if the buffer is displayed, switch the window instead
+        (progn
+          (setq wind (get-buffer-window buf))
+          (if wind
+              (select-window wind)
+            (switch-to-buffer buf))
+          (emms-browser-run-mode-hooks 'emms-browser-show-display-hook))
+      ;; if there's no buffer, create a new window
+      (emms-browser-create)
+      (emms-browse-by type))))
+
+(defun emms-browser-create ()
+  "Create a new emms-browser buffer and start emms-browser-mode."
+  (emms-browser-new-buffer)
+  (emms-browser-mode)
+  (emms-browser-run-mode-hooks 'emms-browser-show-display-hook))
+
+(defun emms-browser-mode (&optional no-update)
+  "A major mode for the Emms browser.
+\\{emms-browser-mode-map}"
+      ;; create a new buffer
+  (interactive)
+
+  (use-local-map emms-browser-mode-map)
+  (setq major-mode 'emms-browser-mode
+        mode-name "Emms-Browser")
+
+  (setq buffer-read-only t)
+  (unless no-update
+    (setq emms-browser-buffer (current-buffer))))
+
+(defun emms-browser-new-buffer ()
+  "Create a new browser buffer, and switch to it."
+  (switch-to-buffer (generate-new-buffer
+                     emms-browser-buffer-name)))
+
+(defun emms-browser-clear ()
+  "Create or switch to a browser buffer, clearing it."
+  (let ((buf (emms-browser-get-buffer)))
+    (if buf
+        (progn
+          (switch-to-buffer buf)
+          (emms-with-inhibit-read-only-t
+           (delete-region (point-min) (point-max))))
+      (emms-browser-create))))
+
+(defun emms-browser-get-buffer ()
+  "Return the current buffer if it exists, or nil."
+  (unless (or (null emms-browser-buffer)
+              (not (buffer-live-p emms-browser-buffer)))
+    emms-browser-buffer))
+
+(defun emms-browser-ensure-browser-buffer ()
+  (unless (eq major-mode 'emms-browser-mode)
+    (error "Current buffer is not an emms-browser buffer")))
+
+(defun emms-browser-bury-buffer ()
+  "Bury the browser buffer, running hooks."
+  (interactive)
+  (emms-browser-run-mode-hooks 'emms-browser-hide-display-hook)
+  (bury-buffer))
+
+;; --------------------------------------------------
+;; Top-level browsing methods - by artist/album/etc
+;; --------------------------------------------------
+
+;; Since the number of tracks may be rather large, we use a hash to
+;; sort the top level elements into various categories. All
+;; subelements will be stored in a bdata alist structure.
+
+(defmacro emms-browser-add-category (name type)
+  "Create an interactive function emms-browse-by-NAME."
+  (let ((funname (intern (concat "emms-browse-by-" name)))
+        (funcdesc (concat "Browse by " name ".")))
+  `(defun ,funname ()
+     ,funcdesc
+     (interactive)
+     (emms-browse-by ,type))))
+
+(defun emms-browse-by (type)
+  "Render a top level buffer based on TYPE."
+  ;; FIXME: assumes we only browse by info-*
+  (let* ((name (substring (symbol-name type) 5))
+         (modedesc (concat "Browsing by: " name))
+         (hash (emms-browser-make-hash-by type)))
+    (when emms-browser-current-filter-name
+      (setq modedesc (concat modedesc
+                             " [" emms-browser-current-filter-name "]")))
+    (emms-browser-clear)
+    (rename-buffer modedesc)
+    (emms-browser-render-hash hash type)
+    (setq emms-browser-top-level-hash hash)
+    (setq emms-browser-top-level-type type)
+    (unless (> (hash-table-count hash) 0)
+      (emms-browser-show-empty-cache-message))
+    (goto-char (point-min))))
+
+(emms-browser-add-category "artist" 'info-artist)
+(emms-browser-add-category "composer" 'info-composer)
+(emms-browser-add-category "performer" 'info-performer)
+(emms-browser-add-category "album" 'info-album)
+(emms-browser-add-category "genre" 'info-genre)
+(emms-browser-add-category "year" 'info-year)
+
+(defun emms-browser-get-track-field (track type)
+  "Return TYPE from TRACK.
+This can be customized to group different artists into one for
+compilations, etc."
+  (funcall emms-browser-get-track-field-function track type))
+
+(defun emms-browser-get-track-field-simple (track type)
+  (emms-track-get track type "misc"))
+
+(defun emms-browser-get-track-field-use-directory-name (track type)
+  (if (eq type 'info-artist)
+      (emms-browser-get-artist-from-path
+       track)
+    (emms-track-get track type "misc")))
+
+(defun emms-browser-get-artist-from-path (track)
+  (let* ((path (emms-track-get track 'name))
+         (dir (file-name-directory path))
+         (basedir
+          (file-name-nondirectory
+           (directory-file-name
+            (file-name-directory dir)))))
+    (car (split-string basedir " - "))))
+
+(defun emms-browser-make-hash-by (type)
+  "Make a hash, mapping with TYPE, eg artist -> tracks."
+  (let ((hash (make-hash-table
+               :test emms-browser-comparison-test))
+        field existing-entry)
+    (maphash (lambda (path track)
+               (unless (run-hook-with-args-until-success
+                        'emms-browser-filter-tracks-hook track)
+                 (setq field
+                       (emms-browser-get-track-field track type))
+                 (when field
+                   (setq existing-entry (gethash field hash))
+                   (if existing-entry
+                       (puthash field (cons track existing-entry) hash)
+                     (puthash field (list track) hash)))))
+             emms-cache-db)
+    hash))
+
+(defun emms-browser-render-hash (db type)
+  "Render a mapping (DB) into a browser buffer."
+  (maphash (lambda (desc data)
+             (emms-browser-insert-top-level-entry desc data type))
+           db)
+  (emms-with-inhibit-read-only-t
+   (let ((sort-fold-case t))
+     (sort-lines nil (point-min) (point-max)))))
+
+(defun case-fold-string= (a b)
+  (eq t (compare-strings a nil nil b nil nil t)))
+
+(defun case-fold-string-hash (a)
+  (sxhash (upcase a)))
+
+(when (fboundp 'define-hash-table-test)
+  (define-hash-table-test 'case-fold
+    'case-fold-string= 'case-fold-string-hash))
+
+(defun emms-browser-insert-top-level-entry (name tracks type)
+  "Insert a single top level entry into the buffer."
+  (emms-browser-ensure-browser-buffer)
+  (let ((bdata (emms-browser-make-bdata-tree type 1 tracks name)))
+    (emms-browser-insert-format bdata)))
+
+(defun emms-browser-show-empty-cache-message ()
+  "Display some help if the cache is empty."
+  (emms-with-inhibit-read-only-t
+   (insert "
+Welcome to EMMS.
+
+There are currently no files in the EMMS database.
+To browse music, you need to tell EMMS where your
+files are.
+
+Try the following commands:
+
+ M-x emms-add-directory-tree:
+  Add all music in a directory and its subdirectories.
+
+ M-x emms-add-directory:
+  Add all music in a directory
+
+ M-x emms-add-file: Add a single music file.
+
+After you have added some files, wait for EMMS to say
+'all track information loaded,' then return to the
+browser, and hit 'b 1' to refresh.")))
+
+;; --------------------------------------------------
+;; Building a subitem tree
+;; --------------------------------------------------
+
+(defun emms-browser-next-mapping-type (current-mapping)
+  "Return the next sensible mapping.
+Eg. if CURRENT-MAPPING is currently 'info-artist, return 'info-album."
+  (cond
+   ((eq current-mapping 'info-artist) 'info-album)
+   ((eq current-mapping 'info-composer) 'info-album)
+   ((eq current-mapping 'info-performer) 'info-album)
+   ((eq current-mapping 'info-album) 'info-title)
+   ((eq current-mapping 'info-genre) 'info-artist)
+   ((eq current-mapping 'info-year) 'info-artist)))
+
+(defun emms-browser-make-bdata-tree (type level tracks name)
+  "Build a tree of browser DB elements for tracks."
+  (emms-browser-make-bdata
+   (emms-browser-make-bdata-tree-recurse
+    type level tracks)
+   name
+   type level))
+
+(defun emms-browser-make-bdata-tree-recurse (type level tracks)
+  "Build a tree of alists based on a list of tracks, TRACKS.
+For example, if TYPE is 'info-year, return an alist like:
+artist1 -> album1 -> *track* 1.."
+  (let* ((next-type (emms-browser-next-mapping-type type))
+         (next-level (1+ level))
+         alist name new-db new-tracks)
+    ;; if we're at a leaf, the db data is a list of tracks
+    (if (eq type 'info-title)
+        tracks
+      ;; otherwise, make DBs from the sub elements
+      (setq alist
+            (emms-browser-make-sorted-alist
+             next-type tracks))
+      (mapcar (lambda (entry)
+                (setq name (emms-browser-make-name
+                            entry next-type))
+                (setq new-tracks (cdr entry))
+                (emms-browser-make-bdata
+                 (emms-browser-make-bdata-tree-recurse
+                  next-type next-level new-tracks)
+                 name next-type next-level))
+              alist))))
+
+(defun emms-browser-make-name (entry type)
+  "Return a name for ENTRY, used for making a bdata object."
+  (let ((key (car entry))
+        (track (cadr entry))
+        artist title) ;; only the first track
+  (cond
+   ((eq type 'info-title)
+    (setq artist (emms-track-get track 'info-artist))
+    (setq title (emms-track-get track 'info-title))
+    (if (not (and artist title))
+        key
+      (concat artist " - " title)))
+   (t key))))
+
+(defun emms-browser-track-number (track)
+  "Return a string representation of a track number.
+The string will end in a space. If no track number is available,
+return an empty string."
+  (let ((tracknum (emms-track-get track 'info-tracknumber)))
+    (if (or (not (stringp tracknum)) (string= tracknum "0"))
+        ""
+      (concat
+       (if (eq (length tracknum) 1)
+           (concat "0" tracknum)
+         tracknum)))))
+
+(defun emms-browser-disc-number (track)
+  "Return a string representation of a track number.
+The string will end in a space. If no track number is available,
+return an empty string."
+  (let ((discnum (emms-track-get track 'info-discnumber)))
+    (if (or (not (stringp discnum)) (string= discnum "0"))
+        ""
+      discnum)))
+
+(defun emms-browser-year-number (track)
+  "Return a string representation of a track's year.
+This will be in the form '(1998) '."
+  (let ((year (emms-track-get track 'info-year)))
+    (if (or (not (stringp year)) (string= year "0"))
+        ""
+      (concat
+       "(" year ") "))))
+
+(defun emms-browser-track-duration (track)
+  "Return a string representation of a track duration.
+If no duration is available, return an empty string."
+  (let ((pmin (emms-track-get track 'info-playing-time-min))
+        (psec (emms-track-get track 'info-playing-time-sec))
+        (ptot (emms-track-get track 'info-playing-time)))
+    (cond ((and pmin psec) (format "%02d:%02d" pmin psec))
+          (ptot (format  "%02d:%02d" (/ ptot 60) (% ptot 60)))
+          (t ""))))
+
+(defun emms-browser-make-bdata (data name type level)
+  "Return a browser data item from ALIST.
+DATA should be a list of DB items, or a list of tracks.
+NAME is a name for the DB item.
+TYPE is a category the data is organised by, such as 'info-artist.
+LEVEL is the number of the sublevel the db item will be placed in."
+  (list (cons 'type type)
+        (cons 'level level)
+        (cons 'name name)
+        (cons 'data data)))
+
+(defun emms-browser-make-alist (type tracks)
+  "Make an alist mapping of TYPE -> TRACKS.
+Items with no metadata for TYPE will be placed in 'misc'"
+  (let (db key existing tracknum)
+    (dolist (track tracks)
+      (setq key (emms-browser-get-track-field track type))
+      (when (eq type 'info-title)
+          ;; try and make every track unique
+        (setq tracknum (emms-browser-track-number track))
+        (if (string= tracknum "")
+            (setq key (file-name-nondirectory
+                       (emms-track-get track 'name)))
+          (setq key (concat tracknum key))))
+      (setq existing (assoc key db))
+      (if existing
+          (setcdr existing (cons track (cdr existing)))
+        (push (cons key (list track)) db)))
+    ;; sort the entries we've built
+    (dolist (item db)
+      (setcdr item (nreverse (cdr item))))
+    db))
+
+(defun emms-browser-make-sorted-alist (type tracks)
+  "Return a sorted alist of TRACKS.
+TYPE is the metadata to make the alist by - eg. if it's
+'info-artist, an alist of artists will be made."
+  (emms-browser-sort-alist
+   (emms-browser-make-alist type tracks)
+   type))
+
+;; --------------------------------------------------
+;; BDATA accessors and predicates
+;; --------------------------------------------------
+
+(defun emms-browser-bdata-level (bdata)
+  (cdr (assq 'level bdata)))
+
+(defun emms-browser-bdata-name (bdata)
+  (cdr (assq 'name bdata)))
+
+(defun emms-browser-bdata-type (bdata)
+  (cdr (assq 'type bdata)))
+
+(defun emms-browser-bdata-data (bdata)
+  (cdr (assq 'data bdata)))
+
+(defun emms-browser-bdata-p (obj)
+  "True if obj is a BDATA object."
+  (consp (assq 'data obj)))
+
+;; --------------------------------------------------
+;; Sorting expanded entries
+;; --------------------------------------------------
+
+(defmacro emms-browser-sort-cadr (sort-func)
+  "Return a function to sort an alist using SORT-FUNC.
+This sorting predicate will compare the cadr of each entry.
+SORT-FUNC should be a playlist sorting predicate like
+`emms-playlist-sort-by-natural-order'."
+  `(lambda (a b)
+     (funcall ,sort-func (cadr a) (cadr b))))
+
+(defmacro emms-browser-sort-car (sort-func)
+  "Return a function to sort an alist using SORT-FUNC.
+This sorting predicate will compare the car of each entry.
+SORT-FUNC should be a playlist sorting predicate like
+`emms-playlist-sort-by-natural-order'."
+  `(lambda (a b)
+     (funcall ,sort-func (car a) (car b))))
+
+(defun emms-browser-sort-by-track (alist)
+  "Sort an ALIST by the tracks in each entry.
+Uses `emms-browser-track-sort-function'."
+  (if emms-browser-track-sort-function
+      (sort alist (emms-browser-sort-cadr
+                  emms-browser-track-sort-function))
+    alist))
+
+(defun emms-browser-sort-by-name (alist)
+  "Sort ALIST by keys alphabetically.
+Uses `emms-browser-alpha-sort-function'."
+  (if emms-browser-alpha-sort-function
+      (sort alist (emms-browser-sort-car
+                  emms-browser-alpha-sort-function))
+    alist))
+
+(defun emms-browser-sort-by-year-or-name (alist)
+  "Sort based on year or name."
+  (sort alist (emms-browser-sort-cadr
+               'emms-browser-sort-by-year-or-name-p)))
+
+(defun emms-browser-sort-by-year-or-name-p (a b)
+  ;; FIXME: this is a bit of a hack
+  (let ((a-desc (concat
+                 (emms-browser-year-number a)
+                 (emms-track-get a 'info-album "misc")))
+        (b-desc (concat
+                 (emms-browser-year-number b)
+                 (emms-track-get b 'info-album "misc"))))
+    (string< a-desc b-desc)))
+
+(defun emms-browser-sort-alist (alist type)
+  "Sort ALIST using the sorting function for TYPE."
+  (let ((sort-func
+         (cond
+          ((or
+            (eq type 'info-artist)
+            (eq type 'info-composer)
+            (eq type 'info-performer)
+            (eq type 'info-year)
+            (eq type 'info-genre))
+           'emms-browser-sort-by-name)
+          ((eq type 'info-album)
+           emms-browser-album-sort-function)
+          ((eq type 'info-title)
+           'emms-browser-sort-by-track)
+          (t (message "Can't sort unknown mapping!")))))
+    (funcall sort-func alist)))
+
+;; --------------------------------------------------
+;; Subitem operations on the buffer
+;; --------------------------------------------------
+
+(defun emms-browser-bdata-at-point ()
+  "Return the bdata object at point.
+Includes information at point (such as album name), and metadata."
+  (get-text-property (point-at-bol)
+                     'emms-browser-bdata))
+
+(defun emms-browser-data-at-point ()
+  "Return the data stored under point.
+This will be a list of DB items."
+  (emms-browser-bdata-data (emms-browser-bdata-at-point)))
+
+(defun emms-browser-level-at-point ()
+  "Return the current level at point."
+  (emms-browser-bdata-level (emms-browser-bdata-at-point)))
+
+(defun emms-browser-tracks-at-point (&optional node)
+  "Return a list of tracks at point."
+  (let (tracks)
+    (dolist (node (if node
+                      node
+                    (emms-browser-data-at-point)))
+      (if (not (emms-browser-bdata-p node))
+          (setq tracks (cons node tracks))
+        (setq tracks
+              (append tracks
+                      (emms-browser-tracks-at-point
+                       (emms-browser-bdata-data node))))))
+    tracks))
+
+(defun emms-browser-expand-one-level ()
+  "Expand the current line by one sublevel."
+  (interactive)
+  (let* ((data (emms-browser-data-at-point)))
+    (save-excursion
+      (forward-line 1)
+      (beginning-of-line)
+      (dolist (data-item data)
+        (emms-browser-insert-data-item data-item)))))
+
+(defun emms-browser-insert-data-item (data-item)
+  "Insert DATA-ITEM into the buffer.
+This checks DATA-ITEM's level to determine how much to indent.
+The line will have a property emms-browser-bdata storing subitem
+information."
+  (emms-browser-insert-format data-item))
+
+(defun emms-browser-find-entry-more-than-level (level)
+  "Move point to next entry more than LEVEL and return point.
+If no entry exits, return nil.
+Returns point if currently on a an entry more than LEVEL."
+  (let ((old-pos (point))
+        level-at-point)
+    (forward-line 1)
+    (setq level-at-point (emms-browser-level-at-point))
+    (if (and level-at-point
+             (> level-at-point level))
+        (point)
+      (goto-char old-pos)
+      nil)))
+
+(defun emms-browser-subitems-visible ()
+  "True if there are any subentries visible point."
+  (let ((current-level (emms-browser-level-at-point))
+        new-level)
+    (save-excursion
+      (re-search-forward "\n" nil t)
+      (when (setq new-level (emms-browser-level-at-point))
+        (> new-level current-level)))))
+
+(defun emms-browser-subitems-exist ()
+  "True if it's possible to expand the current line."
+  (not (eq (emms-browser-bdata-type
+            (emms-browser-bdata-at-point))
+           'info-title)))
+
+(defun emms-browser-move-up-level (&optional direction)
+  "Move up one level if possible.
+Return true if we were able to move up.
+If DIRECTION is 1, move forward, otherwise move backwards."
+  (let ((moved nil)
+        (continue t)
+        (current-level (emms-browser-level-at-point)))
+    (while (and
+            continue
+            (zerop (forward-line
+                    (or direction -1))))
+      (when (> current-level (emms-browser-level-at-point))
+        (setq moved t)
+        (setq continue nil)))
+    moved))
+
+(defun emms-browser-toggle-subitems ()
+  "Show or hide (kill) subitems under the current line."
+  (interactive)
+  (if (emms-browser-subitems-visible)
+      (emms-browser-kill-subitems)
+    (if (emms-browser-subitems-exist)
+        (emms-browser-show-subitems)
+      (assert (emms-browser-move-up-level))
+      (emms-browser-kill-subitems))))
+
+(defun emms-browser-show-subitems ()
+  "Show subitems under the current line."
+  (unless (emms-browser-subitems-visible)
+    (if (emms-browser-subitems-exist)
+        (emms-browser-expand-one-level))))
+
+(defun emms-browser-kill-subitems ()
+  "Remove all subitems under the current line.
+Stops at the next line at the same level, or EOF."
+  (when (emms-browser-subitems-visible)
+    (let ((current-level (emms-browser-level-at-point))
+          (next-line (point-at-bol 2)))
+      (emms-with-inhibit-read-only-t
+       (delete-region next-line
+                      (save-excursion
+                        (while
+                            (emms-browser-find-entry-more-than-level
+                             current-level))
+                        (point-at-bol 2)))))))
+
+;; --------------------------------------------------
+;; Dealing with the playlist (queuing songs, etc)
+;; --------------------------------------------------
+
+(defun emms-browser-playlist-insert-group (bdata)
+  "Insert a group description into the playlist buffer."
+  (let* ((type (emms-browser-bdata-type bdata))
+         (short-type (substring (symbol-name type) 5))
+         (name (emms-browser-format-line bdata 'playlist)))
+    (with-current-emms-playlist
+      (goto-char (point-max))
+      (insert name "\n"))))
+
+(defun emms-browser-playlist-insert-track (bdata)
+  "Insert a track into the playlist buffer."
+  (let ((name (emms-browser-format-line bdata 'playlist))
+        (track (car (emms-browser-bdata-data bdata))))
+    (with-current-emms-playlist
+      (goto-char (point-max))
+      (insert name "\n"))))
+
+(defun emms-browser-playlist-insert-bdata (bdata starting-level)
+  "Add all tracks in BDATA to the playlist."
+  (let ((type (emms-browser-bdata-type bdata))
+        (name (emms-browser-bdata-name bdata))
+        (level (emms-browser-bdata-level bdata))
+        emms-browser-current-indent)
+
+    ;; adjust the indentation relative to the starting level
+    (when starting-level
+      (setq level (- level (1- starting-level))))
+    ;; we temporarily rebind the current indent to the relative indent
+    (setq emms-browser-current-indent
+          (emms-browser-make-indent level))
+
+    ;; add a group heading?
+    (unless (eq type 'info-title)
+      (emms-browser-playlist-insert-group bdata))
+
+    ;; recurse or add tracks
+    (dolist (item (emms-browser-bdata-data bdata))
+      (if (not (eq type 'info-title))
+          (emms-browser-playlist-insert-bdata item starting-level)
+        (emms-browser-playlist-insert-track bdata)))))
+
+;; --------------------------------------------------
+;; Expanding/contracting
+;; --------------------------------------------------
+
+(defun emms-browser-expand-to-level (level)
+  "Expand to a depth specified by LEVEL.
+After expanding, jump to the currently marked entry."
+  (goto-char (point-min))
+  (while (not (eq (buffer-end 1) (point)))
+    (if (< (emms-browser-level-at-point) level)
+        (emms-browser-show-subitems))
+    (emms-browser-next-non-track))
+  (emms-browser-pop-mark)
+  (recenter '(4)))
+
+(defun emms-browser-mark-and-collapse ()
+  "Save the current top level element, and collapse."
+  (emms-browser-mark-entry)
+  (goto-char (point-max))
+  (while (not (eq (buffer-end -1) (point)))
+    (emms-browser-prev-non-track)
+    (emms-browser-kill-subitems)))
+
+(defun emms-browser-find-top-level ()
+  "Move up until reaching a top-level element."
+  (while (not (eq (emms-browser-level-at-point) 1))
+    (forward-line -1)))
+
+(defun emms-browser-mark-entry ()
+  "Mark the current top level entry."
+  (save-excursion
+    (emms-browser-find-top-level)
+    (emms-with-inhibit-read-only-t
+     (add-text-properties (point-at-bol)
+                          (point-at-eol)
+                          (list 'emms-browser-mark t)))))
+
+(defun emms-browser-pop-mark ()
+  "Return to the last marked entry, and remove the mark."
+  (goto-char (point-min))
+  (let ((pos (text-property-any (point-min) (point-max)
+                                'emms-browser-mark t)))
+    (if pos
+        (progn
+          (goto-char pos)
+          (emms-with-inhibit-read-only-t
+           (remove-text-properties (point-at-bol)
+                                   (point-at-eol)
+                                   (list 'emms-browser-mark))))
+      (message "No mark saved!"))))
+
+(defun emms-browser-go-to-parent ()
+  "Move point to the parent of the current node.
+Return point. If at level one, return the current point."
+  (let ((current-level (emms-browser-level-at-point)))
+    (unless (eq current-level 1)
+      (while (<= current-level (emms-browser-level-at-point))
+        (forward-line -1)))
+    (point)))
+
+(defun emms-browser-delete-current-node ()
+  "Remove the current node, and empty parents."
+  ;; set the data to empty
+  (setcdr (assq 'data (emms-browser-bdata-at-point)) nil)
+  (emms-browser-delete-node-if-empty))
+
+(defun emms-browser-delete-node-if-empty ()
+  "If empty, remove node and empty parents."
+  (when (zerop (length (emms-browser-data-at-point)))
+    (save-excursion
+      (let ((child-bdata (emms-browser-bdata-at-point))
+            parent-bdata parent-point)
+        ;; record the parent's position before we delete anything
+        (save-excursion
+          (setq parent-point (emms-browser-go-to-parent)))
+        ;; delete the current line
+        (when (emms-browser-subitems-visible)
+          (emms-browser-kill-subitems))
+        (emms-with-inhibit-read-only-t
+         (goto-char (point-at-bol))
+         (kill-line 1))
+        (unless (eq (emms-browser-bdata-level child-bdata) 1)
+          ;; remove the node from the parent, and recurse
+          (goto-char parent-point)
+          (setq parent-bdata (emms-browser-bdata-at-point))
+          (setcdr (assq 'data parent-bdata)
+                  (delq child-bdata
+                        (emms-browser-bdata-data parent-bdata)))
+          (emms-browser-delete-node-if-empty))))))
+
+;; --------------------------------------------------
+;; User-visible commands
+;; --------------------------------------------------
+
+(defun emms-browser-add-tracks ()
+  "Add all tracks at point.
+Return the previous point-max before adding."
+  (interactive)
+  (let ((first-new-track (with-current-emms-playlist (point-max)))
+        (bdata (emms-browser-bdata-at-point)))
+    (emms-browser-playlist-insert-bdata
+     bdata (emms-browser-bdata-level bdata))
+    (run-hook-with-args 'emms-browser-tracks-added-hook
+                        first-new-track)
+    first-new-track))
+
+(defun emms-browser-add-tracks-and-play ()
+  "Add all tracks at point, and play the first added track."
+  (interactive)
+  (let ((old-pos (emms-browser-add-tracks)))
+    (with-current-emms-playlist
+      (goto-char old-pos)
+      ;; if we're sitting on a group name, move forward
+      (unless (emms-playlist-track-at (point))
+        (emms-playlist-next))
+      (emms-playlist-select (point)))
+    ;; FIXME: is there a better way of doing this?
+    (emms-stop)
+    (emms-start)))
+
+(defun emms-isearch-buffer ()
+  "Isearch through the buffer."
+  (interactive)
+  (goto-char (point-min))
+  (when (isearch-forward)
+    (unless (emms-browser-subitems-visible)
+      (emms-browser-show-subitems))))
+
+(defun emms-browser-next-non-track (&optional direction)
+  "Jump to the next non-track element."
+  (interactive)
+  (let ((continue t))
+    (while (and continue
+                (forward-line (or direction 1)))
+      (unless (eq (emms-browser-bdata-type
+                   (emms-browser-bdata-at-point)) 'info-title)
+        (setq continue)))))
+
+(defun emms-browser-prev-non-track ()
+  "Jump to the previous non-track element."
+  (interactive)
+  (emms-browser-next-non-track -1))
+
+(defun emms-browser-expand-all ()
+  "Expand everything."
+  (interactive)
+  (emms-browser-expand-to-level 99))
+
+(defun emms-browser-expand-to-level-2 ()
+  "Expand all top level items one level."
+  (interactive)
+  (emms-browser-mark-and-collapse)
+  (emms-browser-expand-to-level 2))
+
+(defun emms-browser-expand-to-level-3 ()
+  "Expand all top level items two levels."
+  (interactive)
+  (emms-browser-mark-and-collapse)
+  (emms-browser-expand-to-level 3))
+
+(defun emms-browser-expand-to-level-4 ()
+  "Expand all top level items three levels."
+  (interactive)
+  (emms-browser-mark-and-collapse)
+  (emms-browser-expand-to-level 4))
+
+(defun emms-browser-collapse-all ()
+  "Collapse everything, saving and restoring the mark."
+  (interactive)
+  (emms-browser-mark-and-collapse)
+  (emms-browser-pop-mark)
+  (recenter '(4)))
+
+(defvar emms-browser-seed-pending t
+  "Do we need to seed (random)?")
+
+(defun emms-browser-goto-random ()
+  (interactive)
+  (when emms-browser-seed-pending
+    (random t)
+    (setq emms-browser-seed-pending nil))
+  (goto-char (point-min))
+  (forward-line (1- (random (count-lines (point-min) (point-max))))))
+
+(defun emms-browser-view-in-dired (&optional bdata)
+  "View the current directory in dired."
+  ;; FIXME: currently just grabs the directory from the first track
+  (interactive)
+  (if bdata
+      (if (eq (emms-browser-bdata-type bdata) 'info-title)
+          (let* ((track (car (emms-browser-bdata-data bdata)))
+                 (path (emms-track-get track 'name))
+                 (dir (file-name-directory path)))
+            (find-file dir))
+        (emms-browser-view-in-dired (car (emms-browser-bdata-data bdata))))
+    (emms-browser-view-in-dired (emms-browser-bdata-at-point))))
+
+(defun emms-browser-delete-files ()
+  "Delete all files under point.
+Disabled by default."
+  (interactive)
+  (let ((tracks (emms-browser-tracks-at-point))
+        dirs path)
+    (unless (yes-or-no-p
+             (format "Really permanently delete these %d tracks? "
+                     (length tracks)))
+      (error "Cancelled!"))
+    (message "Deleting files..")
+    (dolist (track tracks)
+      (setq path (emms-track-get track 'name))
+      (delete-file path)
+      (add-to-list 'dirs (file-name-directory path))
+      (emms-cache-del path))
+    ;; remove empty dirs
+    (dolist (dir dirs)
+      (run-hook-with-args 'emms-browser-delete-files-hook dir tracks)
+      (condition-case nil
+          (delete-directory dir)
+        (error nil)))
+    ;; remove the item from the browser
+    (emms-browser-delete-current-node)
+    (message "Deleting files..done")))
+
+(put 'emms-browser-delete-files 'disabled t)
+
+(defun emms-browser-clear-playlist ()
+  (interactive)
+  (with-current-emms-playlist
+    (emms-playlist-clear)))
+
+(defun emms-browser-lookup (field url)
+  (let ((data
+         (emms-track-get (emms-browser-bdata-first-track
+                          (emms-browser-bdata-at-point))
+                         field)))
+    (when data
+      (browse-url
+       (concat url data)))))
+
+(defun emms-browser-lookup-wikipedia (field)
+  (emms-browser-lookup
+   field "http://en.wikipedia.org/wiki/Special:Search?search="))
+
+(defun emms-browser-lookup-pitchfork (field)
+  (emms-browser-lookup
+   field "http://www.pitchforkmedia.com/search/record_reviews/query?query[keywords]="))
+
+(defun emms-browser-lookup-artist-on-wikipedia ()
+  (interactive)
+  (emms-browser-lookup-wikipedia 'info-artist))
+
+(defun emms-browser-lookup-composer-on-wikipedia ()
+  (interactive)
+  (emms-browser-lookup-wikipedia 'info-composer))
+
+(defun emms-browser-lookup-performer-on-wikipedia ()
+  (interactive)
+  (emms-browser-lookup-wikipedia 'info-performer))
+
+(defun emms-browser-lookup-album-on-wikipedia ()
+  (interactive)
+  (emms-browser-lookup-wikipedia 'info-album))
+
+(defun emms-browser-lookup-artist-on-pitchfork ()
+  (interactive)
+  (emms-browser-lookup-pitchfork 'info-artist))
+
+(defun emms-browser-lookup-composer-on-pitchfork ()
+  (interactive)
+  (emms-browser-lookup-pitchfork 'info-composer))
+
+(defun emms-browser-lookup-performer-on-pitchfork ()
+  (interactive)
+  (emms-browser-lookup-pitchfork 'info-performer))
+
+(defun emms-browser-lookup-album-on-pitchfork ()
+  (interactive)
+  (emms-browser-lookup-pitchfork 'info-album))
+
+;; --------------------------------------------------
+;; Linked browser and playlist windows
+;; --------------------------------------------------
+
+(defcustom emms-browser-switch-to-playlist-on-add
+  nil
+  "Whether to switch to to the playlist after adding files."
+  :group 'emms-browser
+  :type 'boolean)
+
+(defun emms-smart-browse ()
+  "Display browser and playlist.
+Toggle between selecting browser, playlist or hiding both. Tries
+to behave sanely if the user has manually changed the window
+configuration."
+  (interactive)
+  (add-to-list 'emms-browser-show-display-hook
+               'emms-browser-display-playlist)
+  (add-to-list 'emms-browser-hide-display-hook
+               'emms-browser-hide-linked-window)
+  ;; switch to the playlist window when adding tracks?
+  (add-to-list 'emms-browser-tracks-added-hook
+               (lambda (start-of-tracks) (interactive)
+                 (let (playlist-window)
+                   (when emms-browser-switch-to-playlist-on-add
+                     (emms-smart-browse))
+                   ;; center on the first added track/group name
+                   (when
+                       (setq playlist-window
+                             (emms-browser-get-linked-window))
+                     (emms-browser-with-selected-window
+                         playlist-window
+                       (goto-char start-of-tracks)
+                       (recenter '(4)))))))
+  (let (wind buf)
+  (cond
+   ((eq major-mode 'emms-browser-mode)
+    (setq buf (emms-browser-get-linked-buffer))
+    (setq wind (emms-browser-get-linked-window))
+    ;; if the playlist window is visible, select it
+    (if wind
+        (select-window wind)
+      ;; otherwise display and select it
+      (select-window (emms-browser-display-playlist))))
+   ((eq major-mode 'emms-playlist-mode)
+    (setq wind (emms-browser-get-linked-window))
+    ;; if the playlist window is selected, and the browser is visible,
+    ;; hide both
+    (if wind
+        (progn
+          (select-window wind)
+          (emms-browser-bury-buffer))
+      ;; otherwise bury both
+      (bury-buffer)
+      (emms-browser-hide-linked-window)))
+   (t
+    ;; show both
+    (emms-browser)))))
+
+(defun emms-browser-get-linked-buffer ()
+  "Return linked buffer (eg browser if playlist is selected."
+  (cond
+   ((eq major-mode 'emms-browser-mode)
+    (car (emms-playlist-buffer-list)))
+   ((eq major-mode 'emms-playlist-mode)
+    emms-browser-buffer)))
+
+(defun emms-browser-get-linked-window ()
+  "Return linked window (eg browser if playlist is selected."
+  (let ((buf (emms-browser-get-linked-buffer)))
+    (when buf
+      (get-buffer-window buf))))
+
+(defun emms-browser-display-playlist ()
+  "A hook to show the playlist when the browser is displayed.
+Returns the playlist window."
+  (interactive)
+  (let ((pbuf (emms-browser-get-linked-buffer))
+        (pwin (emms-browser-get-linked-window)))
+    ;; if the window isn't alive..
+    (unless (window-live-p pwin)
+      (save-selected-window
+        (split-window-horizontally)
+        (other-window 1)
+        (if pbuf
+            (switch-to-buffer pbuf)
+          ;; there's no playlist - create one
+          (setq pbuf (emms-playlist-current-clear))
+          (switch-to-buffer pbuf))
+        ;; make q in the playlist window hide the linked browser
+        (when (boundp 'emms-playlist-mode-map)
+          (define-key emms-playlist-mode-map (kbd "q")
+            (lambda ()
+              (interactive)
+              (emms-browser-hide-linked-window)
+              (bury-buffer))))
+        (setq pwin (get-buffer-window pbuf))))
+    pwin))
+
+(defun emms-browser-hide-linked-window ()
+  "Delete a playlist or browser window when the other is hidden."
+  (interactive)
+  (let ((other-buf (emms-browser-get-linked-buffer))
+        (other-win (emms-browser-get-linked-window)))
+    (when (and other-win
+               (window-live-p other-win))
+      (delete-window other-win))
+    ;; bury the buffer, or it becomes visible when we hide the
+    ;; linked buffer
+    (bury-buffer other-buf)))
+
+;; --------------------------------------------------
+;; Searching
+;; --------------------------------------------------
+
+(defun emms-browser-filter-cache (search-list)
+  "Return a list of tracks that match SEARCH-LIST.
+SEARCH-LIST is a list of cons pairs, in the form:
+
+  ((field1 field2) string)
+
+If string matches any of the fields in a cons pair, it will be
+included."
+
+  (let (tracks)
+    (maphash (lambda (k track)
+               (when (emms-browser-matches-p track search-list)
+                 (push track tracks)))
+             emms-cache-db)
+    tracks))
+
+(defun emms-browser-matches-p (track search-list)
+  (let (no-match matched)
+    (dolist (item search-list)
+      (setq matched nil)
+      (dolist (field (car item))
+        (let ((track-field (emms-track-get track field "")))
+          (when (and track-field (string-match (cadr item) track-field))
+            (setq matched t))))
+      (unless matched
+        (setq no-match t)))
+    (not no-match)))
+
+(defun emms-browser-search-buffer-go ()
+  "Create a new search buffer, or clean the existing one."
+  (switch-to-buffer
+   (get-buffer-create "*emms-browser-search*"))
+  (emms-browser-mode t)
+  (use-local-map emms-browser-search-mode-map)
+  (emms-with-inhibit-read-only-t
+   (delete-region (point-min) (point-max))))
+
+(defun emms-browser-search (fields)
+  "Search for STR using FIELDS."
+  (let* ((prompt (format "Searching with %S: " fields))
+         (str (read-string prompt)))
+    (emms-browser-search-buffer-go)
+    (emms-with-inhibit-read-only-t
+     (emms-browser-render-search
+      (emms-browser-filter-cache
+       (list (list fields str)))))
+    (emms-browser-expand-all)
+    (goto-char (point-min))))
+
+(defun emms-browser-render-search (tracks)
+  (let ((entries
+         (emms-browser-make-sorted-alist 'info-artist tracks)))
+    (dolist (entry entries)
+      (emms-browser-insert-top-level-entry (car entry)
+                                           (cdr entry)
+                                           'info-artist))))
+
+;; hmm - should we be doing this?
+(defun emms-browser-kill-search ()
+  "Kill the buffer when q is hit."
+  (interactive)
+  (kill-buffer (current-buffer)))
+
+(defun emms-browser-search-by-artist ()
+  (interactive)
+  (emms-browser-search '(info-artist)))
+
+(defun emms-browser-search-by-composer ()
+  (interactive)
+  (emms-browser-search '(info-composer)))
+
+(defun emms-browser-search-by-performer ()
+  (interactive)
+  (emms-browser-search '(info-performer)))
+
+(defun emms-browser-search-by-title ()
+  (interactive)
+  (emms-browser-search '(info-title)))
+
+(defun emms-browser-search-by-album ()
+  (interactive)
+  (emms-browser-search '(info-album)))
+
+(defun emms-browser-search-by-names ()
+  (interactive)
+  (emms-browser-search '(info-artist info-composer info-performer info-title info-album)))
+
+;; --------------------------------------------------
+;; Album covers
+;; --------------------------------------------------
+
+(defun emms-browser-get-cover-from-album (bdata &optional size)
+  (assert (eq (emms-browser-bdata-type bdata) 'info-album))
+  (let* ((track1data (emms-browser-bdata-data bdata))
+         (track1 (car (emms-browser-bdata-data (car track1data))))
+         (path (emms-track-get track1 'name)))
+    (emms-browser-get-cover-from-path path size)))
+
+(defun emms-browser-get-cover-from-path (path &optional size)
+  "Return a cover filename, if it exists."
+  (unless size
+    (setq size 'medium))
+  (let* ((size-idx (cond
+                    ((eq size 'small) 0)
+                    ((eq size 'medium) 1)
+                    ((eq size 'large) 2)))
+         (cover
+          (cond
+           ((functionp emms-browser-covers)
+            (funcall emms-browser-covers (file-name-directory path) size))
+           ((and (listp emms-browser-covers)
+                 (nth size-idx emms-browser-covers))
+            (concat (file-name-directory path)
+                    (nth size-idx emms-browser-covers))))))
+    (if (and cover
+             (file-readable-p cover))
+        cover
+      ;; no cover found, use default
+      (when emms-browser-default-covers
+        (nth size-idx emms-browser-default-covers)))))
+
+(defun emms-browser-insert-cover (path)
+  (insert (emms-browser-make-cover path)))
+
+(defun emms-browser-make-cover (path)
+  (let* ((ext (file-name-extension path))
+         (type (cond
+                ((string= ext "png")   'png)
+                ((string= ext "xbm")   'xbm)
+                ((string= ext "xpm")   'xpm)
+                ((string= ext "pbm")   'pbm)
+                ((string-match "e?ps"
+                               ext)    'postscript)
+                ((string= ext "gif")   'gif)
+                ((string= ext "tiff")  'tiff)
+                (t                     'jpeg))))
+    (emms-propertize " "
+                     'display `(image
+                                :type ,type
+                                :margin 5
+                                :file ,path)
+                     'rear-nonsticky '(display))))
+
+(defun emms-browser-get-cover-str (path size)
+  (let ((cover (emms-browser-get-cover-from-path path size)))
+    (if cover
+        (emms-browser-make-cover cover)
+      ;; we use a single space so that cover & no cover tracks line up
+      ;; in a terminal
+      " ")))
+
+;; --------------------------------------------------
+;; Display formats
+;; --------------------------------------------------
+
+(defun emms-browser-bdata-first-track (bdata)
+  "Return the first track from a given bdata.
+If > album level, most of the track data will not make sense."
+  (let ((type (emms-browser-bdata-type bdata)))
+    (if (eq type 'info-title)
+        (car (emms-browser-bdata-data bdata))
+      ;; recurse
+      (emms-browser-bdata-first-track
+       (car (emms-browser-bdata-data bdata))))))
+
+(defun emms-browser-insert-format (bdata)
+  (emms-with-inhibit-read-only-t
+   (insert
+    (emms-browser-format-line bdata)
+    "\n")))
+
+(defun emms-browser-make-indent (level)
+  (or
+   emms-browser-current-indent
+   (make-string (* 1 (1- level)) ?\s)))
+
+(defun emms-browser-format-elem (format-string elem)
+  (cdr (assoc elem format-string)))
+
+(defun emms-browser-format-line (bdata &optional target)
+  "Return a propertized string to be inserted in the buffer."
+  (unless target
+    (setq target 'browser))
+  (let* ((name (or (emms-browser-bdata-name bdata) "misc"))
+         (level (emms-browser-bdata-level bdata))
+         (type (emms-browser-bdata-type bdata))
+         (indent (emms-browser-make-indent level))
+         (track (emms-browser-bdata-first-track bdata))
+         (path (emms-track-get track 'name))
+         (face (emms-browser-get-face bdata))
+         (format (emms-browser-get-format bdata target))
+         (props (list 'emms-browser-bdata bdata))
+         (format-choices
+          `(("i" . ,indent)
+            ("n" . ,name)
+            ("y" . ,(emms-track-get track 'info-year))
+            ("A" . ,(emms-track-get track 'info-album))
+            ("a" . ,(emms-track-get track 'info-artist))
+            ("C" . ,(emms-track-get track 'info-composer))
+            ("p" . ,(emms-track-get track 'info-performer))
+            ("t" . ,(emms-track-get track 'info-title))
+           ("D" . ,(emms-browser-disc-number track))
+            ("T" . ,(emms-browser-track-number track))
+            ("d" . ,(emms-browser-track-duration track))
+            ("cS" . ,(emms-browser-get-cover-str path 'small))
+            ("cM" . ,(emms-browser-get-cover-str path 'medium))
+            ("cL" . ,(emms-browser-get-cover-str path 'large))))
+         str)
+
+    (when (functionp format)
+      (setq format (funcall format bdata format-choices)))
+
+    (setq str
+          (with-temp-buffer
+            (insert format)
+            (goto-char (point-min))
+            (let ((start (point-min)))
+              ;; jump over any image
+              (when (re-search-forward "%c[SML]" nil t)
+                (setq start (point)))
+              ;; jump over the indent
+              (when (re-search-forward "%i" nil t)
+                (setq start (point)))
+              (add-text-properties start (point-max)
+                                   (list 'face face)))
+            (buffer-string)))
+
+    (setq str (emms-browser-format-spec str format-choices))
+
+    ;; give tracks a 'boost' if they're not top-level
+    ;; (covers take up an extra space)
+    (when (and (eq type 'info-title)
+               (not (string= indent "")))
+      (setq str (concat " " str)))
+
+    ;; if we're in playlist mode, add a track
+    (when (and (eq target 'playlist)
+               (eq type 'info-title))
+      (setq props
+            (append props `(emms-track ,track))))
+
+    ;; add properties to the whole string
+    (add-text-properties 0 (length str) props str)
+    str))
+
+(defun emms-browser-get-face (bdata)
+  "Return a suitable face for BDATA."
+  (let* ((type (emms-browser-bdata-type bdata))
+         (name (cond
+                ((or (eq type 'info-year)
+                     (eq type 'info-genre)) "year/genre")
+                 ((eq type 'info-artist) "artist")
+                 ((eq type 'info-composer) "composer")
+                 ((eq type 'info-performer) "performer")
+                 ((eq type 'info-album) "album")
+                 ((eq type 'info-title) "track"))))
+    (intern
+     (concat "emms-browser-" name "-face"))))
+
+;; based on gnus code
+(defun emms-browser-format-spec (format specification)
+  "Return a string based on FORMAT and SPECIFICATION.
+FORMAT is a string containing `format'-like specs like \"bash %u %k\",
+while SPECIFICATION is an alist mapping from format spec characters
+to values.  Any text properties on a %-spec itself are propagated to
+the text that it generates."
+  (with-temp-buffer
+    (insert format)
+    (goto-char (point-min))
+    (while (search-forward "%" nil t)
+      (cond
+       ;; Quoted percent sign.
+       ((eq (char-after) ?%)
+        (delete-char 1))
+       ;; Valid format spec.
+       ((looking-at "\\([-0-9.]*\\)\\([a-zA-Z]+\\)")
+        (let* ((num (match-string 1))
+               (spec (match-string 2))
+               (val (cdr (assoc spec specification))))
+          (unless val
+            (error "Invalid format character: %s" spec))
+          ;; Pad result to desired length.
+          (let ((text (format (concat "%" num "s") val)))
+            ;; Insert first, to preserve text properties.
+            (insert-and-inherit text)
+            ;; Delete the specifier body.
+            (delete-region (+ (match-beginning 0) (length text))
+                           (+ (match-end 0) (length text)))
+            ;; Delete the percent sign.
+            (delete-region (1- (match-beginning 0)) (match-beginning 0)))))
+       ;; Signal an error on bogus format strings.
+       (t
+        (error "Invalid format string"))))
+    (buffer-string)))
+
+;; --------------------------------------------------
+;; Display formats - defaults
+;; --------------------------------------------------
+
+;; FIXME: optional format strings would avoid having to define a
+;; function for specifiers which may be empty.
+
+(defvar emms-browser-default-format "%i%n"
+  "indent + name")
+
+;; tracks
+(defvar emms-browser-info-title-format
+  'emms-browser-track-artist-and-title-format)
+(defvar emms-browser-playlist-info-title-format
+  'emms-browser-track-artist-and-title-format)
+
+(defun emms-browser-get-format (bdata target)
+  (let* ((type (emms-browser-bdata-type bdata))
+         (target-str (or
+                      (and (eq target 'browser) "")
+                      (concat (symbol-name target) "-")))
+         (sym
+          (intern
+           (concat "emms-browser-"
+                   target-str
+                   (symbol-name type)
+                   "-format"))))
+    (if (boundp sym)
+        (symbol-value sym)
+      emms-browser-default-format)))
+
+(defun emms-browser-track-artist-and-title-format (bdata fmt)
+  (concat
+   "%i"
+   (let ((track (emms-browser-format-elem fmt "T")))
+     (if (and track (not (string= track "0")))
+         "%T. "
+       ""))
+   "%n"))
+
+;; albums - we define two formats, one for a small cover (browser),
+;; and one for a medium sized cover (playlist).
+(defvar emms-browser-info-album-format
+  'emms-browser-year-and-album-fmt)
+(defvar emms-browser-playlist-info-album-format
+  'emms-browser-year-and-album-fmt-med)
+
+(defun emms-browser-year-and-album-fmt (bdata fmt)
+  (concat
+   "%i%cS"
+   (let ((year (emms-browser-format-elem fmt "y")))
+     (if (and year (not (string= year "0")))
+         "(%y) "
+       ""))
+   "%n"))
+
+(defun emms-browser-year-and-album-fmt-med (bdata fmt)
+  (concat
+   "%i%cM"
+   (let ((year (emms-browser-format-elem fmt "y")))
+     (if (and year (not (string= year "0")))
+         "(%y) "
+       ""))
+   "%n"))
+
+;; --------------------------------------------------
+;; Display faces
+;; --------------------------------------------------
+
+(defmacro emms-browser-make-face (name dark-col light-col height)
+  (let ((face-name (intern (concat "emms-browser-" name "-face"))))
+    `(defface ,face-name
+       '((((class color) (background dark))
+          (:foreground ,dark-col
+                       :height ,height))
+         (((class color) (background light))
+          (:foreground ,light-col
+                       :height ,height))
+         (((type tty) (class mono))
+          (:inverse-video t))
+         (t (:background ,dark-col)))
+       ,(concat "Face for "
+                name
+                " in a browser/playlist buffer.")
+       :group 'emms-browser-mode)))
+
+(emms-browser-make-face "year/genre" "#aaaaff" "#444477" 1.5)
+(emms-browser-make-face "artist"     "#aaaaff" "#444477" 1.3)
+(emms-browser-make-face "composer"   "#aaaaff" "#444477" 1.3)
+(emms-browser-make-face "performer"  "#aaaaff" "#444477" 1.3)
+(emms-browser-make-face "album"      "#aaaaff" "#444477" 1.1)
+(emms-browser-make-face "track"      "#aaaaff" "#444477" 1.0)
+
+;; --------------------------------------------------
+;; Filtering
+;; --------------------------------------------------
+
+(defvar emms-browser-filters nil
+  "A list of available filters.")
+
+(defmacro emms-browser-make-filter (name func)
+  "Make a user-level function for filtering tracks.
+This:
+ - defines an interactive function M-x emms-browser-show-NAME.
+ - defines a variable emms-browser-filter-NAME of (name . func).
+ - adds the filter to emms-browser-filters."
+  (let ((funcnam (intern (concat "emms-browser-show-" name)))
+        (var  (intern (concat "emms-browser-filter-" name)))
+        (desc (concat "Filter the cache using rule '"
+                       name "'")))
+    `(progn
+       (defvar ,var nil ,desc)
+       (setq ,var (cons ,name ,func))
+       (add-to-list 'emms-browser-filters ,var)
+       (defun ,funcnam ()
+         ,desc
+         (interactive)
+         (emms-browser-refilter ,var)))))
+
+(defun emms-browser-set-filter (filter)
+  "Set the current filter to be used on next update.
+This does not refresh the current buffer."
+  (setq emms-browser-filter-tracks-hook (cdr filter))
+  (setq emms-browser-current-filter-name (car filter))
+  (run-hooks 'emms-browser-filter-changed-hook))
+
+(defun emms-browser-refilter (filter)
+  "Filter and render the top-level tracks."
+  (emms-browser-set-filter filter)
+  (emms-browse-by (or emms-browser-top-level-type
+                      emms-browser-default-browse-type)))
+
+(defun emms-browser-next-filter (&optional reverse)
+  "Redisplay with the next filter."
+  (interactive)
+  (let* ((list (if reverse
+                  (reverse emms-browser-filters)
+                 emms-browser-filters))
+         (key emms-browser-current-filter-name)
+         (next (cadr (member (assoc key list) list))))
+    ;; wrapped
+    (unless next
+      (setq next (car list)))
+    (emms-browser-refilter next)))
+
+(defun emms-browser-previous-filter ()
+  "Redisplay with the previous filter."
+  (interactive)
+  (emms-browser-next-filter t))
+
+(defun emms-browser-filter-only-dir (path)
+  "Generate a function which checks if a track is in path.
+If the track is not in path, return t."
+  `(lambda (track)
+     (not (string-match ,(concat "^" (expand-file-name path))
+                        (emms-track-get track 'name)))))
+
+(defun emms-browser-filter-only-type (type)
+  "Generate a function which checks a track's type.
+If the track is not of TYPE, return t."
+  `(lambda (track)
+     (not (eq (quote ,type) (emms-track-get track 'type)))))
+
+;; seconds in a day (* 60 60 24) = 86400
+(defun emms-browser-filter-only-recent (days)
+  "Show only tracks played within the last number of DAYS."
+  `(lambda (track)
+     (let ((min-date (time-subtract
+                      (current-time)
+                      (seconds-to-time (* ,days 86400))))
+           last-played)
+       (not (and (setq last-played
+                       (emms-track-get track 'last-played nil))
+                 (time-less-p min-date last-played))))))
+
+(provide 'emms-browser)
+;;; emms-browser.el ends here
diff --git a/.emacs.d/elisp/emms/lisp/emms-cache.el b/.emacs.d/elisp/emms/lisp/emms-cache.el
new file mode 100644 (file)
index 0000000..4e45eda
--- /dev/null
@@ -0,0 +1,183 @@
+;;; emms-cache.el --- persistence for emms-track
+
+;; Copyright (C) 2006, 2007, 2008, 2009  Free Software Foundation, Inc.
+
+;; Author: Damien Elmes <emacs@repose.cx>
+;; Keywords: emms, mp3, mpeg, multimedia
+
+;; This file is part of EMMS.
+
+;; EMMS is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; EMMS is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with EMMS; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; The cache is a mapping of a full path name to information, and so
+;; it is invalidated when you rename or move files about.  It also
+;; does not differentiate between file or uri tracks.
+
+;; Because cache lookups are much faster than disk access, this works
+;; much better with a later-do-interval of something like 0.001.  Also
+;; consider using synchronous mode, as it's quite fast now.
+
+;; This code is activated by (emms-standard) and above.
+
+;; To activate it by hand, use:
+
+;; (emms-cache 1)
+
+;;; Code:
+
+(require 'emms)
+(require 'emms-info)
+
+(when (fboundp 'define-hash-table-test)
+  (define-hash-table-test 'string-hash 'string= 'sxhash))
+
+(defvar emms-cache-db (make-hash-table
+                       :test (if (fboundp 'define-hash-table-test)
+                                 'string-hash
+                               'equal))
+  "A mapping of paths to file info.
+This is used to cache over emacs sessions.")
+
+(defvar emms-cache-dirty nil
+  "True if the cache has been updated since init.")
+
+(defcustom emms-cache-file (concat (file-name-as-directory emms-directory) "cache")
+  "A file used to store cached file information over sessions."
+  :group 'emms
+  :type 'file)
+
+(defcustom emms-cache-file-coding-system 'utf-8
+  "Coding system used for saving `emms-cache-file'."
+  :group 'emms
+  :type 'coding-system)
+
+(defun emms-cache (arg)
+  "Turn on Emms caching if ARG is positive, off otherwise."
+  (interactive "p")
+  (if (and arg (> arg 0))
+      (progn
+       (unless emms-cache-dirty
+         (emms-cache-restore))
+        (unless noninteractive
+          (add-hook 'kill-emacs-hook 'emms-cache-save))
+        (setq emms-cache-get-function 'emms-cache-get)
+        (setq emms-cache-set-function 'emms-cache-set)
+        (setq emms-cache-modified-function 'emms-cache-dirty))
+    (remove-hook 'kill-emacs-hook 'emms-cache-save)
+    (setq emms-cache-get-function nil)
+    (setq emms-cache-set-function nil)
+    (setq emms-cache-modified-function nil)))
+
+;;;###autoload
+(defun emms-cache-enable ()
+  "Enable caching of Emms track data."
+  (interactive)
+  (emms-cache 1)
+  (message "Emms cache enabled"))
+
+;;;###autoload
+(defun emms-cache-disable ()
+  "Disable caching of Emms track data."
+  (interactive)
+  (emms-cache -1)
+  (message "Emms cache disabled"))
+
+;;;###autoload
+(defun emms-cache-toggle ()
+  "Toggle caching of Emms track data."
+  (interactive)
+  (if emms-cache-get-function
+      (emms-cache-disable)
+    (emms-cache-enable)))
+
+(defsubst emms-cache-dirty (&rest ignored)
+  "Mark the cache as dirty."
+  (setq emms-cache-dirty t))
+
+(defun emms-cache-get (type path)
+  "Return a cache element for PATH, or nil."
+  (gethash path emms-cache-db))
+
+;; Note we ignore TYPE, as it's stored in TRACK
+(defun emms-cache-set (type path track)
+  "Set PATH to TRACK in the cache."
+  (puthash path track emms-cache-db)
+  (emms-cache-dirty))
+
+(defun emms-cache-del (path)
+  "Remove a track from the cache, with key PATH."
+  (remhash path emms-cache-db)
+  (emms-cache-dirty))
+
+(defun emms-cache-save ()
+  "Save the track cache to a file."
+  (interactive)
+  (when emms-cache-dirty
+    (message "Saving emms track cache...")
+    (set-buffer (get-buffer-create " emms-cache "))
+    (erase-buffer)
+    (insert
+     (concat ";;; .emms-cache -*- mode: emacs-lisp; coding: "
+             (symbol-name emms-cache-file-coding-system)
+             "; -*-\n"))
+    (maphash (lambda (k v)
+               (insert (format
+                        "(puthash %S '%S emms-cache-db)\n" k v)))
+             emms-cache-db)
+    (when (fboundp 'set-buffer-file-coding-system)
+      (set-buffer-file-coding-system emms-cache-file-coding-system))
+    (unless (file-directory-p (file-name-directory emms-cache-file))
+      (make-directory (file-name-directory emms-cache-file)))
+    (write-region (point-min) (point-max) emms-cache-file)
+    (kill-buffer (current-buffer))
+    (message "Saving emms track cache...done")
+    (setq emms-cache-dirty nil)))
+
+(defun emms-cache-restore ()
+  "Restore the track cache from a file."
+  (interactive)
+  (load emms-cache-file t nil t)
+  (setq emms-cache-dirty nil))
+
+(defun emms-cache-sync ()
+  "Sync the cache with the data on disc.
+Remove non-existent files, and update data for files which have
+been modified."
+  (interactive)
+  (message "Syncing emms track cache...")
+  (let (removed)
+    (maphash (lambda (path track)
+               (when (eq (emms-track-get track 'type) 'file)
+                 ;; if no longer here, remove
+                 (if (not (file-exists-p path))
+                     (progn
+                       (remhash path emms-cache-db)
+                       (setq removed t))
+                   (let ((file-mtime (emms-info-track-file-mtime track))
+                         (info-mtime (emms-track-get track 'info-mtime)))
+                     (when (or (not info-mtime)
+                               (emms-time-less-p
+                                info-mtime file-mtime))
+                       (run-hook-with-args 'emms-info-functions track))))))
+             emms-cache-db)
+    (when removed
+      (setq emms-cache-dirty t)))
+  (message "Syncing emms track cache...done"))
+
+(provide 'emms-cache)
+;;; emms-cache.el ends here
diff --git a/.emacs.d/elisp/emms/lisp/emms-compat.el b/.emacs.d/elisp/emms/lisp/emms-compat.el
new file mode 100644 (file)
index 0000000..ba233ea
--- /dev/null
@@ -0,0 +1,182 @@
+;;; emms-compat.el --- Compatibility routines for EMMS
+
+;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+
+;; Author: Michael Olson <mwolson@gnu.org>
+
+;; This file is part of EMMS.
+
+;; EMMS is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+;;
+;; EMMS is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with EMMS; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; These are functions and macros that EMMS needs in order to be
+;; compatible with various Emacs and XEmacs versions.
+
+;;; Code:
+
+\f
+;;; Miscellaneous
+
+(defun emms-propertize (string &rest properties)
+  (if (fboundp 'propertize)
+      (apply #'propertize string properties)
+    (set-text-properties 0 (length string) properties string)
+    string))
+
+;; Emacs accepts three arguments to `make-obsolete', but the XEmacs
+;; version only takes two arguments
+(defun emms-make-obsolete (old-name new-name when)
+  "Make the byte-compiler warn that OLD-NAME is obsolete.
+The warning will say that NEW-NAME should be used instead.
+WHEN should be a string indicating when the function was
+first made obsolete, either the file's revision number or an
+EMMS release version number."
+  (if (featurep 'xemacs)
+      (make-obsolete old-name new-name)
+    (make-obsolete old-name new-name when)))
+
+\f
+;;; Time and timers
+
+(defun emms-cancel-timer (timer)
+  "Cancel the given TIMER."
+  (when timer
+    (cond ((fboundp 'cancel-timer)
+           (cancel-timer timer))
+          ((fboundp 'delete-itimer)
+           (delete-itimer timer)))))
+
+(defun emms-time-less-p (t1 t2)
+  "Say whether time T1 is less than time T2."
+  (or (< (car t1) (car t2))
+      (and (= (car t1) (car t2))
+           (< (nth 1 t1) (nth 1 t2)))))
+
+\f
+;;; Highline
+
+(defun emms-activate-highlighting-mode ()
+  "Activate highline mode."
+  (if (featurep 'xemacs)
+      (progn
+        (require 'highline)
+        (highline-local-mode 1))
+    (progn
+      (require 'hl-line)
+      (hl-line-mode 1))))
+
+(defun emms-line-highlight ()
+  "Highlight the current line. You must call
+emms-activate-highlighting-mode beforehand."
+  (if (featurep 'xemacs)
+      (highline-highlight-current-line)
+    (hl-line-highlight)))
+
+\f
+;;; Movement and position
+
+(defun emms-move-beginning-of-line (arg)
+  "Move point to beginning of current line as displayed.
+If there's an image in the line, this disregards newlines
+which are part of the text that the image rests on."
+  (if (fboundp 'move-beginning-of-line)
+      (move-beginning-of-line arg)
+    (if (numberp arg)
+        (forward-line (1- arg))
+      (forward-line 0))))
+
+(defun emms-line-number-at-pos (&optional pos)
+  "Return (narrowed) buffer line number at position POS.
+If POS is nil, use current buffer location."
+  (if (fboundp 'line-number-at-pos)
+      (line-number-at-pos pos)
+    (let ((opoint (or pos (point))) start)
+      (save-excursion
+        (goto-char (point-min))
+        (setq start (point))
+        (goto-char opoint)
+        (forward-line 0)
+        (1+ (count-lines start (point)))))))
+
+\f
+;;; Regular expression matching
+
+(defun emms-replace-regexp-in-string (regexp replacement text
+                                      &optional fixedcase literal)
+  "Replace REGEXP with REPLACEMENT in TEXT.
+If fourth arg FIXEDCASE is non-nil, do not alter case of replacement text.
+If fifth arg LITERAL is non-nil, insert REPLACEMENT literally."
+  (cond
+   ((fboundp 'replace-regexp-in-string)
+    (replace-regexp-in-string regexp replacement text fixedcase literal))
+   ((and (featurep 'xemacs) (fboundp 'replace-in-string))
+    (replace-in-string text regexp replacement literal))
+   (t (let ((repl-len (length replacement))
+            start)
+        (save-match-data
+          (while (setq start (string-match regexp text start))
+            (setq start (+ start repl-len)
+                  text (replace-match replacement fixedcase literal text)))))
+      text)))
+
+(defun emms-match-string-no-properties (num &optional string)
+  (if (fboundp 'match-string-no-properties)
+      (match-string-no-properties num string)
+    (match-string num string)))
+
+\f
+;;; Common Lisp
+
+(defun emms-delete-if (predicate seq)
+  "Remove all items satisfying PREDICATE in SEQ.
+This is a destructive function: it reuses the storage of SEQ
+whenever possible."
+  ;; remove from car
+  (while (when (funcall predicate (car seq))
+           (setq seq (cdr seq))))
+  ;; remove from cdr
+  (let ((ptr seq)
+        (next (cdr seq)))
+    (while next
+      (when (funcall predicate (car next))
+        (setcdr ptr (if (consp next)
+                        (cdr next)
+                      nil)))
+      (setq ptr (cdr ptr))
+      (setq next (cdr ptr))))
+  seq)
+
+(defun emms-find-if (predicate seq)
+  "Find the first item satisfying PREDICATE in SEQ.
+Return the matching item, or nil if not found."
+  (catch 'found
+    (dolist (el seq)
+      (when (funcall predicate el)
+        (throw 'found el)))))
+
+(defun emms-remove-if-not (predicate seq)
+  "Remove all items not satisfying PREDICATE in SEQ.
+This is a non-destructive function; it makes a copy of SEQ to
+avoid corrupting the original SEQ."
+  (let (newseq)
+    (dolist (el seq)
+      (when (funcall predicate el)
+        (setq newseq (cons el newseq))))
+    (nreverse newseq)))
+
+(provide 'emms-compat)
+;;; emms-compat.el ends here
diff --git a/.emacs.d/elisp/emms/lisp/emms-cue.el b/.emacs.d/elisp/emms/lisp/emms-cue.el
new file mode 100644 (file)
index 0000000..aefcafd
--- /dev/null
@@ -0,0 +1,120 @@
+;;; emms-cue.el --- Recognize cue sheet file
+
+;; Copyright (C) 2009 Free Software Foundation, Inc.
+
+;; Author: William Xu <william.xwl@gmail.com>
+
+;; This file is part of EMMS.
+
+;; EMMS is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License
+;; as published by the Free Software Foundation; either version 3
+;; of the License, or (at your option) any later version.
+
+;; EMMS is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with EMMS; if not, write to the Free Software Foundation,
+;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; By parsing cue file, we will be able to play next/previous track from a
+;; single .ape or .flac file.
+
+;;; Code:
+
+(require 'emms-playing-time)
+(require 'emms-info)
+
+(defun emms-cue-next ()
+  "Play next track from .cue file."
+  (interactive)
+  (let ((cue-track (emms-cue-next-track)))
+    (if (cdr cue-track)
+        (progn
+          (emms-seek-to (cdr cue-track))
+          (message "Will play: %s" (car cue-track)))
+      (message "Nothing to seek or missing .cue file?"))))
+
+(defun emms-cue-previous ()
+  "Play previous track from .cue file."
+  (interactive)
+  (let ((cue-track (emms-cue-previous-track)))
+    (if (cdr cue-track)
+        (progn
+          (emms-seek-to (cdr cue-track))
+          (message "Will play: %s" (car cue-track)))
+      (message "Nothing to seek or missing .cue file?"))))
+
+(defun emms-cue-next-track (&optional previous-p)
+  "Get title and offset of next track from .cue file.
+
+When PREVIOUS-P is t, get previous track info instead."
+  (let* ((track (emms-playlist-current-selected-track))
+         (name (emms-track-get track 'name))
+         (cue (concat (file-name-sans-extension name)".cue")))
+    (when (file-exists-p cue)
+      (with-temp-buffer
+        (emms-insert-file-contents cue)
+        (save-excursion
+          (if previous-p
+              (goto-char (point-max))
+            (goto-char (point-min)))
+          (let ((offset nil)
+                (title "")
+                ;; We should search one more track far when getting previous
+                ;; track.
+                (one-more-track previous-p))
+            (while (and (not offset)
+                        (funcall 
+                         (if previous-p 'search-backward-regexp 'search-forward-regexp)
+                         "INDEX 01 \\([0-9][0-9]\\):\\([0-9][0-9]\\):\\([0-9][0-9]\\)" nil t 1))
+              (let* ((min (string-to-number (match-string-no-properties 1)))
+                     (sec (string-to-number (match-string-no-properties 2)))
+                     (msec (string-to-number (match-string-no-properties 3)))
+                     (total-sec (+ (* min 60) sec (/ msec 100.0))))
+                (when (funcall (if previous-p '> '<) emms-playing-time total-sec)
+                  (if (not one-more-track)
+                      (progn
+                        (setq offset total-sec)
+                        (when (search-backward-regexp "TITLE \"\\(.*\\)\"" nil t 1)
+                          (setq title (match-string-no-properties 1))))
+                    (setq one-more-track nil)))))
+            (cons title offset)))))))
+
+(defun emms-cue-previous-track ()
+  "See `emms-cue-next-track'."
+  (emms-cue-next-track t))
+
+(defun emms-info-cueinfo (track)
+  "Add track information to TRACK.
+This is a useful element for `emms-info-functions'."
+  (when (and (eq 'file (emms-track-type track))
+             (string-match "\\.\\(ape\\|flac\\)\\'" (emms-track-name track)))
+    (let ((cue (concat (file-name-sans-extension (emms-track-name track))
+                       ".cue")))
+      (when (file-exists-p cue)
+        (with-temp-buffer
+          (emms-insert-file-contents cue)
+          (save-excursion
+            (mapc (lambda (i)
+                    (goto-char (point-min))
+                    (when (let ((case-fold-search t))
+                            (search-forward-regexp 
+                             (concat (car i) " \\(.*\\)") nil t 1))
+                      (emms-track-set track 
+                                      (cdr i)
+                                      (replace-regexp-in-string
+                                       "\\`\"\\|\"\\'" "" (match-string 1)))))
+                  '(("performer" . info-artist)
+                    ("title" . info-album)
+                    ("title" . info-title)
+                    ("rem date" . info-year)))))))))
+
+
+(provide 'emms-cue)
+;;; emms-cue.el ends here
diff --git a/.emacs.d/elisp/emms/lisp/emms-history.el b/.emacs.d/elisp/emms/lisp/emms-history.el
new file mode 100644 (file)
index 0000000..4d3238b
--- /dev/null
@@ -0,0 +1,135 @@
+;;; emms-history.el -- save all playlists when exiting emacs
+
+;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+;;
+;; Author: Ye Wenbin <wenbinye@163.com>
+
+;; This file is part of EMMS.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program; if not, write to the Free Software
+;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;; Commentary:
+
+;; Saves all playlists when you close emacs.  When you start it up again use
+;; M-x emms-history-load to restore all saved playlists.
+
+;; To use it put the following into your ~/.emacs:
+;;
+;;   (require 'emms-history)
+;;
+;; If all playlists should be restored on startup add this, too:
+;;
+;;   (emms-history-load)
+
+;;; Code:
+
+(require 'emms)
+(eval-when-compile
+  (require 'cl))
+
+(defgroup emms-history nil
+  "Saving and restoring all playlists when closing/restarting
+Emacs."
+  :prefix "emms-history-"
+  :group 'emms)
+
+(defcustom emms-history-file (concat (file-name-as-directory emms-directory) "history")
+  "The file to save playlists in."
+  :type   'string
+  :group  'emms-history)
+
+(defcustom emms-history-start-playing nil
+  "If non-nil emms starts playing the current track after
+`emms-history-load' was invoked."
+  :type   'boolean
+  :group  'emms-history)
+
+(defcustom emms-history-file-coding-system 'utf-8
+  "Coding system used for saving `emms-history-file'."
+  :type 'coding-system
+  :group 'emms-history)
+
+(defun emms-history-save ()
+  "Save all playlists that are open in this Emacs session."
+  (interactive)
+  (when (stringp emms-history-file)
+    (let ((oldbuf emms-playlist-buffer)
+          ;; print with no limit
+          print-length print-level
+          emms-playlist-buffer playlists)
+      (save-excursion
+        (dolist (buf (emms-playlist-buffer-list))
+          (set-buffer buf)
+          (when (> (buffer-size) 0) ; make sure there is track in the buffer
+            (setq emms-playlist-buffer buf
+                  playlists
+                  (cons
+                   (list (buffer-name)
+                         (or
+                          (and emms-playlist-selected-marker
+                               (marker-position emms-playlist-selected-marker))
+                          (point-min))
+                         (save-restriction
+                           (widen)
+                           (nreverse
+                            (emms-playlist-tracks-in-region (point-min)
+                                                            (point-max)))))
+                   playlists))))
+        (with-temp-buffer
+          (insert
+           (concat ";;; emms history -*- mode: emacs-lisp; coding: "
+                   (symbol-name emms-history-file-coding-system)
+                   "; -*-\n"))
+          (insert "(\n;; active playlist\n")
+          (prin1 (buffer-name oldbuf) (current-buffer))
+          (insert "\n;; playlists: ((BUFFER_NAME SELECT_POSITION TRACKS) ...)\n")
+          (prin1 playlists (current-buffer))
+          (insert "\n;; play method\n")
+          (prin1 `((emms-repeat-track . ,emms-repeat-track)
+                   (emms-repeat-playlist . ,emms-repeat-playlist))
+                 (current-buffer))
+          (insert "\n)")
+          (write-file emms-history-file))))))
+
+(unless noninteractive
+  (add-hook 'kill-emacs-hook 'emms-history-save))
+
+(defun emms-history-load ()
+  "Restore all playlists in `emms-history-file'."
+  (interactive)
+  (when (and (stringp emms-history-file)
+             (file-exists-p emms-history-file))
+    (let (history buf)
+      (with-temp-buffer
+        (emms-insert-file-contents emms-history-file)
+        (setq history (read (current-buffer)))
+        (dolist (playlist (cadr history))
+          (with-current-buffer (emms-playlist-new (car playlist))
+            (setq emms-playlist-buffer (current-buffer))
+            (if (string= (car playlist) (car history))
+                (setq buf (current-buffer)))
+            (mapc 'emms-playlist-insert-track
+                  (nth 2 playlist))
+            (ignore-errors
+              (emms-playlist-select (cadr playlist)))))
+        (setq emms-playlist-buffer buf)
+        (dolist (method (nth 2 history))
+          (set (car method) (cdr method)))
+        (ignore-errors
+          (when emms-history-start-playing
+            (emms-start)))))))
+
+(provide 'emms-history)
+;;; emms-history.el ends here
diff --git a/.emacs.d/elisp/emms/lisp/emms-i18n.el b/.emacs.d/elisp/emms/lisp/emms-i18n.el
new file mode 100644 (file)
index 0000000..748f4aa
--- /dev/null
@@ -0,0 +1,182 @@
+;;; emms-i18n.el --- functions for handling coding systems
+
+;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+
+;; Author: Ye Wenbin <wenbinye@163.com>
+
+;; This file is part of EMMS.
+
+;; This program is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation; either version 3, or (at
+;; your option) any later version.
+
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;; General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program; if not, write to the Free Software
+;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;; Commentary:
+
+;; When reading from process, first check the car part of
+;; `emms-i18n-default-coding-system'; if non-nil, use this for
+;; decoding, and never detect coding system; if nil, first call
+;; `emms-i18n-coding-detect-functions' to get coding system, if
+;; success, decode the result, otherwise, use
+;; `emms-i18n-detect-coding-function', the Emacs detect coding
+;; function, if the coding detected is not in
+;; `emms-i18n-never-used-coding-system', decode it, otherwise use
+;; locale-coding-system.
+
+;; When writing/sending data to process, first check the cdr part of
+;; `emms-i18n-default-coding-system', if non-nil, use this to encode
+;; data, otherwise do nothing, that means use
+;; `default-process-coding-system' or `process-coding-system-alist' to
+;; encode data.
+
+;; Put this file into your load-path and the following into your
+;; ~/.emacs:
+
+;;   (require 'emms-i18n)
+
+;;; Code:
+
+(provide 'emms-i18n)
+(eval-when-compile
+  (require 'cl))
+
+;; TODO: Use defcustom.
+(defvar emms-i18n-never-used-coding-system
+  '(raw-text undecided)
+  "If the `emms-i18n-coding-detect-functions' return a coding
+system in this list, use `emms-i18n-default-coding-system'
+instead.")
+
+;; TODO: Use defcustom.
+(defvar emms-i18n-coding-system-for-read
+  'utf-8
+  "If coding detect fails, use this for decoding.")
+
+;; TODO: Use defcustom.
+(defvar emms-i18n-default-coding-system
+  '(no-conversion . no-conversion)
+  "If non-nil, use this for decoding and encoding.")
+
+;; TODO: Use defcustom.
+(defvar emms-i18n-coding-detect-functions
+  nil
+  "A list of functions to call to detect codings.")
+
+;; TODO: Use defcustom.
+(defvar emms-i18n-detect-max-size
+  10000
+  "Maximum amount of bytes to detect the coding system.  nil
+means to scan the whole buffer.")
+
+(defun emms-i18n-iconv (from to str)
+  "Convert string STR from FROM coding to TO coding."
+  (if (and from to)
+      (decode-coding-string
+       (encode-coding-string str to)
+       from)
+    str))
+
+(defun emms-i18n-iconv-region (beg end from to)
+  (when (and from to)
+    (save-restriction
+      (narrow-to-region beg end)
+      (encode-coding-region (point-min) (point-max) to)
+      (decode-coding-region (point-min) (point-max) from))))
+
+(defun emms-i18n-iconv-buffer (from to &optional buf)
+  "Convert buffer BUF from FROM coding to TO coding.  BUF
+defaults to the current buffer."
+  (save-excursion
+    (and buf (set-buffer buf))
+    (emms-i18n-iconv-region (point-min) (point-max) from to)))
+
+(defun emms-i18n-set-default-coding-system (read-coding write-coding)
+  "Set `emms-i18n-default-coding-system'."
+  (interactive "zSet coding system for read: \nzSet coding system for write: ")
+  (setq emms-i18n-default-coding-system
+        (cons
+         (and (coding-system-p read-coding) read-coding)
+         (and (coding-system-p write-coding) write-coding)))
+  (message (concat
+            (if (car emms-i18n-default-coding-system)
+                (format "The coding system for reading is %S." (car emms-i18n-default-coding-system))
+              "Good, you want me to detect the coding system!")
+            (format " The coding system for writing is %S."
+                    (or (cdr emms-i18n-default-coding-system)
+                        (cdr default-process-coding-system))))))
+
+(defun emms-i18n-call-process-simple (&rest args)
+  "Run a program and return the program result.
+If the car part of `emms-i18n-default-coding-system' is non-nil,
+the program result will be decoded using the car part of
+`emms-i18n-default-coding-system'.  Otherwise, use
+`emms-i18n-coding-detect-functions' to detect the coding system
+of the result.  If the `emms-i18n-coding-detect-functions'
+failed, use `emms-i18n-detect-coding-function' to detect coding
+system.  If all the coding systems are nil or in
+`emms-i18n-never-used-coding-system', decode the result using
+`emms-i18n-coding-system-for-read'.
+
+ARGS are the same as in `call-process', except BUFFER should
+always have the value t.  Otherwise the coding detection will not
+be performed."
+  (let ((default-process-coding-system (copy-tree default-process-coding-system))
+        (process-coding-system-alist nil) exit pos)
+    (when (eq (nth 2 args) 't)
+      (setcar default-process-coding-system (car emms-i18n-default-coding-system))
+      (setq pos (point)))
+    (setq exit (apply 'call-process args))
+    (when (and (eq (nth 2 args) 't)
+               (eq (car emms-i18n-default-coding-system) 'no-conversion))
+      (save-restriction
+        (narrow-to-region pos (point))
+        (decode-coding-region (point-min) (point-max) (emms-i18n-detect-buffer-coding-system))))
+    exit))
+
+;; TODO: Is this function useful?
+(defun emms-i18n-call-process (&rest args)
+  "Run the program like `call-process'.  If the cdr part of
+`emms-i18n-default-coding-system' is non-nil, the string in ARGS
+will be encoded by the cdr part of
+`emms-i18n-default-coding-system'; otherwise, all parameters are
+simply passed to `call-process'."
+  (with-temp-buffer
+    (if (cdr emms-i18n-default-coding-system)
+        (let ((default-process-coding-system emms-i18n-default-coding-system)
+              (process-coding-system-alist nil))
+          (apply 'call-process args))
+      (apply 'call-process args))))
+
+(defun emms-i18n-detect-coding-function (size)
+  (detect-coding-region (point)
+                        (+ (if (null emms-i18n-detect-max-size)
+                               size
+                             (min size emms-i18n-detect-max-size))
+                           (point)) t))
+
+(defun emms-i18n-detect-buffer-coding-system (&optional buf)
+  "Before calling this function, make sure the buffer is literal."
+  (let ((size (- (point-max) (point-min)))
+        (func (append emms-i18n-coding-detect-functions 'emms-i18n-detect-coding-function))
+        coding)
+    (save-excursion
+      (and buf (set-buffer buf))
+      (goto-char (point-min))
+      (when (> size 0)
+        (setq coding (run-hook-with-args-until-success 'func size))
+        (if (member (coding-system-base coding) emms-i18n-never-used-coding-system)
+            (setq coding (emms-i18n-detect-coding-function size))))
+      (if (or (null coding) (member (coding-system-base coding) emms-i18n-never-used-coding-system))
+          emms-i18n-coding-system-for-read
+        coding))))
+
+;;; emms-i18n.el ends here
diff --git a/.emacs.d/elisp/emms/lisp/emms-info-libtag.el b/.emacs.d/elisp/emms/lisp/emms-info-libtag.el
new file mode 100644 (file)
index 0000000..5bb49e8
--- /dev/null
@@ -0,0 +1,82 @@
+;;; emms-info-libtag.el --- Info-method for EMMS using libtag
+
+;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008,
+;;   2009 Free Software Foundation, Inc.
+
+;; Authors: Ulrik Jensen <terryp@daimi.au.dk>
+;;          Jorgen Schäfer <forcer@forcix.cx>
+;; Keywords:
+
+;; This file is part of EMMS.
+
+;; EMMS is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; EMMS is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with EMMS; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; This code has been adapted from code found in mp3player.el, written
+;; by Jean-Philippe Theberge (jphiltheberge@videotron.ca), Mario
+;; Domgoergen (kanaldrache@gmx.de) and Jorgen Schäfer
+;; <forcer@forcix.cx>
+
+;; To activate this method for getting info, use something like:
+
+;; (require 'emms-info-libtag)
+;; (add-to-list 'emms-info-functions 'emms-info-libtag)
+
+;; Note that you should remove emms-info-mp3info and emms-info-ogginfo
+;; from the emms-info-functions list if you want to avoid
+;; conflicts. For example, to set libtag as your exclusive info
+;; provider:
+
+;; (setq emms-info-functions '(emms-info-libtag))
+
+;;; Code:
+
+(require 'emms-info)
+
+(defvar emms-info-libtag-coding-system 'utf-8)
+(defvar emms-info-libtag-program-name "emms-print-metadata")
+
+(defun emms-info-libtag (track)
+  (when (and (eq 'file (emms-track-type track))
+             (string-match 
+              "\\.\\([Mm][Pp]3\\|[oO][gG][gG]\\|[fF][lL][aA][cC]\\|[sS][pP][xX]\\)\\'"
+              (emms-track-name track)))
+    (with-temp-buffer
+      (when (zerop
+             (let ((coding-system-for-read 'utf-8))
+               (call-process emms-info-libtag-program-name
+                             nil '(t nil) nil
+                             (emms-track-name track))))
+        (goto-char (point-min))
+        ;; Crush the trailing whitespace
+        (while (re-search-forward "[[:space:]]+$" nil t)
+          (replace-match "" nil nil))
+        (goto-char (point-min))
+        (while (looking-at "^\\([^=\n]+\\)=\\(.*\\)$")
+          (let ((name (intern-soft (match-string 1)))
+                (value (match-string 2)))
+            (when (> (length value)
+                     0)
+              (emms-track-set track
+                              name
+                              (if (eq name 'info-playing-time)
+                                  (string-to-number value)
+                                value))))
+          (forward-line 1))))))
+
+(provide 'emms-info-libtag)
+;;; emms-info-libtag.el ends here
diff --git a/.emacs.d/elisp/emms/lisp/emms-info-metaflac.el b/.emacs.d/elisp/emms/lisp/emms-info-metaflac.el
new file mode 100644 (file)
index 0000000..5e50578
--- /dev/null
@@ -0,0 +1,108 @@
+;;; emms-info-metaflac.el --- Info-method for EMMS using metaflac
+
+;; Copyright (C) 2006, 2007, 2008, 2009  Free Software Foundation, Inc.
+
+;; Author: Matthew Kennedy <mkennedy@gentoo.org>
+;; Keywords:
+
+;; This file is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; This file is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+;; Boston, MA 02110-1301 USA
+
+;;; Commentary:
+
+;; This code has been adapted from code found in emms-info-mp3info.el
+;; written by Ulrik Jensen <terryp@daimi.au.dk> which contains the
+;; following attribution:
+
+;; This code has been adapted from code found in mp3player.el, written
+;; by Jean-Philippe Theberge (jphiltheberge@videotron.ca), Mario
+;; Domgoergen (kanaldrache@gmx.de) and Jorgen Schäfer
+;; <forcer@forcix.cx>
+
+;; To activate this method for getting info, use something like:
+
+;; (require 'emms-info-metaflac)
+;; (add-to-list 'emms-info-methods-list 'emms-info-metaflac)
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+(require 'emms-info)
+
+(defvar emms-info-metaflac-version "0.1 $Revision: 1.10 $"
+  "EMMS info metaflac version string.")
+
+;; $Id: emms-info-mp3info.el,v 1.10 2005/08/12 18:01:16 xwl Exp $
+
+(defgroup emms-info-metaflac nil
+  "An EMMS-info method for getting/setting FLAC tags, using the
+external metaflac program"
+  :group 'emms-info)
+
+(defcustom emms-info-metaflac-program-name "metaflac"
+  "*The name/path of the metaflac program."
+  :type 'string
+  :group 'emms-info-metaflac)
+
+(defcustom emms-info-metaflac-options
+  '("--no-utf8-convert"
+    "--show-tag=TITLE"
+    "--show-tag=ARTIST"
+    "--show-tag=ALBUM"
+    "--show-tag=NOTE"
+    "--show-tag=YEAR"
+    "--show-tag=TRACKNUMBER"
+    "--show-tag=DISCNUMBER"
+    "--show-tag=GENRE")
+  "The argument to pass to `emms-info-metaflac-program-name'."
+  :type '(repeat string)
+  :group 'emms-info-metaflac)
+
+(defun emms-info-metaflac (track)
+  "Get the FLAC tag of file TRACK, using `emms-info-metaflac-program'
+and return an emms-info structure representing it."
+  (when (and (eq 'file (emms-track-type track))
+             (string-match "\\.\\(flac\\|FLAC\\)\\'" (emms-track-name track)))
+    (with-temp-buffer
+      (when (zerop
+             (apply 'call-process
+              emms-info-metaflac-program-name
+              nil t nil
+              "--show-total-samples"
+              "--show-sample-rate"
+              (append emms-info-metaflac-options
+                      (list (emms-track-name track)))))
+        (goto-char (point-min))
+        (emms-track-set track 'info-playing-time
+                        (/ (string-to-number (buffer-substring (point) (line-end-position)))
+                           (progn
+                             (forward-line 1)
+                             (string-to-number (buffer-substring (point) (line-end-position))))))
+        (forward-line 1)
+        (while (looking-at "^\\([^=\n]+\\)=\\(.*\\)$")
+          (let ((name (intern (concat "info-" (downcase (match-string 1)))))
+                (value (match-string 2)))
+            (when (> (length value)
+                     0)
+              (emms-track-set track
+                              name
+                              (if (eq name 'info-playing-time)
+                                  (string-to-number value)
+                                value))))
+          (forward-line 1))))))
+
+(provide 'emms-info-metaflac)
+
+;;; emms-info-metaflac.el ends here
diff --git a/.emacs.d/elisp/emms/lisp/emms-info-mp3info.el b/.emacs.d/elisp/emms/lisp/emms-info-mp3info.el
new file mode 100644 (file)
index 0000000..b01217e
--- /dev/null
@@ -0,0 +1,104 @@
+;;; emms-info-mp3info.el --- Info-method for EMMS using mp3info
+
+;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008,
+;;   2009 Free Software Foundation, Inc.
+
+;; Authors: Ulrik Jensen <terryp@daimi.au.dk>
+;;          Jorgen Schäfer <forcer@forcix.cx>
+;; Keywords:
+
+;; This file is part of EMMS.
+
+;; EMMS is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; EMMS is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with EMMS; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; This code has been adapted from code found in mp3player.el, written
+;; by Jean-Philippe Theberge (jphiltheberge@videotron.ca), Mario
+;; Domgoergen (kanaldrache@gmx.de) and Jorgen Schäfer
+;; <forcer@forcix.cx>
+
+;; To activate this method for getting info, use something like:
+
+;; (require 'emms-info-mp3info)
+;; (add-to-list 'emms-info-functions 'emms-info-mp3info)
+
+;;; Code:
+
+(require 'emms-info)
+
+(defvar emms-info-mp3info-version "0.2 $Revision: 1.10 $"
+  "EMMS info mp3info version string.")
+;; $Id: emms-info-mp3info.el,v 1.10 2005/08/12 18:01:16 xwl Exp $
+
+(defgroup emms-info-mp3info nil
+  "An EMMS-info method for getting/setting ID3v1 tags, using the
+external mp3info program"
+  :group 'emms-info)
+
+(defcustom emms-info-mp3info-coding-system 'utf-8
+  "*Coding system used in the output of mp3info."
+  :type 'coding-system
+  :group 'emms-info-mp3info)
+
+(defcustom emms-info-mp3info-program-name "mp3info"
+  "*The name/path of the mp3info tag program."
+  :type 'string
+  :group 'emms-info-mp3info)
+
+(defcustom emms-info-mp3find-arguments
+  `("-p" ,(concat "info-artist=%a\\n"
+                  "info-title=%t\\n"
+                  "info-album=%l\\n"
+                  "info-tracknumber=%n\\n"
+                  "info-year=%y\\n"
+                  "info-genre=%g\\n"
+                  "info-note=%c\\n"
+                  "info-playing-time=%S\\n"))
+  "The argument to pass to `emms-info-mp3info-program-name'.
+This should be a list of info-flag=value lines."
+  :type '(repeat string)
+  :group 'emms-info-mp3info)
+
+(defun emms-info-mp3info (track)
+  "Add track information to TRACK.
+This is a useful element for `emms-info-functions'."
+  (when (and (eq 'file (emms-track-type track))
+             (string-match "\\.[Mm][Pp]3\\'" (emms-track-name track)))
+    (with-temp-buffer
+      (when (zerop
+             (apply (if (fboundp 'emms-i18n-call-process-simple)
+                        'emms-i18n-call-process-simple
+                      'call-process)
+                    emms-info-mp3info-program-name
+                    nil t nil
+                    (append emms-info-mp3find-arguments
+                            (list (emms-track-name track)))))
+        (goto-char (point-min))
+        (while (looking-at "^\\([^=\n]+\\)=\\(.*\\)$")
+          (let ((name (intern (match-string 1)))
+                (value (match-string 2)))
+            (when (> (length value)
+                     0)
+              (emms-track-set track
+                              name
+                              (if (eq name 'info-playing-time)
+                                  (string-to-number value)
+                                value))))
+          (forward-line 1))))))
+
+(provide 'emms-info-mp3info)
+;;; emms-info-mp3info.el ends here
diff --git a/.emacs.d/elisp/emms/lisp/emms-info-ogginfo.el b/.emacs.d/elisp/emms/lisp/emms-info-ogginfo.el
new file mode 100644 (file)
index 0000000..f9054ec
--- /dev/null
@@ -0,0 +1,85 @@
+;;; emms-info-ogginfo.el --- Emms information from Ogg Vorbis files.
+
+;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+
+;; Author: Jorgen Schaefer <forcer@forcix.cx>
+;;         Yoni Rabkin <yonirabkin@member.fsf.org>
+
+;; This file is part of EMMS.
+
+;; EMMS is free software; you can redistribute it and/or modify it
+;; under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3 of the License, or
+;; (at your option) any later version.
+
+;; EMMS is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with EMMS; if not, write to the Free Software Foundation,
+;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+;;
+
+;;; Code:
+
+(require 'emms-info)
+
+(defgroup emms-info-ogginfo nil
+  "An EMMS-info method for getting, using the external ogginfo
+program"
+  :group 'emms-info)
+
+(defcustom emms-info-ogginfo-coding-system 'utf-8
+  "*Coding system used in the output of ogginfo."
+  :type 'coding-system
+  :group 'emms-info-ogginfo)
+
+(defcustom emms-info-ogginfo-program-name "ogginfo"
+  "*The name/path of the ogginfo tag program."
+  :type 'string
+  :group 'emms-info-ogginfo)
+
+(defun emms-info-ogginfo (track)
+  "Add track information to TRACK.
+This is a useful element for `emms-info-functions'."
+  (when (and (eq 'file (emms-track-type track))
+             (string-match "\\.[Oo][Gg][Gg]\\'" (emms-track-name track)))
+
+    (with-temp-buffer
+      (call-process emms-info-ogginfo-program-name
+                    nil t nil (emms-track-name track))
+
+      ;; play time, emms-info-ogg.el [U. Jensen]
+      (goto-char (point-min))
+      (when (re-search-forward
+             "Playback length: \\([0-9]*\\)m:\\([0-9]*\\)" nil t)
+        (let* ((minutes (string-to-number (match-string 1)))
+               (seconds (string-to-number (match-string 2)))
+               (ptime-total (+ (* minutes 60) seconds))
+               (ptime-min minutes)
+               (ptime-sec seconds))
+          (emms-track-set track 'info-playing-time ptime-total)
+          (emms-track-set track 'info-playing-time-min ptime-min)
+          (emms-track-set track 'info-playing-time-sec ptime-sec)
+          (emms-track-set track 'info-file (emms-track-name track))))
+
+      ;; all the rest of the info available
+      (goto-char (point-min))
+      (when (re-search-forward "^.*\\.\\.\\.$" (point-max) t)
+        (while (zerop (forward-line 1))
+          (when (looking-at "^\t\\(.*?\\)=\\(.*\\)$") ; recognize the first '='
+            (let ((a (match-string 1))
+                  (b (match-string 2)))
+              (when (and (< 0 (length a))
+                         (< 0 (length b)))
+                (emms-track-set track
+                                (intern (downcase (concat "info-" (match-string 1))))
+                                (match-string 2))))))))))
+
+(provide 'emms-info-ogginfo)
+
+;;; emms-info-ogginfo.el ends here
diff --git a/.emacs.d/elisp/emms/lisp/emms-info.el b/.emacs.d/elisp/emms/lisp/emms-info.el
new file mode 100644 (file)
index 0000000..cfc206b
--- /dev/null
@@ -0,0 +1,140 @@
+;;; emms-info.el --- Retrieving track information
+
+;; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation Inc.
+
+;; Author: Jorgen Schaefer <forcer@forcix.cx>
+
+;; This file is part of EMMS.
+
+;; EMMS is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License
+;; as published by the Free Software Foundation; either version 3
+;; of the License, or (at your option) any later version.
+
+;; EMMS is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with EMMS; if not, write to the Free Software
+;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+;; 02110-1301, USA.
+
+;;; Commentary:
+
+;; This EMMS module provides a way to add information for a track.
+;; This can use an ID3 or OGG comment like syntax.
+
+;; The code will add info symbols to the track. The following symbols
+;; are defined:
+
+;; info-artist - string naming the artist
+;; info-composer - string naming the composer
+;; info-performer - string naming the performer
+;; info-title - string naming the title of the song
+;; info-album - string naming the album
+;; info-tracknumber - string(?) naming the track number
+;; info-discnumber - string naming the disc number
+;; info-year - string naming the year
+;; info-note - string of free-form entry
+;; info-genre - string naming the genre
+;; info-playing-time - number giving the seconds of playtime
+
+;;; Code:
+
+(require 'emms)
+(require 'later-do)
+
+(defgroup emms-info nil
+  "*Track information. ID3, OGG, etc."
+  :group 'emms)
+
+(defcustom emms-info-auto-update t
+  "*Non-nil when EMMS should update track information if the file changes.
+This will cause hard drive activity on track loading. If this is
+too annoying for you, set this variable to nil."
+  :type 'boolean
+  :group 'emms-info)
+
+(defcustom emms-info-asynchronously t
+  "*Non-nil when track information should be loaded asynchronously.
+This requires `later-do', which should come with EMMS."
+  :type 'boolean
+  :group 'emms-info)
+
+(defcustom emms-info-report-each-num-tracks 200
+  "*Non-zero will report progress information every number of tracks.
+The default is to display a message every 200 tracks.
+This variable is only used when adding tracks asynchronously."
+  :type 'integer
+  :group 'emms-info)
+
+(defcustom emms-info-functions nil
+  "*Functions which add information to tracks.
+Each is called with a track as argument."
+  :type 'hook
+  :group 'emms-info)
+
+(defvar emms-info-asynchronous-tracks 0
+  "Number of tracks we're waiting for to be done.")
+
+(defun emms-info-initialize-track (track)
+  "Initialize TRACK with emms-info information.
+This is a suitable value for `emms-track-initialize-functions'."
+  (if (not emms-info-asynchronously)
+      (emms-info-really-initialize-track track)
+    (setq emms-info-asynchronous-tracks (1+ emms-info-asynchronous-tracks))
+    (later-do 'emms-info-really-initialize-track track)))
+
+(defun emms-info-really-initialize-track (track)
+  "Really initialize TRACK.
+Return t when the track got changed."
+  (let ((file-mtime (when emms-info-auto-update
+                      (emms-info-track-file-mtime track)))
+        (info-mtime (emms-track-get track 'info-mtime))
+        (name (emms-track-get track 'name)))
+
+    ;; if the file's been modified or is new
+    (when (or (not file-mtime)
+              (not info-mtime)
+              (emms-time-less-p info-mtime file-mtime))
+      (run-hook-with-args 'emms-info-functions track)
+      ;; not set by info functions
+      (when file-mtime
+        (emms-track-set track 'info-mtime file-mtime))
+      (emms-track-updated track))
+
+    (when emms-info-asynchronously
+      (setq emms-info-asynchronous-tracks (1- emms-info-asynchronous-tracks))
+      (if (zerop emms-info-asynchronous-tracks)
+          (message "EMMS: All track information loaded.")
+        (unless (zerop emms-info-report-each-num-tracks)
+          (if (zerop
+               (mod emms-info-asynchronous-tracks
+                    emms-info-report-each-num-tracks))
+              (message "EMMS: %d tracks to go.."
+                       emms-info-asynchronous-tracks)))))))
+
+(defun emms-info-track-file-mtime (track)
+  "Return the mtime of the file of TRACK, if any.
+Return nil otherwise."
+  (if (eq (emms-track-type track)
+          'file)
+      (nth 5 (file-attributes (emms-track-name track)))
+    nil))
+
+(defun emms-info-track-description (track)
+  "Return a description of TRACK."
+  (let ((artist (emms-track-get track 'info-artist))
+        (title  (emms-track-get track 'info-title)))
+    (cond
+     ((and artist title)
+      (concat artist " - " title))
+     (title
+      title)
+     (t
+      (emms-track-simple-description track)))))
+
+(provide 'emms-info)
+;;; emms-info.el ends here
diff --git a/.emacs.d/elisp/emms/lisp/emms-last-played.el b/.emacs.d/elisp/emms/lisp/emms-last-played.el
new file mode 100644 (file)
index 0000000..529869a
--- /dev/null
@@ -0,0 +1,123 @@
+;;; emms-last-played.el --- Support for last-played-time of a track
+
+;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+
+;; Author: Lucas Bonnet <lucas@rincevent.net>
+;; Keywords: emms, mp3, mpeg, multimedia
+
+;; This file is part of EMMS.
+
+;; EMMS is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; EMMS is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with EMMS; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; Records when the track was last played.
+;; Big portions of the time handling fuctions are copied from
+;; gnus-util.el, and slightly adapted.
+
+;;; Code:
+
+(require 'emms)
+
+(defvar emms-last-played-keep-count t
+  "Specifies if EMMS should record the number of times you play a track.
+Set it to t if you want such a feature, and to nil if you don't.")
+
+(defvar emms-last-played-format-alist
+  '(((emms-last-played-seconds-today) . "%k:%M")
+    (604800 . "%a %k:%M")                   ;;that's one week
+    ((emms-last-played-seconds-month) . "%a %d")
+    ((emms-last-played-seconds-year) . "%b %d")
+    (t . "%b %d '%y"))                      ;;this one is used when no
+                                           ;;other does match
+  "Specifies date format depending on when a track was last played.
+This is an alist of items (AGE . FORMAT).  AGE can be a number (of
+seconds) or a Lisp expression evaluating to a number.  When the age of
+the track is less than this number, then use `format-time-string'
+with the corresponding FORMAT for displaying the date of the track.
+If AGE is not a number or a Lisp expression evaluating to a
+non-number, then the corresponding FORMAT is used as a default value.
+
+Note that the list is processed from the beginning, so it should be
+sorted by ascending AGE.  Also note that items following the first
+non-number AGE will be ignored.
+
+You can use the functions `emms-last-played-seconds-today',
+`emms-last-played-seconds-month' and
+`emms-last-played-seconds-year' in the AGE spec.  They return the
+number of seconds passed since the start of today, of this month,
+of this year, respectively.")
+
+
+(defun emms-last-played-update-track (track)
+  "Updates the last-played time of TRACK."
+  (emms-track-set track 'last-played (current-time)))
+
+(defun emms-last-played-increment-count (track)
+  "Increments the play-count property of TRACK.
+If non-existent, it is set to 1."
+  (let ((play-count (emms-track-get track 'play-count)))
+    (if play-count
+        (emms-track-set track 'play-count (1+ play-count))
+      (emms-track-set track 'play-count 1))))
+
+(defun emms-last-played-update-current ()
+  "Updates the current track."
+  (emms-last-played-update-track (emms-playlist-current-selected-track))
+  (if emms-last-played-keep-count
+      (emms-last-played-increment-count (emms-playlist-current-selected-track))))
+
+(defun emms-last-played-seconds-today ()
+  "Return the number of seconds passed today."
+  (let ((now (decode-time (current-time))))
+    (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600))))
+
+(defun emms-last-played-seconds-month ()
+  "Return the number of seconds passed this month."
+  (let ((now (decode-time (current-time))))
+    (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600)
+       (* (- (car (nthcdr 3 now)) 1) 3600 24))))
+
+(defun emms-last-played-seconds-year ()
+  "Return the number of seconds passed this year."
+  (let ((now (decode-time (current-time)))
+       (days (format-time-string "%j" (current-time))))
+    (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600)
+       (* (- (string-to-number days) 1) 3600 24))))
+
+(defun emms-last-played-format-date (messy-date)
+  "Format the messy-date according to emms-last-played-format-alist.
+Returns \"  ?  \" if there's bad input or if an other error occurs.
+Input should look like this: \"Sun, 14 Oct 2001 13:34:39 +0200\"."
+  (condition-case ()
+      (let* ((messy-date (float-time messy-date))
+             (now (float-time (current-time)))
+            ;;If we don't find something suitable we'll use this one
+            (my-format "%b %d '%y"))
+       (let* ((difference (- now messy-date))
+              (templist emms-last-played-format-alist)
+              (top (eval (caar templist))))
+         (while (if (numberp top) (< top difference) (not top))
+           (progn
+             (setq templist (cdr templist))
+             (setq top (eval (caar templist)))))
+         (if (stringp (cdr (car templist)))
+             (setq my-format (cdr (car templist)))))
+       (format-time-string (eval my-format) (seconds-to-time messy-date)))
+    (error "Never.")))
+
+(provide 'emms-last-played)
+;;; emms-last-played.el ends here
diff --git a/.emacs.d/elisp/emms/lisp/emms-lastfm-client.el b/.emacs.d/elisp/emms/lisp/emms-lastfm-client.el
new file mode 100644 (file)
index 0000000..2a8e0fa
--- /dev/null
@@ -0,0 +1,1099 @@
+;;; emms-lastfm-client.el --- Last.FM Music API
+
+;; Copyright (C) 2009, 2010, 2011  Free Software Foundation, Inc.
+
+;; Author: Yoni Rabkin <yonirabkin@member.fsf.org>
+
+;; Keywords: emms, lastfm
+
+;; EMMS is free software; you can redistribute it and/or modify it
+;; under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+;;
+;; EMMS is distributed in the hope that it will be useful, but WITHOUT
+;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+;; License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with EMMS; see the file COPYING.  If not, write to the Free
+;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+;; MA 02110-1301, USA.
+
+;;; Commentary:
+;;
+;; Definitive information on how to setup and use this package is
+;; provided in the wonderful Emms manual, in the /doc directory of the
+;; Emms distribution.
+
+;;; Code:
+
+(require 'md5)
+(require 'parse-time)
+(require 'emms)
+(require 'emms-source-file)
+(require 'xml)
+
+(defcustom emms-lastfm-client-username nil
+  "Valid Last.fm account username."
+  :group 'emms-lastfm
+  :type 'string)
+
+(defcustom emms-lastfm-client-api-key nil
+  "Key for the Last.fm API."
+  :group 'emms-lastfm
+  :type 'string)
+
+(defcustom emms-lastfm-client-api-secret-key nil
+  "Secret key for the Last.fm API."
+  :group 'emms-lastfm
+  :type 'string)
+
+(defvar emms-lastfm-client-api-session-key nil
+  "Session key for the Last.fm API.")
+
+(defvar emms-lastfm-client-track nil
+  "Latest Last.fm track.")
+
+(defvar emms-lastfm-client-submission-api t
+  "Use the Last.fm submission API if true, otherwise don't.")
+
+(defvar emms-lastfm-client-token nil
+  "Authorization token for API.")
+
+(defvar emms-lastfm-client-api-base-url
+  "http://ws.audioscrobbler.com/2.0/"
+  "URL for API calls.")
+
+(defvar emms-lastfm-client-session-key-file
+  (concat (file-name-as-directory emms-directory)
+         "emms-lastfm-client-sessionkey")
+  "File for storing the Last.fm API session key.")
+
+(defvar emms-lastfm-client-cache-directory
+  (file-name-as-directory
+   (concat (file-name-as-directory emms-directory)
+          "emms-lastfm-client-cache"))
+  "File for storing Last.fm cache data.")
+
+(defvar emms-lastfm-client-playlist-valid nil
+  "True if the playlist hasn't expired.")
+
+(defvar emms-lastfm-client-playlist-timer nil
+  "Playlist timer object.")
+
+(defvar emms-lastfm-client-playlist nil
+  "Latest Last.fm playlist.")
+
+(defvar emms-lastfm-client-track nil
+  "Latest Last.fm track.")
+
+(defvar emms-lastfm-client-original-next-function nil
+  "Original `-next-function'.")
+
+(defvar emms-lastfm-client-playlist-buffer-name
+  "*Emms Last.fm*"
+  "Name for non-interactive Emms Last.fm buffer.")
+
+(defvar emms-lastfm-client-playlist-buffer nil
+  "Non-interactive Emms Last.fm buffer.")
+
+(defvar emms-lastfm-client-inhibit-cleanup nil
+  "If true, do not perform clean-up after `emms-stop'.")
+
+(defvar emms-lastfm-client-image-size "mega"
+  "Default size for artist information images.")
+
+(defvar emms-lastfm-client-artist-info-buffer-name
+  "*Emms Last.fm Artist Info*"
+  "Name for displaying artist information.")
+
+(defvar emms-lastfm-client-api-method-dict
+  '((auth-get-token    . ("auth.gettoken"
+                         emms-lastfm-client-auth-get-token-ok
+                         emms-lastfm-client-auth-get-token-failed))
+    (auth-get-session  . ("auth.getsession"
+                         emms-lastfm-client-auth-get-session-ok
+                         emms-lastfm-client-auth-get-session-failed))
+    (radio-tune        . ("radio.tune"
+                         emms-lastfm-client-radio-tune-ok
+                         emms-lastfm-client-radio-tune-failed))
+    (radio-getplaylist . ("radio.getplaylist"
+                         emms-lastfm-client-radio-getplaylist-ok
+                         emms-lastfm-client-radio-getplaylist-failed))
+    (track-love        . ("track.love"
+                         emms-lastfm-client-track-love-ok
+                         emms-lastfm-client-track-love-failed))
+    (track-ban         . ("track.ban"
+                         emms-lastfm-client-track-ban-ok
+                         emms-lastfm-client-track-ban-failed))
+    (artist-getinfo    . ("artist.getinfo"
+                         emms-lastfm-client-artist-getinfo-ok
+                         emms-lastfm-client-artist-getinfo-failed)))
+  "Mapping symbols to method calls. This is a list of cons pairs
+  where the CAR is the symbol name of the method and the CDR is a
+  list whose CAR is the method call string, CADR is the function
+  to call on a success and CADDR is the function to call on
+  failure.")
+
+;;; ------------------------------------------------------------------
+;;; API method call
+;;; ------------------------------------------------------------------
+
+(defun emms-lastfm-client-get-method (method)
+  "Return the associated method cons for the symbol METHOD."
+  (let ((m (cdr (assoc method emms-lastfm-client-api-method-dict))))
+    (if (not m)
+       (error "method not in dictionary: %s" method)
+      m)))
+
+(defun emms-lastfm-client-get-method-name (method)
+  "Return the associated method string for the symbol METHOD."
+  (let ((this (nth 0 (emms-lastfm-client-get-method method))))
+    (if (not this)
+       (error "no name string registered for method: %s" method)
+      this)))
+
+(defun emms-lastfm-client-get-method-ok (method)
+  "Return the associated OK function for METHOD.
+
+This function is called when the method call returns
+successfully."
+  (let ((this (nth 1 (emms-lastfm-client-get-method method))))
+    (if (not this)
+       (error "no OK function registered for method: %s" method)
+      this)))
+
+(defun emms-lastfm-client-get-method-fail (method)
+  "Return the associated fail function for METHOD.
+
+This function is called when the method call returns a failure
+status message."
+  (let ((this (nth 2 (emms-lastfm-client-get-method method))))
+    (if (not this)
+       (error "no fail function registered for method: %s" method)
+      this)))
+
+(defun emms-lastfm-client-encode-arguments (arguments)
+  "Encode ARGUMENTS in UTF-8 for the Last.fm API."
+  (let ((result nil))
+    (while arguments
+      (setq result
+           (append result
+                   (list
+                    (cons
+                     (encode-coding-string (caar arguments) 'utf-8)
+                     (encode-coding-string (cdar arguments) 'utf-8)))))
+      (setq arguments (cdr arguments)))
+    result))
+
+(defun emms-lastfm-client-construct-arguments (str arguments)
+  "Return a concatenation of arguments for the URL."
+  (cond ((not arguments) str)
+       (t (emms-lastfm-client-construct-arguments
+           (concat str "&" (caar arguments) "=" (url-hexify-string (cdar arguments)))
+           (cdr arguments)))))
+
+(defun emms-lastfm-client-construct-method-call (method arguments)
+  "Return a complete URL method call for METHOD with ARGUMENTS.
+
+This function includes the cryptographic signature."
+  (concat emms-lastfm-client-api-base-url "?"
+         "method=" (emms-lastfm-client-get-method-name method)
+         (emms-lastfm-client-construct-arguments
+          "" arguments)
+         "&api_sig="
+         (emms-lastfm-client-construct-signature method arguments)))
+
+(defun emms-lastfm-client-construct-write-method-call (method arguments)
+  "Return a complete POST body method call for METHOD with ARGUMENTS.
+
+This function includes the cryptographic signature."
+  (concat "method=" (emms-lastfm-client-get-method-name method)
+         (emms-lastfm-client-construct-arguments
+          "" arguments)
+         "&api_sig="
+         (emms-lastfm-client-construct-signature method arguments)))
+
+;;; ------------------------------------------------------------------
+;;; Response handler
+;;; ------------------------------------------------------------------
+
+(defun emms-lastfm-client-handle-response (method xml-response)
+  "Dispatch the handler functions of METHOD for XML-RESPONSE."
+  (let ((status (cdr (assoc 'status (nth 1 (car xml-response)))))
+       (data (cdr (cdr (car xml-response)))))
+    (when (not status)
+      (error "error parsing status from: %s" xml-response))
+    (cond ((string= status "failed")
+          (funcall (emms-lastfm-client-get-method-fail method) data))
+         ((string= status "ok")
+          (funcall (emms-lastfm-client-get-method-ok method) data))
+         (t (error "unknown response status %s" status)))))
+
+;;; ------------------------------------------------------------------
+;;; Unathorized request token for an API account
+;;; ------------------------------------------------------------------
+
+(defun emms-lastfm-client-construct-urt ()
+  "Return a request for an Unauthorized Request Token."
+  (let ((arguments
+        (emms-lastfm-client-encode-arguments
+         `(("api_key" . ,emms-lastfm-client-api-key)))))
+    (emms-lastfm-client-construct-method-call
+     'auth-get-token arguments)))
+
+(defun emms-lastfm-client-make-call-urt ()
+  "Make method call for Unauthorized Request Token."
+  (let* ((url-request-method "POST"))
+    (let ((response
+          (url-retrieve-synchronously
+           (emms-lastfm-client-construct-urt))))
+      (emms-lastfm-client-handle-response
+       'auth-get-token
+       (with-current-buffer response
+        (xml-parse-region (point-min) (point-max)))))))
+
+;; example response: ((lfm ((status . \"ok\")) \"\" (token nil
+;; \"31cab3398a9b46cf7231ef84d73169cf\")))
+
+;;; ------------------------------------------------------------------
+;;; Signatures
+;;; ------------------------------------------------------------------
+;;
+;; From [http://www.last.fm/api/desktopauth]:
+;;
+;; Construct your api method signatures by first ordering all the
+;; parameters sent in your call alphabetically by parameter name and
+;; concatenating them into one string using a <name><value>
+;; scheme. So for a call to auth.getSession you may have:
+;;
+;;   api_keyxxxxxxxxmethodauth.getSessiontokenxxxxxxx
+;;
+;; Ensure your parameters are utf8 encoded. Now append your secret
+;; to this string. Finally, generate an md5 hash of the resulting
+;; string. For example, for an account with a secret equal to
+;; 'mysecret', your api signature will be:
+;;
+;;   api signature = md5("api_keyxxxxxxxxmethodauth.getSessiontokenxxxxxxxmysecret")
+;;
+;; Where md5() is an md5 hashing operation and its argument is the
+;; string to be hashed. The hashing operation should return a
+;; 32-character hexadecimal md5 hash.
+
+(defun emms-lastfm-client-construct-lexi (arguments)
+  "Return ARGUMENTS sorted in lexicographic order."
+  (let ((lexi (sort arguments
+                   '(lambda (a b) (string< (car a) (car b)))))
+       (out ""))
+    (while lexi
+      (setq out (concat out (caar lexi) (cdar lexi)))
+      (setq lexi (cdr lexi)))
+    out))
+
+(defun emms-lastfm-client-construct-signature (method arguments)
+  "Return request signature for METHOD and ARGUMENTS."
+  (let ((complete-arguments
+        (append arguments
+                `(("method" .
+                   ,(emms-lastfm-client-get-method-name method))))))
+    (md5
+     (concat (emms-lastfm-client-construct-lexi complete-arguments)
+            emms-lastfm-client-api-secret-key))))
+
+;;; ------------------------------------------------------------------
+;;; General error handling
+;;; ------------------------------------------------------------------
+
+;; Each method call provides its own error codes, but if we don't want
+;; to code a handler for a method we call this instead:
+(defun emms-lastfm-client-default-error-handler (data)
+  "Default method failure handler."
+  (let ((errorcode (cdr (assoc 'code (nth 1 (cadr data)))))
+       (message (nth 2 (cadr data))))
+    (when (not (and errorcode message))
+      (error "failed to read errorcode or message: %s %s"
+            errorcode message))
+    (error "method call failed with code %s: %s"
+          errorcode message)))
+
+;;; ------------------------------------------------------------------
+;;; Request authorization from the user
+;;; ------------------------------------------------------------------
+
+(defun emms-lastfm-client-ask-for-auth ()
+  "Open a Web browser for authorizing the application."
+  (when (not (and emms-lastfm-client-api-key
+                 emms-lastfm-client-token))
+    (error "API key and authorization token needed."))
+  (browse-url
+   (format "http://www.last.fm/api/auth/?api_key=%s&token=%s"
+          emms-lastfm-client-api-key
+          emms-lastfm-client-token)))
+
+;;; ------------------------------------------------------------------
+;;; Parse XSPF
+;;; ------------------------------------------------------------------
+
+(defun emms-lastfm-client-xspf-header (data)
+  "Return an alist representing the XSPF header of DATA."
+  (let (out
+       (orig data))
+    (setq data (cadr data))
+    (while data
+      (when (and (car data)
+                (listp (car data))
+                (= (length (car data)) 3))
+       (setq out (append out (list (cons (nth 0 (car data))
+                                         (nth 2 (car data)))))))
+      (setq data (cdr data)))
+    (if (not out)
+       (error "failed to parse XSPF header from: %s" orig)
+      out)))
+
+(defun emms-lastfm-client-xspf-tracklist (data)
+  "Return the start of the track-list in DATE."
+  (nthcdr 3 (nth 11 (cadr data))))
+
+(defun emms-lastfm-client-xspf-header-date (header-alist)
+  "Return the date parameter from HEADER-ALIST."
+  (let ((out (cdr (assoc 'date header-alist))))
+    (if (not out)
+       (error "could not read date from header alist: %s"
+              header-alist)
+      out)))
+
+(defun emms-lastfm-client-xspf-header-expiry (header-alist)
+  "Return the expiry parameter from HEADER-ALIST."
+  (let ((out (cdr (assoc 'link header-alist))))
+    (if (not out)
+       (error "could not read expiry from header alist: %s"
+              header-alist)
+      out)))
+
+(defun emms-lastfm-client-xspf-header-creator (header-alist)
+  "Return the creator parameter from HEADER-ALIST."
+  (let ((out (cdr (assoc 'creator header-alist))))
+    (if (not out)
+       (error "could not read creator from header alist: %s"
+              header-alist)
+      out)))
+
+(defun emms-lastfm-client-xspf-playlist (data)
+  "Return the playlist from the XSPF DATA."
+  (let ((playlist (car (nthcdr 11 data))))
+    (if (not playlist)
+       (error "could not read playlist from: %s" data)
+      playlist)))
+
+;; note: the result of this function can be used with
+;; `emms-lastfm-client-xspf-get' as well
+(defun emms-lastfm-client-xspf-extension (track)
+  "Return the Extension portion of TRACK."
+  (let ((this (copy-sequence track))
+       (cont t))
+    (while (and cont this)
+      (when (consp this)
+       (let ((head (car this)))
+         (when (consp head)
+           (when (equal 'extension (car head))
+             (setq cont nil)))))
+      (when cont
+       (setq this (cdr this))))
+    (if this
+       (car this)
+      (error "could not find track extension data"))))
+
+(defun emms-lastfm-client-xspf-get (node track)
+  "Return data associated with NODE in TRACK."
+  (let ((result nil))
+    (while track
+      (when (consp track)
+       (let ((this (car track)))
+         (when (and (consp this)
+                    (= (length this) 3)
+                    (symbolp (nth 0 this))
+                    (stringp (nth 2 this))
+                    (equal (nth 0 this) node))
+           (setq result (nth 2 this)))))
+      (setq track (cdr track)))
+    (if (not result)
+       nil
+      result)))
+
+;;; ------------------------------------------------------------------
+;;; Timers
+;;; ------------------------------------------------------------------
+
+;; timed playlist invalidation is a part of the Last.fm API
+(defun emms-lastfm-client-set-timer (header)
+  "Start timer countdown to playlist invalidation"
+  (when (not header)
+    (error "can't set timer with no header data"))
+  (let ((expiry (parse-integer
+                (emms-lastfm-client-xspf-header-expiry header))))
+    (setq emms-lastfm-client-playlist-valid t)
+    (when emms-lastfm-client-playlist-timer
+      (cancel-timer emms-lastfm-client-playlist-timer))
+    (setq emms-lastfm-client-playlist-timer
+         (run-at-time
+          expiry nil
+          '(lambda ()
+             (cancel-timer emms-lastfm-client-playlist-timer)
+             (setq emms-lastfm-client-playlist-valid nil))))))
+
+;;; ------------------------------------------------------------------
+;;; Player
+;;; ------------------------------------------------------------------
+
+;; this should return `nil' to the track-manager when the playlist has
+;; been exhausted
+(defun emms-lastfm-client-consume-next-track ()
+  "Pop and return the next track from the playlist or nil."
+  (when emms-lastfm-client-playlist
+    (if emms-lastfm-client-playlist-valid
+       (let ((track (car emms-lastfm-client-playlist)))
+         ;; we can only request each track once so we pop it off the
+         ;; playlist
+         (setq emms-lastfm-client-playlist
+               (if (stringp (cdr emms-lastfm-client-playlist))
+                   (cddr emms-lastfm-client-playlist)
+                 (cdr emms-lastfm-client-playlist)))
+         track)
+      (error "playlist invalid"))))
+
+(defun emms-lastfm-client-set-lastfm-playlist-buffer ()
+  "Set `emms-playlist-buffer' to a be an Emms lastfm buffer."
+  (when (buffer-live-p emms-lastfm-client-playlist-buffer)
+    (kill-buffer emms-lastfm-client-playlist-buffer))
+  (setq emms-lastfm-client-playlist-buffer
+       (emms-playlist-new
+        emms-lastfm-client-playlist-buffer-name))
+  (setq emms-playlist-buffer emms-lastfm-client-playlist-buffer))
+
+
+(defun emms-lastfm-client-load-next-track ()
+  "Queue the next track from Last.fm."
+  (with-current-buffer emms-lastfm-client-playlist-buffer
+    (let ((inhibit-read-only t))
+      (widen)
+      (delete-region (point-min)
+                    (point-max)))
+    (if emms-lastfm-client-playlist
+       (let ((track (emms-lastfm-client-consume-next-track)))
+         (setq emms-lastfm-client-track track)
+         (setq emms-lastfm-scrobbler-track-play-start-timestamp
+               (emms-lastfm-scrobbler-timestamp))
+         (let ((emms-lastfm-client-inhibit-cleanup t))
+           (emms-play-url
+            (emms-lastfm-client-xspf-get 'location track))))
+      (emms-lastfm-client-make-call-radio-getplaylist)
+      (emms-lastfm-client-load-next-track))))
+
+(defun emms-lastfm-client-love-track ()
+  "Submit the currently playing track with a `love' rating."
+  (interactive)
+  (when emms-lastfm-client-track
+    (emms-lastfm-scrobbler-make-async-submission-call
+     (emms-lastfm-client-convert-track
+      emms-lastfm-client-track) 'love)
+    ;; the following submission API call looks redundant but
+    ;; isn't; indeed, it might be done away with in a future
+    ;; version of the Last.fm API (see API docs)
+    (emms-lastfm-client-make-call-track-love)))
+
+(defun emms-lastfm-client-ban-track ()
+  "Submit currently playing track with a `ban' rating and skip."
+  (interactive)
+  (when emms-lastfm-client-track
+    (emms-lastfm-scrobbler-make-async-submission-call
+     (emms-lastfm-client-convert-track
+      emms-lastfm-client-track) 'ban)
+    ;; the following submission API call looks redundant but
+    ;; isn't; see `...-love-track'
+    (emms-lastfm-client-make-call-track-ban)
+    (emms-lastfm-client-load-next-track)))
+
+;; call this `-track-advance' to avoid confusion with Emms'
+;; `-next-track-' mechanism
+(defun emms-lastfm-client-track-advance (&optional first)
+  "Move to the next track in the playlist."
+  (interactive)
+  (when (equal emms-playlist-buffer
+              emms-lastfm-client-playlist-buffer)
+    (when (and emms-lastfm-client-submission-api
+              (not first))
+      (let ((result (emms-lastfm-scrobbler-make-async-submission-call
+                    (emms-lastfm-client-convert-track
+                     emms-lastfm-client-track) nil)))))
+    (emms-lastfm-client-load-next-track)))
+
+(defun emms-lastfm-client-next-function ()
+  "Replacement function for `emms-next-noerror'."
+  (if (equal emms-playlist-buffer
+            emms-lastfm-client-playlist-buffer)
+      (emms-lastfm-client-track-advance)
+    (funcall emms-lastfm-client-original-next-function)))
+
+(defun emms-lastfm-client-clean-after-stop ()
+  "Kill the emms-lastfm buffer."
+  (when (and (equal emms-playlist-buffer
+                   emms-lastfm-client-playlist-buffer)
+            (not emms-lastfm-client-inhibit-cleanup))
+    (kill-buffer emms-lastfm-client-playlist-buffer)
+    (setq emms-lastfm-client-playlist-buffer nil)))
+
+(defun emms-lastfm-client-play-playlist ()
+  "Entry point to play tracks from Last.fm."
+  (emms-lastfm-client-set-lastfm-playlist-buffer)
+  (when (not (equal emms-player-next-function
+                   'emms-lastfm-client-next-function))
+    (add-to-list 'emms-player-stopped-hook
+                'emms-lastfm-client-clean-after-stop)
+    (setq emms-lastfm-client-original-next-function
+         emms-player-next-function)
+    (setq emms-player-next-function
+         'emms-lastfm-client-next-function))
+  (emms-lastfm-client-track-advance t))
+
+;; stolen from Tassilo Horn's original emms-lastfm.el
+(defun emms-lastfm-client-read-artist ()
+  "Read an artist name from the user."
+  (let ((artists nil))
+    (when (boundp 'emms-cache-db)
+      (maphash
+       #'(lambda (file track)
+          (let ((artist (emms-track-get track 'info-artist)))
+            (when artist
+              (add-to-list 'artists artist))))
+       emms-cache-db))
+    (if artists
+       (emms-completing-read "Artist: " artists)
+      (read-string "Artist: "))))
+
+(defun emms-lastfm-client-initialize-session ()
+  "Run per-session functions."
+  (emms-lastfm-client-check-session-key))
+
+(defun emms-lastfm-client-info ()
+  "Display information about the latest track."
+  (interactive)
+  (emms-lastfm-client-make-call-artist-getinfo))
+
+;;; ------------------------------------------------------------------
+;;; Stations
+;;; ------------------------------------------------------------------
+
+(defun emms-lastfm-client-play-user-station (username url)
+  "Play URL for USERNAME."
+  (when (not (and username url))
+    (error "username and url must be set"))
+  (emms-lastfm-client-initialize-session)
+  (emms-lastfm-client-make-call-radio-tune
+   (format url username))
+  (emms-lastfm-client-make-call-radio-getplaylist)
+  (emms-lastfm-scrobbler-handshake)
+  (emms-lastfm-client-play-playlist))
+
+(defun emms-lastfm-client-play-similar-artists (artist)
+  "Play a Last.fm station with music similar to ARTIST."
+  (interactive (list (emms-lastfm-client-read-artist)))
+  (when (not (stringp artist))
+    (error "not a string: %s" artist))
+  (emms-lastfm-client-initialize-session)
+  (emms-lastfm-client-make-call-radio-tune
+   (format "lastfm://artist/%s/similarartists" artist))
+  (emms-lastfm-client-make-call-radio-getplaylist)
+  (emms-lastfm-scrobbler-handshake)
+  (emms-lastfm-client-play-playlist))
+
+(defun emms-lastfm-client-play-recommended ()
+  "Play a Last.fm station with \"recommended\" tracks."
+  (interactive)
+  (emms-lastfm-client-play-user-station
+   emms-lastfm-client-username
+   "lastfm://user/%s/recommended"))
+
+(defun emms-lastfm-client-play-loved ()
+  "Play a Last.fm station with \"loved\" tracks."
+  (interactive)
+  (emms-lastfm-client-play-user-station
+   emms-lastfm-client-username
+   "lastfm://user/%s/loved"))
+
+(defun emms-lastfm-client-play-mix ()
+  "Play the \"Mix\" station the current user."
+  (interactive)
+  (emms-lastfm-client-play-user-station
+   emms-lastfm-client-username
+   "lastfm://user/%s/mix"))
+
+(defun emms-lastfm-client-play-neighborhood ()
+  "Play a Last.fm station with \"neighborhood\" tracks."
+  (interactive)
+  (emms-lastfm-client-play-user-station
+   emms-lastfm-client-username
+   "lastfm://user/%s/neighbours"))
+
+(defun emms-lastfm-client-play-library ()
+  "Play a Last.fm station with \"library\" tracks."
+  (interactive)
+  (emms-lastfm-client-play-user-station
+   emms-lastfm-client-username
+   "lastfm://user/%s/personal"))
+
+(defun emms-lastfm-client-play-user-loved (user)
+  (interactive "sLast.fm username: ")
+  (emms-lastfm-client-play-user-station
+   user
+   "lastfm://user/%s/loved"))
+
+(defun emms-lastfm-client-play-user-neighborhood (user)
+  (interactive "sLast.fm username: ")
+  (emms-lastfm-client-play-user-station
+   user
+   "lastfm://user/%s/neighbours"))
+
+(defun emms-lastfm-client-play-user-library (user)
+  (interactive "sLast.fm username: ")
+  (emms-lastfm-client-play-user-station
+   user
+   "lastfm://user/%s/personal"))
+
+;;; ------------------------------------------------------------------
+;;; Information
+;;; ------------------------------------------------------------------
+
+(defun emms-lastfm-client-convert-track (track)
+  "Convert a Last.fm track to an Emms track."
+  (let ((emms-track (emms-dictionary '*track*)))
+    (emms-track-set emms-track 'name
+                   (emms-lastfm-client-xspf-get 'location track))
+    (emms-track-set emms-track 'info-artist
+                   (emms-lastfm-client-xspf-get 'creator track))
+    (emms-track-set emms-track 'info-title
+                   (emms-lastfm-client-xspf-get 'title track))
+    (emms-track-set emms-track 'info-album
+                   (emms-lastfm-client-xspf-get 'album track))
+    (emms-track-set emms-track 'info-playing-time
+                   (/ (parse-integer
+                       (emms-lastfm-client-xspf-get 'duration
+                                                    track))
+                      1000))
+    (emms-track-set emms-track 'type 'lastfm-streaming)
+    emms-track))
+
+(defun emms-lastfm-client-show-track (track)
+  "Return description of TRACK."
+  (decode-coding-string
+   (format emms-show-format
+          (emms-track-description
+           (emms-lastfm-client-convert-track track)))
+   'utf-8))
+
+(defun emms-lastfm-client-show ()
+  "Display a description of the current track."
+  (interactive)
+  (if emms-player-playing-p
+      (message
+       (emms-lastfm-client-show-track emms-lastfm-client-track))
+    nil))
+
+;;; ------------------------------------------------------------------
+;;; Desktop application authorization [http://www.last.fm/api/desktopauth]
+;;; ------------------------------------------------------------------
+
+(defun emms-lastfm-client-user-authorization ()
+  "Ask user to authorize the application."
+  (interactive)
+  (emms-lastfm-client-make-call-urt)
+  (emms-lastfm-client-ask-for-auth))
+
+(defun emms-lastfm-client-get-session ()
+  "Retrieve and store session key."
+  (interactive)
+  (emms-lastfm-client-make-call-get-session)
+  (emms-lastfm-client-save-session-key
+   emms-lastfm-client-api-session-key))
+
+;;; ------------------------------------------------------------------
+;;; method: auth.getToken [http://www.last.fm/api/show?service=265]
+;;; ------------------------------------------------------------------
+
+(defun emms-lastfm-client-auth-get-token-ok (data)
+  "Function called when auth.getToken succeeds."
+  (setq emms-lastfm-client-token
+       (nth 2 (cadr data)))
+  (if (or (not emms-lastfm-client-token)
+         (not (= (length emms-lastfm-client-token) 32)))
+      (error "could not read token from response %s" data)
+    (message "Emms Last.FM auth.getToken method call success.")))
+
+(defun emms-lastfm-client-auth-get-token-failed (data)
+  "Function called when auth.getToken fails."
+  (emms-lastfm-client-default-error-handler data))
+
+;;; ------------------------------------------------------------------
+;;; method: auth.getSession [http://www.last.fm/api/show?service=125]
+;;; ------------------------------------------------------------------
+
+(defun emms-lastfm-client-construct-get-session ()
+  "Return an auth.getSession request string."
+  (let ((arguments
+        (emms-lastfm-client-encode-arguments
+         `(("token"   . ,emms-lastfm-client-token)
+           ("api_key" . ,emms-lastfm-client-api-key)))))
+    (emms-lastfm-client-construct-method-call
+     'auth-get-session arguments)))
+
+(defun emms-lastfm-client-make-call-get-session ()
+  "Make auth.getSession call."
+  (let* ((url-request-method "POST"))
+    (let ((response
+          (url-retrieve-synchronously
+           (emms-lastfm-client-construct-get-session))))
+      (emms-lastfm-client-handle-response
+       'auth-get-session
+       (with-current-buffer response
+        (xml-parse-region (point-min) (point-max)))))))
+
+(defun emms-lastfm-client-save-session-key (key)
+  "Store KEY."
+  (let ((buffer (find-file-noselect
+                emms-lastfm-client-session-key-file)))
+    (set-buffer buffer)
+    (erase-buffer)
+    (insert key)
+    (save-buffer)
+    (kill-buffer buffer)))
+
+(defun emms-lastfm-client-load-session-key ()
+  "Return stored session key."
+  (let ((file (expand-file-name emms-lastfm-client-session-key-file)))
+    (setq emms-lastfm-client-api-session-key
+         (if (file-readable-p file)
+             (with-temp-buffer
+               (emms-insert-file-contents file)
+               (goto-char (point-min))
+               (buffer-substring-no-properties
+                (point) (point-at-eol)))
+           nil))))
+
+(defun emms-lastfm-client-check-session-key ()
+  "Signal an error condition if there is no session key."
+  (if emms-lastfm-client-api-session-key
+      emms-lastfm-client-api-session-key
+    (if (emms-lastfm-client-load-session-key)
+       emms-lastfm-client-api-session-key
+      (error "no session key for API access"))))
+
+(defun emms-lastfm-client-auth-get-session-ok (data)
+  "Function called on DATA if auth.getSession succeeds."
+  (let ((session-key (nth 2 (nth 5 (cadr data)))))
+    (cond (session-key
+          (setq emms-lastfm-client-api-session-key session-key)
+          (message "Emms Last.fm session key retrieval successful"))
+         (t (error "failed to parse session key data %s" data)))))
+
+(defun emms-lastfm-client-auth-get-session-failed (data)
+  "Function called on DATA if auth.getSession fails."
+  (emms-lastfm-client-default-error-handler data))
+
+;;; ------------------------------------------------------------------
+;;; method: radio.tune [http://www.last.fm/api/show?service=160]
+;;; ------------------------------------------------------------------
+
+(defun emms-lastfm-client-construct-radio-tune (station)
+  "Return a request to tune to STATION."
+  (let ((arguments
+        (emms-lastfm-client-encode-arguments
+         `(("sk"   . ,emms-lastfm-client-api-session-key)
+           ("station" . ,station)
+           ("api_key" . ,emms-lastfm-client-api-key)))))
+    (emms-lastfm-client-construct-write-method-call
+     'radio-tune arguments)))
+
+(defun emms-lastfm-client-make-call-radio-tune (station)
+  "Make call to tune to STATION."
+  (let ((url-request-method "POST")
+       (url-request-extra-headers
+        `(("Content-type" . "application/x-www-form-urlencoded")))
+       (url-request-data
+        (emms-lastfm-client-construct-radio-tune station)))
+    (let ((response
+          (url-retrieve-synchronously
+           emms-lastfm-client-api-base-url)))
+      (emms-lastfm-client-handle-response
+       'radio-tune
+       (with-current-buffer response
+        (xml-parse-region (point-min) (point-max)))))))
+
+(defun emms-lastfm-client-radio-tune-failed (data)
+  "Function called on DATA when tuning fails."
+  (emms-lastfm-client-default-error-handler data))
+
+(defun emms-lastfm-client-radio-tune-ok (data)
+  "Set the current radio station according to DATA."
+  (let ((response (cdr (cadr data)))
+       data)
+    (while response
+      (when (and (listp (car response))
+                (car response)
+                (= (length (car response)) 3))
+       (add-to-list 'data (cons (caar response)
+                                (car (cdr (cdr (car response)))))))
+      (setq response (cdr response)))
+    (when (not data)
+      (error "could not parse station information %s" data))
+    data))
+
+;;; ------------------------------------------------------------------
+;;; method: radio.getPlaylist [http://www.last.fm/api/show?service=256]
+;;; ------------------------------------------------------------------
+
+(defun emms-lastfm-client-construct-radio-getplaylist ()
+  "Return a request for a playlist from the tuned station."
+  (let ((arguments
+        (emms-lastfm-client-encode-arguments
+         `(("sk"   . ,emms-lastfm-client-api-session-key)
+           ("api_key" . ,emms-lastfm-client-api-key)))))
+    (emms-lastfm-client-construct-write-method-call
+     'radio-getplaylist arguments)))
+
+(defun emms-lastfm-client-make-call-radio-getplaylist ()
+  "Make call for playlist from the tuned station."
+  (let ((url-request-method "POST")
+       (url-request-extra-headers
+        `(("Content-type" . "application/x-www-form-urlencoded")))
+       (url-request-data
+        (emms-lastfm-client-construct-radio-getplaylist)))
+    (let ((response
+          (url-retrieve-synchronously
+           emms-lastfm-client-api-base-url)))
+      (emms-lastfm-client-handle-response
+       'radio-getplaylist
+       (with-current-buffer response
+        (xml-parse-region (point-min) (point-max)))))))
+
+(defun emms-lastfm-client-radio-getplaylist-failed (data)
+  "Function called on DATA when retrieving a playlist fails."
+  'stub-needs-to-handle-playlist-issues
+  (emms-lastfm-client-default-error-handler data))
+
+(defun emms-lastfm-client-list-filter (l)
+  "Remove strings from the roots of list L."
+  (let (acc)
+    (while l
+      (when (listp (car l))
+       (push (car l) acc))
+      (setq l (cdr l)))
+    (reverse acc)))
+
+(defun emms-lastfm-client-radio-getplaylist-ok (data)
+  "Function called on DATA when retrieving a playlist succeeds."
+  (let ((header (emms-lastfm-client-xspf-header data))
+       (tracklist (emms-lastfm-client-xspf-tracklist data)))
+    (emms-lastfm-client-set-timer header)
+    (setq emms-lastfm-client-playlist
+         (emms-lastfm-client-list-filter tracklist))))
+
+;;; ------------------------------------------------------------------
+;;; method: track.love [http://www.last.fm/api/show?service=260]
+;;; ------------------------------------------------------------------
+
+(defun emms-lastfm-client-construct-track-love ()
+  "Return a request for setting current track rating to `love'."
+  (let ((arguments
+        (emms-lastfm-client-encode-arguments
+         `(("sk"      . ,emms-lastfm-client-api-session-key)
+           ("api_key" . ,emms-lastfm-client-api-key)
+           ("track"   . ,(emms-lastfm-client-xspf-get
+                          'title emms-lastfm-client-track))
+           ("artist"  . ,(emms-lastfm-client-xspf-get
+                          'creator emms-lastfm-client-track))))))
+    (emms-lastfm-client-construct-write-method-call
+     'track-love arguments)))
+
+(defun emms-lastfm-client-make-call-track-love ()
+  "Make call for setting track rating to `love'."
+  (let ((url-request-method "POST")
+       (url-request-extra-headers
+        `(("Content-type" . "application/x-www-form-urlencoded")))
+       (url-request-data
+        (emms-lastfm-client-construct-track-love)))
+    (let ((response
+          (url-retrieve-synchronously
+           emms-lastfm-client-api-base-url)))
+      (emms-lastfm-client-handle-response
+       'track-love
+       (with-current-buffer response
+        (xml-parse-region (point-min) (point-max)))))))
+
+(defun emms-lastfm-client-track-love-failed (data)
+  "Function called with DATA when setting `love' rating fails."
+  'stub-needs-to-handle-track-love-issues
+  (emms-lastfm-client-default-error-handler data))
+
+(defun emms-lastfm-client-track-love-ok (data)
+  "Function called with DATA after `love' rating succeeds."
+  'track-love-succeed)
+
+;;; ------------------------------------------------------------------
+;;; method: artist.getInfo [http://www.last.fm/api/show?service=267]
+;;; ------------------------------------------------------------------
+
+(defun emms-lastfm-client-cache-file (url)
+  "Download a file from URL and return a pathname."
+  (make-directory emms-lastfm-client-cache-directory t)
+  (let ((files (directory-files emms-lastfm-client-cache-directory
+                               t)))
+    (dolist (file files)
+      (when (file-regular-p file)
+       (delete-file file)))
+    (call-process "wget" nil nil nil url "-P"
+                 (expand-file-name
+                  emms-lastfm-client-cache-directory))
+    (car (directory-files emms-lastfm-client-cache-directory
+                         t ".\\(jpg\\|png\\)"))))
+
+(defun emms-lastfm-client-display-artist-getinfo (artist-name
+                                                 lastfm-url
+                                                 artist-image
+                                                 stats-listeners
+                                                 stats-playcount
+                                                 bio-summary
+                                                 bio-complete)
+  "Display a buffer with the artist information."
+  (let ((buf (get-buffer-create
+             emms-lastfm-client-artist-info-buffer-name)))
+    (with-current-buffer buf
+      (let ((inhibit-read-only t))
+       (delete-region (point-min) (point-max))
+       (insert-image
+        (create-image (emms-lastfm-client-cache-file artist-image)))
+       (insert (format "\n\n%s\n\n"
+                       (decode-coding-string artist-name 'utf-8)))
+       (insert (format "Last.fm page: %s\n\n" lastfm-url))
+       (insert (format "Listeners: %s\n" stats-listeners))
+       (insert (format "Plays: %s\n\n" stats-playcount))
+       (let ((p (point)))
+         (insert (format "<p>%s</p>" bio-complete))))
+      (setq buffer-read-only t)
+      (text-mode)
+      (goto-char (point-min)))
+    (switch-to-buffer buf)))
+
+(defun emms-lastfm-client-parse-artist-getinfo (data)
+  "Parse the artist information."
+  (when (or (not data)
+           (not (listp data)))
+    (error "no artist info to parse"))
+  (let ((c (copy-seq (nth 1 data)))
+       artist-name lastfm-url artist-image
+       stats-listeners stats-playcount
+       bio-summary bio-complete)
+    (while c
+      (let ((entry (car c)))
+       (when (listp entry)
+         (let ((name (nth 0 entry))
+               (value (nth 2 entry)))
+           (cond ((equal name 'name) (setq artist-name value))
+                 ((equal name 'url)  (setq lastfm-url value))
+                 ((equal name 'image)
+                  (let ((size (cdar (nth 1 entry))))
+                    (when (string-equal emms-lastfm-client-image-size
+                                        size)
+                      (setq artist-image value))))
+                 ((equal name 'stats)
+                  (setq stats-listeners (nth 2 (nth 3 entry))
+                        stats-playcount (nth 2 (nth 5 entry))))
+                 ((equal name 'bio)
+                  (setq bio-summary (nth 2 (nth 5 entry))
+                        bio-complete (nth 2 (nth 7 entry))))))))
+      (setq c (cdr c)))
+    (emms-lastfm-client-display-artist-getinfo
+     artist-name lastfm-url artist-image
+     stats-listeners stats-playcount
+     bio-summary bio-complete)))
+
+(defun emms-lastfm-client-construct-artist-getinfo ()
+  "Return a request for getting info about an artist."
+  (let ((arguments
+        (emms-lastfm-client-encode-arguments
+         `(("sk"      . ,emms-lastfm-client-api-session-key)
+           ("api_key" . ,emms-lastfm-client-api-key)
+           ("autocorrect" . "1")
+           ("artist"  . ,(emms-lastfm-client-xspf-get
+                          'creator emms-lastfm-client-track))))))
+    (emms-lastfm-client-construct-write-method-call
+     'artist-getinfo arguments)))
+
+(defun emms-lastfm-client-make-call-artist-getinfo ()
+  "Make a call for artist info."
+  (let ((url-request-method "POST")
+       (url-request-extra-headers
+        `(("Content-type" . "application/x-www-form-urlencoded")))
+       (url-request-data
+        (emms-lastfm-client-construct-artist-getinfo)))
+    (let ((response
+          (url-retrieve-synchronously
+           emms-lastfm-client-api-base-url)))
+      (emms-lastfm-client-handle-response
+       'artist-getinfo
+       (with-current-buffer response
+        (xml-parse-region (point-min) (point-max)))))))
+
+(defun emms-lastfm-client-artist-getinfo-failed (data)
+  "Function called with DATA when setting `ban' rating fails."
+  'stub-needs-to-handle-artist-getinfo-issues
+  (emms-lastfm-client-default-error-handler data))
+
+(defun emms-lastfm-client-artist-getinfo-ok (data)
+  "Function called with DATA after `ban' rating succeeds."
+  (emms-lastfm-client-parse-artist-getinfo data))
+
+;;; ------------------------------------------------------------------
+;;; method: track.ban [http://www.last.fm/api/show?service=261]
+;;; ------------------------------------------------------------------
+
+(defun emms-lastfm-client-construct-track-ban ()
+  "Return a request for setting current track rating to `ban'."
+  (let ((arguments
+        (emms-lastfm-client-encode-arguments
+         `(("sk"      . ,emms-lastfm-client-api-session-key)
+           ("api_key" . ,emms-lastfm-client-api-key)
+           ("track"   . ,(emms-lastfm-client-xspf-get
+                          'title emms-lastfm-client-track))
+           ("artist"  . ,(emms-lastfm-client-xspf-get
+                          'creator emms-lastfm-client-track))))))
+    (emms-lastfm-client-construct-write-method-call
+     'track-ban arguments)))
+
+(defun emms-lastfm-client-make-call-track-ban ()
+  "Make call for setting track rating to `ban'."
+  (let ((url-request-method "POST")
+       (url-request-extra-headers
+        `(("Content-type" . "application/x-www-form-urlencoded")))
+       (url-request-data
+        (emms-lastfm-client-construct-track-ban)))
+    (let ((response
+          (url-retrieve-synchronously
+           emms-lastfm-client-api-base-url)))
+      (emms-lastfm-client-handle-response
+       'track-ban
+       (with-current-buffer response
+        (xml-parse-region (point-min) (point-max)))))))
+
+(defun emms-lastfm-client-track-ban-failed (data)
+  "Function called with DATA when setting `ban' rating fails."
+  'stub-needs-to-handle-track-ban-issues
+  (emms-lastfm-client-default-error-handler data))
+
+(defun emms-lastfm-client-track-ban-ok (data)
+  "Function called with DATA after `ban' rating succeeds."
+  'track-ban-succeed)
+
+(provide 'emms-lastfm-client)
+
+;;; emms-lastfm-client.el ends here
diff --git a/.emacs.d/elisp/emms/lisp/emms-lastfm-scrobbler.el b/.emacs.d/elisp/emms/lisp/emms-lastfm-scrobbler.el
new file mode 100644 (file)
index 0000000..cfc9a75
--- /dev/null
@@ -0,0 +1,367 @@
+;;; emms-lastfm-scrobbler.el --- Last.FM Music API
+
+;; Copyright (C) 2009, 2010  Free Software Foundation, Inc.
+
+;; Authors: Bram van der Kroef <bram@fortfrances.com>, Yoni Rabkin
+;; <yonirabkin@member.fsf.org>
+
+;; Keywords: emms, lastfm
+
+;; EMMS is free software; you can redistribute it and/or modify it
+;; under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+;;
+;; EMMS is distributed in the hope that it will be useful, but WITHOUT
+;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+;; License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with EMMS; see the file COPYING.  If not, write to the Free
+;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+;; MA 02110-1301, USA.
+
+;;; Code:
+
+;;; ------------------------------------------------------------------
+;;; Submission API [http://www.last.fm/api/submissions]
+;;; ------------------------------------------------------------------
+
+(require 'emms)
+(require 'emms-playing-time)
+(require 'emms-lastfm-client)
+
+;; Variables referenced from emms-lastfm-client:
+;;  emms-lastfm-client-username, emms-lastfm-client-api-key,
+;;  emms-lastfm-client-api-secret-key, emms-lastfm-client-api-session-key,
+;;   emms-lastfm-client-track
+;; Functions referenced:
+;;  emms-lastfm-client-xspf-get, emms-lastfm-client-xspf-extension,
+;;  emms-lastfm-client-initialize-session
+
+(defcustom emms-lastfm-scrobbler-submit-track-types '(file)
+  "Specify what types of tracks to submit to Last.fm.
+The default is to only submit files.
+
+To submit every track to Last.fm, set this to t."
+  :type '(choice (const :tag "All" t)
+                (set :tag "Types"
+                     (const :tag "Files" file)
+                     (const :tag "URLs" url)
+                     (const :tag "Playlists&qu