How to use EMMS effectively
First thing is to set up emms.
I could simply enable all the emms features in one line,
(with-eval-after-load 'emms (emms-all))
But above setup makes filtering tracks very slow because it's too heavy weight.
So I use below setup,
(with-eval-after-load 'emms
;; minimum setup is more robust
(emms-minimalistic)
;; `emms-info-native' supports mp3,flac and requires NO cli tools
(unless (memq 'emms-info-native emms-info-functions)
(require 'emms-info-native)
(push 'emms-info-native emms-info-functions))
;; extract track info when loading the playlist
(push 'emms-info-initialize-track emms-track-initialize-functions)
;; I also use emms to manage tv shows, so I use mplayer only
(setq emms-player-list '(emms-player-mplayer)))
Play mp3&flac in "~/Dropbox/music",
(defun my-music ()
"My music."
(interactive)
(emms-stop)
(when (bufferp emms-playlist-buffer-name)
(kill-buffer emms-playlist-buffer-name))
(emms-play-directory-tree "~/Dropbox/music")
(emms-shuffle)
(emms-next))
Sometimes I need focus on challenge programming tasks and emms should play only Mozart&Bach.
(defvar my-emms-playlist-filter-keyword "mozart|bach"
"Keyword to filter tracks in emms playlist.
Space in the keyword matches any characters.
\"|\" means OR operator in regexp.")
(defun my-strip-path (path strip-count)
"Strip PATH with STRIP-COUNT."
(let* ((i (1- (length path)))
str)
(while (and (> strip-count 0)
(> i 0))
(when (= (aref path i) ?/)
(setq strip-count (1- strip-count)))
(setq i (1- i)))
(setq str (if (= 0 strip-count) (substring path (1+ i)) path))
(replace-regexp-in-string "^/" "" str)))
(defun my-emms-track-description (track)
"Description of TRACK."
(let ((desc (emms-track-simple-description track))
(type (emms-track-type track)))
(when (eq 'file type)
(setq desc (my-strip-path desc 2)))
desc))
(defvar my-emms-track-regexp-function #'my-emms-track-regexp-internal
"Get regexp to search track.")
(defun my-emms-track-regexp-internal (keyword)
"Convert KEYWORD into regexp for matching tracks."
(let* ((re (replace-regexp-in-string "|" "\\\\|" keyword)))
(setq re (replace-regexp-in-string " +" ".*" re))))
(defun my-emms-track-match-p (track keyword)
"Test if TRACK's information match KEYWORD."
(let* ((case-fold-search t)
(regexp (funcall my-emms-track-regexp-function keyword))
s)
(or (string-match regexp (emms-track-force-description track))
(and (setq s (emms-track-get track 'info-genre)) (string-match regexp s))
(and (setq s (emms-track-get track 'info-title)) (string-match regexp s))
(and (setq s (emms-track-get track 'info-album)) (string-match regexp s))
(and (setq s (emms-track-get track 'info-composer)) (string-match regexp s))
(and (setq s (emms-track-get track 'info-artist)) (string-match regexp s)))))
(defun my-emms-show ()
"Show information of current track."
(interactive)
(let* ((emms-track-description-function (lambda (track)
(let ((composer (emms-track-get track 'info-composer))
(artist (emms-track-get track 'info-artist)))
(concat (if composer (format "%s(C) => " composer))
(if artist (format "%s(A) => " artist))
(my-emms-track-description track))))))
(emms-show)))
(defun my-emms-playlist-filter (&optional input-p)
"Filter tracks in emms playlist.
If INPUT-P is t, `my-emms-playlist-random-track-keyword' is input by user."
(interactive "P")
;; shuffle the playlist
(when input-p
(setq my-emms-playlist-filter-keyword
(read-string "Keyword to filter tracks in playlist: ")))
(with-current-buffer emms-playlist-buffer-name
(goto-char (point-min))
(let* ((case-fold-search t)
track)
(while (setq track (emms-playlist-track-at))
(cond
((my-emms-track-match-p track my-emms-playlist-filter-keyword)
(forward-line 1))
(t
(emms-playlist-mode-kill-track))))))
(emms-random)
;; show current track info
(my-emms-show))
As you can see, a little EMMS api knowledge could go a long way.
If you want to study EMMS API by practice, run M-x emms-playlist-mode-go
, then M-x eval-expression RETURN (emms-playlist-track-at)
to get the information of the track at point.
Here is my real world emms setup where you can see below code,
(defvar my-emms-track-regexp-function
(lambda (str)
;; can search track with Chinese information
(my-emms-track-regexp-internal (my-extended-regexp str)))
"Get regexp to search track.")
So I can use Pinyin to search track's Chinese information. I don't know any other multimedia manager can do the same thing.
Add live demo to emacs package
Emacs package developers sometimes need add live demo to her/his project.
The requirement came from my discussion with pyim's developer.
With all the linting and unit tests running for ages, he still need a quick way to test if the package actually works on Emacs 25 (and other Emacs versions). I totally agree with him because my own projects have similar problems.
A live demo built into the project is very useful for developers and testers.
Besides, a live demo could help users. They try the new package with no hassle. They don't modify their own emacs configuration to try the new package.
So I figured out a simple solution. The best part is that any packages could use this solution with minimum change if their CI script is already set up.
In a package's CI script, Emacs is running in batch mode (with "–batch" option). What I suggest is add another Makefile task runemacs
which is very similar to the original CI task. But in this task, the "–batch" options is removed.
See the solution I added for find-file-in-project,
diff --git a/Makefile b/Makefile
index 9005ca4..8f7a8ae 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,8 @@
SHELL = /bin/sh
EMACS ?= emacs
PROFILER =
-EMACS_BATCH_OPTS=--batch -Q -l find-file-in-project.el
+EMACS_GENERIC_OPTS=-Q -L . -L deps/ivy-0.13.4
+EMACS_BATCH_OPTS:=--batch $(EMACS_GENERIC_OPTS)
RM = @rm -rf
.PHONY: test clean test compile
@@ -18,3 +19,8 @@ compile: clean
# Run tests.
test: compile
@$(EMACS) $(EMACS_BATCH_OPTS) -l tests/ffip-tests.el
+
+runemacs:
+ @mkdir -p deps;
+ @if [ ! -f deps/ivy-0.13.4/ivy.el ]; then curl -L https://stable.melpa.org/packages/ivy-0.13.4.tar | tar x -C deps/; fi;
+ @$(EMACS) $(EMACS_GENERIC_OPTS) --load ./tests/emacs-init.el
diff --git a/tests/emacs-init.el b/tests/emacs-init.el
new file mode 100644
index 0000000..a4df068
--- /dev/null
+++ b/tests/emacs-init.el
@@ -0,0 +1,17 @@
+(require 'find-file-in-project)
+(require 'ivy)
+(ivy-mode 1)
+(setq ffip-match-path-instead-of-filename t)
+(run-with-idle-timer
+ 1
+ nil
+ (lambda ()
+ (erase-buffer)
+ (goto-char (point-min))
+ (insert
+ ";; Setup of this demo,\n"
+ "(setq ffip-match-path-instead-of-filename t)\n\n\n"
+ ";; Run \"M-x find-file-in-project-by-selected\" and input search keyword \"el\" or \"tests\".\n\n\n"
+ ";; Move cursor above below paths and run \"M-x find-file-in-project-at-point\",\n\n"
+ ";; tests/ffip-tests.el ; open file directly \n"
+ ";; find-file-in-project.el:50 ; open file and jump to line 50\n")))
Similar solution is also used in pyim, it's one liner in shell to test it in Emacs 25,
EMACS=/home/cb/what-ever-path/25.1/bin/emacs make runemacs
凉拌苦瓜
做法:
- 一个大苦瓜洗净后去掉瓜瓤和苦瓜的白色部分,将片好的苦瓜斜刀切成细丝
- 一个尖辣椒和大葱斜切丝(辣椒不用去籽),将蒜末(3或4个蒜瓣),生抽,糖,盐,醋,香油混合均匀制成碗汁备用
- 锅内做水,水开后放入一小勺盐和少许油。下入苦瓜丝焯烫约15秒
- 盛出苦瓜丝迅速用水冲凉后控干水分。放上红椒丝和葱丝,淋上调好的汁搅拌均匀即可
- 喜欢辣椒的可以放些辣椒油搅拌均匀,不喜欢的可以不放
小结:
- 开水烫过苦瓜变色了
- 尖辣椒和蒜的味道出来了
下次用台湾阿基师的方法改进.
- 苦瓜(白色比绿色苦味淡一点)去馅,里面薄膜也要刮掉以去苦味
- 斜切片,一个技巧是用刮皮的刀可以切更薄的片,用盐抓一下,浸水去苦味(是否这样营养也损失了?).之后浸入冰块变脆
- 调汁.鲜味酱油和净水比例1:5,加少许柴鱼粉(可用鸡精代替),滴入两块柠檬的汁(柠檬的香味和酸味可以掩盖苦味)
- 将汁浇在苦瓜上,洒一些烤干的白芝麻
蒸茄盒
- 两个茄子选软且颜色深,去皮.抹盐再冲掉(为了避免表面变黑),切茄盒
- 葱姜末,料酒,肉米,全蛋,少许盐鸡精,花椒水三勺,二合一酱油20克,加淀粉适量(不用太多,2~3勺)锁住水分.搅拌制作成馅(口感类似午餐肉)
- 茄盒加入馅,摆盘后洒三分口盐(盐引出茄子的汁),大火蒸15分钟,洒红绿尖椒末,蒸一分钟
- 浇上酱油,加葱丝香菜,呲热油(速度要快)