;;; init-music.el --- Initialize the EMMS.

;;; Copyright (C) 2007--2011 Nix <nix@esperi.org.uk>.

;; Author: Nix <nix@esperi.org.uk>
;; Created: 2007-08-16
;; Keywords: c lisp local

;; This file is not part of Emacs.

;; This library 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.

;;; Commentary:

;; This file sets up the Emacs Multi-Media System.

;;; Requirements:

(require 'emms)                         ; The EMMS itself.
(require 'emms-source-file)             ; Music from files.
(require 'emms-source-playlist)         ; Music from playlists.
(require 'emms-player-mpd)              ; Play music via MPD.
(require 'emms-player-simple)           ; Fall back to direct playing.
(require 'emms-playlist-mode)           ; The playlist viewing mode.
(require 'emms-playlist-limit)          ; Limit the playlist in different ways.
(require 'emms-info)                    ; Track info retrieval.
(require 'emms-cache)                   ; The track cache.
(require 'emms-mode-line)               ; EMMS modeline code, a bit of which is useful
(require 'emms-streams)                 ; Stream playback.
(require 'emms-stream-info)             ; Info from streams.
(require 'emms-playing-time)            ; Playing time, not properly working yet.
(require 'emms-playlist-sort)           ; Sorting playlists.
(require 'emms-browser)                 ; The EMMS smart browser.
(require 'emms-metaplaylist-mode)       ; Lists of playlists.
(require 'emms-bookmarks)               ; Bookmarks on the current track.
(require 'emms-history)                 ; Save playlist state on exit.
(require 'emms-volume)                  ; Volume setting.
(require 'gawd-lists)                   ; For `splice-equal'.

;; Fix up paths.

(setq emms-history-file "~/.emms/history"
      emms-score-file "~/.emms/scores"
      emms-cache-file "~/.emms/cache"
      emms-stream-bookmarks-file "~/.emms/stream-bookmarks")

;; Locations.

(setq emms-source-file-default-directory "/usr/archive/music"
      emms-player-mpd-music-directory "/usr/archive/music"
      emms-player-mpd-server-name "music.srvr.nix")
 
;; Use MPD, then GStreamer.

(setq emms-player-list '(emms-player-mpd
                         emms-player-gstreamer))
(add-to-list 'emms-info-functions 'emms-info-mpd)
(setq emms-volume-change-function 'emms-volume-mpd-change)

;; Modeline setting. The stuff in emms-mode-line is mostly worthless
;; for us, except for the parts which actually do the updating.

(if (featurep 'xemacs)
    (progn
      (define-modeline-control "emms" nil
        "Modeline control for EMMS.")

      (setq-default modeline-emms '((emms-mode-line-active-p . (emms-mode-line-string))
                                    (emms-playing-time-display-p . (emms-playing-time-string))))

      ;; Add the emms element to the modeline just after the mode info.

      (setq modeline-format (splice-equal '(")%]") (cons modeline-emms-extent 'modeline-emms) modeline-format)))
  ;; Emacs.
  
  (defvar mode-line-emms '((emms-mode-line-active-p . (emms-mode-line-string))
                           (emms-playing-time-display-p . (emms-playing-time-string)))
    "String displayed in the modeline for EMMS.")

  (put 'mode-line-emms 'risky-local-variable t)
  (setq mode-line-format (splice-before 'gawd-uptime-mode-line-string 'mode-line-emms mode-line-format)))

(setq emms-mode-line-format " «%s»")
(setq emms-playing-time-display-format " [%s]")

(defun emms-mode-line (arg)
  "Turn on `emms-mode-line' iff ARG is positive, off otherwise."
  (if (and arg (> arg 0))
      (progn
        (setq emms-mode-line-active-p t)
        (add-hook 'emms-track-updated-functions 'emms-mode-line-alter)
        (add-hook 'emms-player-finished-hook 'emms-mode-line-blank)
        (add-hook 'emms-player-stopped-hook 'emms-mode-line-blank)
        (add-hook 'emms-player-started-hook 'emms-mode-line-alter))
    (setq emms-mode-line-active-p nil)
    (remove-hook 'emms-track-updated-functions 'emms-mode-line-alter)
    (remove-hook 'emms-player-finished-hook 'emms-mode-line-blank)
    (remove-hook 'emms-player-stopped-hook 'emms-mode-line-blank)
    (remove-hook 'emms-player-started-hook 'emms-mode-line-alter)))

; Stub out functions whose effects are superseded by the modeline control.
(defun emms-playing-time-mode-line ())
(defun emms-playing-time-restore-mode-line ())

(emms-mode-line 1)
(emms-playing-time-enable-display)

;; Give priority to composer and album over artist, then sort by track number.

(setq emms-playlist-sort-list '(info-composer info-album info-artist 'info-tracknumber))

;; Browser formatting. Composers take priority over artists; albums don't bother
;; with covers and are named with the composer in the name (`Overture in A' isn't much
;; use unless you know who it's by)

(defun nix-emms-browser-track-composer-or-artist-format (bdata fmt)
  "Display the composer or the artist, whichever is set, then the item."
  (concat
   "%i"
   (let ((track (emms-browser-format-elem fmt "T"))
         (composer (emms-browser-format-elem fmt "C"))
         (artist (emms-browser-format-elem fmt "a")))
     (if (and track (not (string= track "0")))
         "%T "
       "")
     (if composer
         "%C: "
       (if artist
           "%a: ")))
   "%n"))

(setq emms-browser-info-title-format 'nix-emms-browser-track-composer-or-artist-format)
(setq emms-browser-playlist-info-title-format 'nix-emms-browser-track-composer-or-artist-format)
(setq emms-browser-info-album-format 'nix-emms-browser-track-composer-or-artist-format)
(setq emms-browser-playlist-info-album-format 'nix-emms-browser-track-composer-or-artist-format)

(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-composer)
   ((eq current-mapping 'info-year) 'info-composer)))

(defun nix-emms-browser-make-composer-name (entry type)
  "Return a name for ENTRY, used for making a bdata object.

Prioritizes composer over artist."
  (let ((key (car entry))
        (track (cadr entry))
        artist composer title) ;; only the first track
    (cond
     ((eq type 'info-title)
      (setq artist (emms-track-get track 'info-artist))
      (setq composer (emms-track-get track 'info-composer))
      (setq title (emms-track-get track 'info-title))
      (if (not (or (and artist title)
                   (and composer title)))
          key
        (concat (or composer artist) " - " title)))
     (t key))))

(setq emms-browser-default-browse-type 'info-composer
      emms-browser-make-name-function 'nix-emms-browser-make-composer-name)

(defun nix-emms-info-track-description (track)
  "Return a description of the current track."
  (let ((person (or (emms-track-get track 'info-composer)
                    (emms-track-get track 'info-artist)))
        (album (emms-track-get track 'info-album))
        (title (emms-track-get track 'info-title)))
    (if (and person album title)
        (format "%s's %s: %s" person album title)
      (if (and person title)
          (format "%s's %s" person title)
        (emms-track-simple-description track)))))

(setq emms-track-description-function 'nix-emms-info-track-description)

;; Volume mode keybindings.

(define-minor-mode emms-volume-minor-mode
  "Allows volume setting with + and - after an initial key combo."
  :global t
  :init-value nil
  :lighter " (+/-)"
  :keymap '(("<F11>" . emms-volume-mode-plus)
            ("<F2>" . emms-volume-mode-minus)))

;; Go!

(setq emms-source-file-directory-tree-function 'emms-source-file-directory-tree-find)

(emms-cache-enable)
(emms-history-load)
(condition-case nil (emms-player-mpd-connect) (error nil))

;; (Keybindings are in .emacs, in the master keybinding alist.)

(provide 'init-music)