aboutsummaryrefslogtreecommitdiff
path: root/emacs
diff options
context:
space:
mode:
Diffstat (limited to 'emacs')
-rw-r--r--emacs/.emacs.d/lisp/my/my-emms.el31
-rw-r--r--emacs/.emacs.d/lisp/my/my-spc.el144
2 files changed, 170 insertions, 5 deletions
diff --git a/emacs/.emacs.d/lisp/my/my-emms.el b/emacs/.emacs.d/lisp/my/my-emms.el
index dadbb55..fa0ae17 100644
--- a/emacs/.emacs.d/lisp/my/my-emms.el
+++ b/emacs/.emacs.d/lisp/my/my-emms.el
@@ -39,7 +39,7 @@
(defun my-emms-mpv-toggle-video ()
(interactive)
(if (member "--no-video" emms-player-mpv-parameters)
- (progn
+ (progn
(setq emms-player-mpv-parameters
(remove "--no-video" emms-player-mpv-parameters))
(message "emms: video enabled!"))
@@ -126,6 +126,14 @@
playlist buffer-name)))
(and saved-buffer (emms-playlist-set-playlist-buffer saved-buffer))))
+(defun my-emms-add-directory-files-as-url (dir)
+ "Add all files under directory DIR as file:// url."
+ (mapc
+ (lambda (file)
+ (emms-add-url (format "file://%s" file)))
+ (directory-files-recursively (expand-file-name dir) ".*"))
+ )
+
(defun my-emms-add-all ()
(interactive)
(mapc 'my-emms-load-from-native my-emms-native-playlists)
@@ -147,13 +155,13 @@ either 'audio or 'video
(emms-playlist-set-playlist-buffer to)
(with-current-buffer to (emms-playlist-clear))
(let ((emms-track-initialize-functions nil))
- (my-emms-add-url-lists from
- (alist-get type my-extension-types)))
+ (my-emms-add-url-lists from
+ (alist-get type my-extension-types)))
(with-current-buffer to (emms-sort))))
(defvar my-emms-playlist-alist nil
"alist controlling playlists, where the cdr of each item is an also an alist,
-with possible keys 'source and 'type.
+with possible keys 'source and 'type.
'source is a list of files of url lists.
'type is one of 'audio, 'video, or 'audiovideo")
@@ -371,6 +379,19 @@ filter extensions from filter-exts."
(setq my-emms-albums-cache (vconcat album-list))
(message "Emms album cache updated.")))
+(defun my-emms-get-random-album ()
+ "Returns a random album from the current playlist.
+
+We put a low weight on discovery album, currently any directory
+under /zzz-seren/."
+ (let ((album
+ (elt my-emms-albums-cache (random (length my-emms-albums-cache)))))
+ (while (and (string-match "/zzz-seren/" album)
+ (>= (random 100) 4))
+ (setq album
+ (elt my-emms-albums-cache (random (length my-emms-albums-cache)))))
+ album))
+
(defun my-emms-random-album (update-album)
(interactive "P")
(with-current-emms-playlist
@@ -380,7 +401,7 @@ filter extensions from filter-exts."
(let ((saved-position (point)))
(goto-char (point-min))
(if (search-forward
- (elt my-emms-albums-cache (random (length my-emms-albums-cache)))
+ (my-emms-get-random-album)
nil t)
(emms-playlist-mode-play-current-track)
(goto-char saved-position)
diff --git a/emacs/.emacs.d/lisp/my/my-spc.el b/emacs/.emacs.d/lisp/my/my-spc.el
new file mode 100644
index 0000000..a6ecab3
--- /dev/null
+++ b/emacs/.emacs.d/lisp/my/my-spc.el
@@ -0,0 +1,144 @@
+;;; my-spc.el -- handling spc files -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation.
+
+;; Author: Yuchen Pei <id@ypei.org>
+;; Warren Wilkinson <warrenwilkinson@gmail.com>
+;; Maintainer: Yuchen Pei <id@ypei.org>
+;; Package-Requires: ((emacs "28"))
+
+;; This file is part of dotted.
+
+;; dotted is free software: you can redistribute it and/or modify it under
+;; the terms of the GNU Affero General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; dotted is distributed in the hope that it will be useful, but WITHOUT
+;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
+;; Public License for more details.
+
+;; You should have received a copy of the GNU Affero General Public
+;; License along with dotted. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; handling spc files.
+
+;;; Code:
+
+(require 'bindat)
+
+(defconst my-spc--id666-magic-array
+ [#x53 #x4e #x45 #x53 #x2d #x53 #x50#x43 #x37 #x30 #x30 #x20 #x53 #x6f #x75 #x6e #x64 #x20 #x46 #x69 #x6c #x65 #x20 #x44 #x61 #x74 #x61 #x20 #x76 #x30 #x2e #x33 #x30]
+ "id666 header magic pattern `SNES-SPC700 Sound File Data v0.30'")
+
+(defconst my-spc--id666-header-bindat-spec
+ '((file-identifier vec 33)
+ (eval (unless (equal last my-spc--id666-magic-array)
+ (error "id666 framing mismatch: expected `%s', got `%s'"
+ my-spc--id666-magic-array
+ last)))
+ (unused u16)
+ (has-id666 u8)
+ (revision u8)
+ (pc-reg u16)
+ (a-reg u8)
+ (x-reg u8)
+ (y-reg u8)
+ (psw-reg u8)
+ (sp-reg u8)
+ (res-reg u16)
+ (song-title strz 32)
+ (game-title strz 32)
+ (dumper strz 16)
+ (comment strz 32)
+ (date strz 11)
+ (fadeout vec 3)
+ (fadeout-length vec 5)
+ (artist strz 32))
+ "id666 header specification.
+
+Sources:
+
+- URL `https://ocremix.org/info/SPC_Format_Specification'
+- URL `https://picard-docs.musicbrainz.org/en/appendices/tag_mapping.html'")
+
+(defun my-spc--decode-id666-header (filename)
+ "Read and decode id666 header from FILENAME."
+ (with-temp-buffer
+ (set-buffer-multibyte nil)
+ (insert-file-contents-literally filename nil 0 210)
+ (bindat-unpack my-spc--id666-header-bindat-spec
+ (buffer-string))))
+
+(defun my-spc-decode-id666 (filename)
+ "Read and decode id666 metadata from FILENAME.
+Return metadata in a list of (FIELD . VALUE) cons cells, or nil
+in case of errors or if there were no known fields in FILENAME."
+ (condition-case nil
+ (let ((header (my-spc--decode-id666-header filename)))
+ (when (= 26 (bindat-get-field header 'has-id666))
+ `(
+ (title . ,(bindat-get-field header 'song-title))
+ (album . ,(bindat-get-field header 'game-title))
+ (artist . ,(bindat-get-field header 'artist))
+ (comment . ,(bindat-get-field header 'comment))
+ (dumper . ,(bindat-get-field header 'dumper))
+ (date . ,(bindat-get-field header 'date))
+ (fadeout . ,(bindat-get-field header 'fadeout))
+ (fadeout-length . ,(bindat-get-field header 'fadeout))
+ (extension . ,(file-name-extension filename))
+ (number . ,(replace-regexp-in-string
+ "[^-]+-" ""
+ (file-name-base filename)))
+ )))
+ (error nil)))
+
+(defun my-spc-format-file-name (file)
+ "Format a filename from an spc FILE.
+
+artist/album/title (note).extension"
+ (when-let* ((metadata (my-spc-decode-id666 file))
+ (artist (alist-get 'artist metadata))
+ (album (alist-get 'album metadata))
+ (title (alist-get 'title metadata))
+ (extension (alist-get 'extension metadata))
+ (number (alist-get 'number metadata)))
+ (if (or (string-empty-p artist)
+ (string-empty-p album)
+ (string-empty-p title)
+ (string-empty-p extension)
+ (string-empty-p number))
+ nil
+ (format "%s/%s/%s %s.%s" artist album number title extension))))
+
+(defun my-spc-file-rename (dir)
+ "Rename all spc files under DIR using dired."
+ (let ((pairs
+ (seq-filter
+ 'cdr
+ (mapcar
+ (lambda (file)
+ (cons (expand-file-name file)
+ (format "%s/%s"
+ (expand-file-name my-audio-incoming-dir)
+ (my-spc-format-file-name file))))
+ (directory-files-recursively dir "\\`.*\\.spc\\'")))))
+ (when (y-or-n-p
+ (print
+ (format
+ "Will do the following renaming: %s\nContinue?"
+ (string-join
+ (mapcar (lambda (pair)
+ (format "%s -> %s" (car pair) (cdr pair)))
+ pairs)
+ "\n"))))
+ (mapc
+ (lambda (pair)
+ (dired-rename-file (car pair) (cdr pair) t))
+ pairs))))
+
+(provide 'my-spc)
+;;; my-spc.el ends here