diff options
Diffstat (limited to 'emacs')
-rw-r--r-- | emacs/.emacs.d/lisp/my/my-emms.el | 31 | ||||
-rw-r--r-- | emacs/.emacs.d/lisp/my/my-spc.el | 144 |
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 |