2015-12-05 Emms and hydra

Some time ago I wrote a bit about my Emms setup. Well, I changed my computer now (after my old laptop died), and I no longer have convenient multimedia keys. So, I decided that I need to change my keybindings. I figured out that the time has finally come to play around with hydra. (I’m probably the last user to switch to it…)

In case I’m not the last, hydra is really great. While it has some (minor) shortcomings, I like it a lot. Here is my current emms/hydra setup:

(emms-standard)
(emms-default-players)

(setq emms-source-file-directory-tree-function #'emms-source-file-directory-tree-find)
(setq emms-source-file-default-directory "~/Muzyka/")

(defun emms-status (&optional print-message)
  "Returns the status of Emms - one of `playing', `paused' and
`stopped'.  If called interactively, prints an appropriate
message.  If called from Lisp code, returns an appropriate
symbol."
  (interactive "p")
  (let ((status
         (if emms-player-playing-p
             (if emms-player-paused-p
                 'paused 'playing)
           'stopped)))
    (if print-message
        (message "Emms status: %s." status)
      status)))

(defun emms-status-track (&optional print-message)
  "Return or print in the echo area the status of Emms.  If playing,
include the current track info."
  (interactive "p")
  (let ((status (emms-status))
        (fun (if print-message #'message #'format)))
    (if (eq status 'stopped)
        (funcall fun "Emms stopped.")
      (funcall fun "Emms %s, current track: %s."
               status
               (emms-track-description
                (emms-playlist-current-selected-track))))))

(defun mb/emms-forward (&optional seconds)
  "Play next track if SECONDS is nil, and seek forward SECONDS if
given.  In particular, if called with \\[universal-argument], seek 4 seconds
forward."
  (interactive "P")
  (if seconds
      (emms-seek (prefix-numeric-value seconds))
    (emms-next)))

(defun mb/emms-backward (&optional seconds)
  "Play previous track if SECONDS is nil, and seek forward SECONDS if
given.  In particular, if called with \\[universal-argument], seek 4 seconds
backward."
  (interactive "P")
  (if seconds
      (emms-seek (- (prefix-numeric-value seconds)))
    (emms-previous)))

(defun mb/emms-pause ()
  "Like emms-pause, but show the status afterwards."
  (interactive)
  (emms-pause))

(defun mb/emms-stop ()
  "Like emms-stop, but show the status afterwards."
  (interactive)
  (emms-stop))

(defhydra hydra-emms (:timeout 6 :columns 4)
  "
Emms: %s(emms-status-track)
"
  ("SPC" mb/emms-pause "Play/Pause")
  ("s" mb/emms-stop "Stop")
  ("<left>" mb/emms-backward "Backward")
  ("<right>" mb/emms-forward "Forward")
  ("<down>" emms-volume-lower "Volume down")
  ("<up>" emms-volume-raise "Volume up")
  ("+" emms-add-directory "Add directory")
  ("C-+" emms-add-directory-tree "Add directory tree")
  ("RET" emms "Show playlist")
  ("r" emms-shuffle "Shuffle tracks")
  ("<f12>" ignore "Exit" :exit t))

(global-set-key (kbd "<f12>") 'hydra-emms/body)

As usual, there are a few (simple) tricks here. One of them is funcall‘ing format or message, depending on whether emms-status-track was called interactively or not. The trick with a prefix argument with (interactive "p") (which is never nil when the function is called interactively!) is nice, and “official” (i.e., recommended in the manual). The hydra was nice, and I can do all sorts of things with it, but for a moment I didn’t know how to quit it (other than C-g – I wanted one of the hydra options to just quit it); after a moment, the simple idea with a blue keybinding to (ignore) occured to me. Also, I like the fact that hydra updates the %s(...) field in real time.

Let me join the crowd of people who are really, really thankful to Oleh for hydra. It’s a great package, and I’ll definitely use it more and more with time! (Also, I’m considering marrying hydra with chord.el – but that will have to wait a bit.)

CategoryEnglish, CategoryBlog, CategoryEmacs