;;; emms-init.el --- Initialize emms
;; This file is part of Michael Olson's Emacs settings.
;; The code in this file may be used, distributed, and modified
;; without restriction.
;; I use initsplit.el to separate customize settings on a per-project
;; basis.
;;; Setup
(add-to-list 'load-path "/home/mwolson/proj/emacs/emms/emms2")
;; Initialize
(require 'emms)
(require 'emms-browser)
(require 'emms-cache)
(require 'emms-info)
(require 'emms-lastfm)
(require 'emms-player-mpd)
(require 'emms-playing-time)
(require 'emms-playlist-mode)
(require 'emms-streams)
(require 'emms-tag-editor)
(require 'emms-volume)
(emms-cache 1)
(emms-playing-time 1)
(emms-lastfm 1) ; must come after emms-playing-time
;; Load authentication info from an external source
(load "~/.emacs.d/.emms-auth")
;; The name of emms-playlist-mode is *way* too long
(add-hook 'emms-playlist-mode-hook
#'(lambda ()
(setq mode-name "EMMS")))
;; Only show files in emms-browser, not playlists
(emms-browser-make-filter
"all-files" (emms-browser-filter-only-type 'file))
(emms-browser-set-filter (assoc "all-files" emms-browser-filters))
;;; Helper functions
(defun my-upcase-initials (string)
"Do `upcase-initials' on STRING, but do not uppercase letters
that come after quote characters."
(with-temp-buffer
(insert (upcase-initials string))
(goto-char (point-min))
(while (re-search-forward "['`]\\([[:upper:]]\\)" nil t)
(downcase-region (match-beginning 1) (match-end 1)))
(buffer-string)))
(defun my-emms-info-track-description (track)
"Return a description of the current track."
(let ((artist (emms-track-get track 'info-artist))
(title (emms-track-get track 'info-title))
(name (emms-track-get track 'name))
(type (emms-track-type track)))
(cond ((and (stringp artist) (not (string= artist ""))
(stringp title) (not (string= title "")))
(let ((desc (format "%s - %s" artist title)))
(if (memq type '(file url))
desc
(concat (symbol-name type) ": " desc))))
((and (stringp title) (not (string= title "")))
(my-upcase-initials title))
((null name)
"Invalid track!")
((memq type '(url streamlist))
"")
(t
(concat (symbol-name type) ": " name)))))
(defun my-emms-info-fromname (track)
"Add track information to TRACK.
This is a useful element for `emms-info-functions'."
(when (and (memq (emms-track-type track) '(file playlist))
(not (or (emms-track-get track 'info-artist)
(emms-track-get track 'info-title))))
(let ((name (emms-track-get track 'name))
artist title)
(unless (or (null name) (string-match "\\`http://" name))
(setq name (file-name-sans-extension (file-name-nondirectory name)))
(save-match-data
(when (string-match "\\`\\([^-]+?\\) *- *\\(.*\\)\\'" name)
(setq artist (match-string 1 name)
title (match-string 2 name))
(setq artist (my-upcase-initials
(emms-replace-regexp-in-string "_" " " artist)))
(setq title (my-upcase-initials
(emms-replace-regexp-in-string "_" " " title)))
(emms-track-set track 'info-artist artist)
(emms-track-set track 'info-title title)))))))
;;; Utilities
;; Show the currently-playing song
(defun np (&optional arg)
(interactive "P")
(emms-player-mpd-show arg))
;; Reset the cached info for all songs in the current buffer
(defun my-emms-reset-cache ()
(interactive)
(save-excursion
(goto-char (point-min))
(emms-walk-tracks
(let ((track (emms-playlist-track-at (point))))
(emms-cache-set (emms-track-get track 'type)
(emms-track-get track 'name)
nil)))))
;; Open file at point in playlist
(defun my-emms-playlist-open-file ()
(interactive)
(let ((name (emms-track-get (emms-playlist-track-at) 'name)))
(when name
(find-file name))))
;; Switch to the radio buffer
(defun my-emms-streams ()
(interactive)
(let ((buf (get-buffer emms-stream-buffer-name)))
(if buf
(switch-to-buffer buf)
(emms-streams))))
;; Switch to either the radio buffer or the current EMMS playlist
(defun my-emms-switch-to-current-playlist ()
(interactive)
(if (and (boundp 'emms-stream-playlist-buffer)
(eq emms-stream-playlist-buffer emms-playlist-buffer))
(switch-to-buffer emms-stream-buffer-name)
(if (or (null emms-playlist-buffer)
(not (buffer-live-p emms-playlist-buffer)))
(error "No current Emms buffer")
(switch-to-buffer emms-playlist-buffer))))
;; Bring up the browser interface with my preferences
(defvar my-emms-browser-buffer nil)
(defun my-emms-switch-to-browser ()
(interactive)
(if (and my-emms-browser-buffer
(buffer-live-p my-emms-browser-buffer))
(switch-to-buffer my-emms-browser-buffer)
(emms-browse-by-artist)
(emms-browser-expand-to-level-2)
(setq my-emms-browser-buffer (current-buffer)))
(isearch-forward))
;;; Every time the song changes, show me its description
(defvar np-interval 5
"How often (in seconds) to check whether the current song has changed.")
;; Internal
(defvar np-last nil
"Description for most recently-played track.")
(defvar np-timer nil
"Timer used by `np-maybe'.")
(defun np-maybe ()
(condition-case nil
(emms-player-mpd-show
nil
(lambda (buffer desc)
(when (and (stringp desc)
(not (string= desc "")))
(unless (and (stringp np-last)
(string= np-last desc))
(setq np-last desc)
(message "%s" desc)))))
(error (np-remove))))
(defun np-insinuate ()
(unless np-timer
(setq np-timer (run-at-time t np-interval #'np-maybe))))
(defun np-remove ()
(when np-timer
(emms-cancel-timer np-timer)
(setq np-timer nil)))
(add-hook 'emms-player-started-hook #'np-insinuate)
(add-hook 'emms-player-stopped-hook #'np-remove)
(add-hook 'emms-player-finished-hook #'np-remove)
;;; Key customizations
(define-key emms-playlist-mode-map "o" 'my-emms-playlist-open-file)
(define-key emms-playlist-mode-map "q" 'emms-playlist-current-kill)
(define-key emms-playlist-mode-map (kbd "SPC") 'emms-pause)
(global-set-key "\C-cm" nil)
(global-set-key "\C-cm " 'emms-pause)
(global-set-key "\C-cm0" 'emms-playlist-current-clear)
(global-set-key "\C-cmb" 'my-emms-switch-to-browser)
(global-set-key "\C-cmc" 'emms-player-mpd-connect)
(global-set-key "\C-cml" nil)
(global-set-key "\C-cmlb" 'emms-lastfm-radio-ban)
(global-set-key "\C-cmll" 'emms-lastfm-radio-love)
(global-set-key "\C-cmls" 'emms-lastfm-radio-skip)
(global-set-key "\C-cmn" 'emms-player-mpd-next)
(global-set-key "\C-cmo" 'my-emms-switch-to-current-playlist)
(global-set-key "\C-cmp" 'emms-player-mpd-previous)
(global-set-key "\C-cmq" 'emms-stop)
(global-set-key "\C-cmQ" #'(lambda () (interactive) (emms-player-mpd-clear)))
(global-set-key "\C-cmr" 'my-emms-streams)
(global-set-key "\C-cms" 'emms-seek)
(global-set-key "\C-cm-" 'emms-volume-mode-minus)
(global-set-key "\C-cm+" 'emms-volume-mode-plus)
(global-set-key "\C-cm\C-c" 'my-emms-reset-cache)
;;; Custom variables
(custom-set-variables
'(emms-cache-file "~/.emacs.d/.emms-cache")
'(emms-info-asynchronously nil)
'(emms-info-functions (quote (emms-info-mpd my-emms-info-fromname)))
'(emms-lastfm-radio-metadata-period nil)
'(emms-player-list (quote (emms-player-mpd emms-player-lastfm-radio)))
'(emms-player-mpd-music-directory "/stuff/music")
'(emms-player-mpd-verbose t)
'(emms-playlist-buffer-name "*EMMS Playlist*")
'(emms-playlist-default-major-mode (quote emms-playlist-mode))
'(emms-playlist-mode-open-playlists t)
'(emms-show-format "NP: %s")
'(emms-stream-default-action "play")
'(emms-track-description-function (quote my-emms-info-track-description))
'(emms-track-initialize-functions (quote (emms-info-initialize-track)))
'(emms-volume-change-amount 1)
'(emms-volume-change-function (quote emms-volume-mpd-change)))
(custom-set-faces)
;;; Initialization
;;; (define-emms-combined-source all nil
;;; '((emms-source-playlist-directory "/stuff/music/albums")
;;; (emms-source-playlist-directory "/stuff/music/albums/classical")
;;; (emms-source-playlist-directory "/stuff/music/albums/candy_mind")
;;; (emms-source-playlist-directory "/stuff/music/mixes")
;;; (emms-source-playlist-file "~/.emacs.d/emms-playlist")))
;;; (emms-add-all)
;;; emms-init.el ends here