Skip to Content.
Sympa Menu

emacspeak - [Emacspeak] Blog Article: Smart Media Selector

Subject: Emacspeak discussion list

List archive

[Emacspeak] Blog Article: Smart Media Selector

Chronological Thread 
  • From: "T.V Raman" <raman AT>
  • To: emacspeak AT
  • Subject: [Emacspeak] Blog Article: Smart Media Selector
  • Date: Mon, 18 Mar 2024 20:55:26 -0700

* Overview

I have over 60MB of audio content on my laptop spread across 755
subdirecories in over 9100 files. I also have many Internet stream
shortcuts that I listen to on a regular basis.

This blog article outlines the media selector implementation in
Emacspeak and shows how a small amount of Lisp code built atop
Emacs' built-in affordances of completion provides a light-weight yet
efficient interface. Notice that the implementation does not involve
fancy things like SQL databases, MP3 tags that one needs to update
etc.; the solution relies on the speed of today's laptops, especially
given the speed of disk access.

* User Experience

As I type this up, the set of requirements as expressed in English
is far more verbose (and likely more complicated) than its
expression in Lisp!

** Pre-requisites for content selection and playback

1. Launch either MPV (via package ~empv.el~) or ~mplayer~ via
Emacspeak's ~emacspeak-mplayer~ with a few keystrokes.
2. Media selection uses ~ido~ with ~fuzzy~ matching.
3. Choices are filtered incrementally for efficient eyes-free
interaction; see the relevant blog article on
effective-suggest-and-complete-in-eyes.html][Search, Input, Filter, Target]]
for additional background.
4. Content can be filtered using the directory structure,
where directories conceptually equate to music albums, audio
books or othre logical content groups.Once selected, a directory
and its contents are played as a conceptual /play-list/.
5. Searching and filtering can also occur across the list of all
9,100+ media files spread across 700+ directories.
6. Starting point of the SIFT process should be influenced by one's current
context, e.g., default-directory.
7. Each step of this process should have reasonable fallbacks.

* Mapping Design To Implementation

1. Directory where we start AKA /context/ is selected by function
A. If default directory matches emacspeak-media-directory-regexp,use it.
B. If default directory contains media files, then use it.
C. If default directory contains directory emacspeak-media --- then use
D. Otherwise use emacspeak-media-shortcuts as the fallback.
2. Once we have selected the context, function

~ido~ style interaction with
fuzzy-matching to pick the file to play.
3. That function uses Emacs' built-in ~directory-files-recursively~
to build the ~collection~ to hand-off to ~completing-read~; It
uses an Emacspeak provided function
build up the list of 755+ sub-directories that live under

* Resulting Experience

1. I can pick the media to play with a few keystrokes.
2. I use Emacs' ~repeat-mode~ to advantage whereby I can quickly
change volume etc once content is playing before going back to work.
3. There's *no* media-player UI to get in my way while working, but I
can stop playing media with a single keystroke.
4. Most importantly, I dont have to tag media, maintain databases or
do other busy work to be able to launch the media that I want!

* The Lisp Code

The hyperlinks to the Emacspeak code-base are the source of
truth. I'll include a snapshot of the functions mentioned
above for completeness.

** Guess Context

#+begin_src emacs-lisp
(defun emacspeak-media-guess-directory ()
"Guess media directory.
1. If default directory matches emacspeak-media-directory-regexp,use it.
2. If default directory contains media files, then use it.
3. If default directory contains directory emacspeak-media --- then use it.
4. Otherwise use emacspeak-media-shortcuts as the fallback."
(cl-declare (special emacspeak-media-directory-regexp
emacspeak-media emacspeak-m-player-hotkey-p))
(let ((case-fold-search t))
((or (eq major-mode 'dired-mode) (eq major-mode 'locate-mode)) nil)
(emacspeak-m-player-hotkey-p emacspeak-media-shortcuts)
((or ; dir contains media:
(string-match emacspeak-media-directory-regexp default-directory)
(directory-files default-directory nil emacspeak-media-extensions))
((file-in-directory-p emacspeak-media default-directory) emacspeak-media)
(t emacspeak-media-shortcuts))))

** Read Resource
#+begin_src emacs-lisp
(defun emacspeak-media-read-resource (&optional prefix)
"Read resource from minibuffer.
If a dynamic playlist exists, just use it."
(cl-declare (special emacspeak-media-dynamic-playlist
(emacspeak-media-dynamic-playlist nil) ; do nothing if dynamic playlist
(emacspeak-m-player-hotkey-p (emacspeak-media-local-resource prefix))
(t ; not hotkey, not dynamic playlist
(let* ((completion-ignore-case t)
(read-file-name-completion-ignore-case t)
(when (memq major-mode '(dired-mode locate-mode))
(dired-get-filename 'local 'no-error)))
(dir (emacspeak-media-guess-directory))
filename ; short-circuit expensive call
(if prefix
(ems--subdirs-recursively dir) ;list dirs
(directory-files-recursively dir
(or filename (completing-read "Media: " collection))))))

** Helper: Recursive List Of Sub-directories

#+begin_src emacs-lisp

;;; Helpers: subdirs

(defconst ems--subdirs-filter
(concat (regexp-opt '("/.." "/." "/.git")) "$"))
"Pattern to filter out dirs during traversal.")

(defsubst ems--subdirs (d)
"Return list of subdirs in directory d"
(cl-remove-if-not #'file-directory-p (cddr (directory-files d 'full))))

(defun ems--subdirs-recursively (d)
"Recursive list of subdirs"
(cl-declare (special ems--subdirs-filter))
(let ((result (list d))
(subdirs (ems--subdirs d)))
((string-match ems--subdirs-filter d) nil)
; pass
for dir in subdirs
if (not (string-match ems--subdirs-filter dir)) do
(setq result (nconc result (ems--subdirs-recursively dir))))))


#+options: ':nil *:t -:t ::t <:t H:3 \n:nil ^:t arch:headline
#+options: author:t broken-links:nil c:nil creator:nil
#+options: d:(not "LOGBOOK") date:t e:t email:nil f:t inline:t num:t
#+options: p:nil pri:nil prop:nil stat:t tags:t tasks:t tex:t
#+options: timestamp:t title:t toc:nil todo:t |:t
#+title: Smart Media Selector For The Audio Desktop
#+date: <2024-03-18 Mon>
#+author: T.V Raman
#+email: raman AT
#+language: en
#+select_tags: export
#+exclude_tags: noexport
#+creator: Emacs 30.0.50 (Org mode 9.6.15)




Archive powered by MHonArc 2.6.19+.

Top of Page