From 6f0796a53f2f8bd0027714796b9feac054bee313 Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Sat, 19 Aug 2023 18:16:32 +1000 Subject: Adding a file handling snes spc music files. Also update emms random album mechanism to assign low weight to the discovery directory --- emacs/.emacs.d/lisp/my/my-spc.el | 144 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 emacs/.emacs.d/lisp/my/my-spc.el (limited to 'emacs/.emacs.d/lisp/my/my-spc.el') 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 +;; Warren Wilkinson +;; Maintainer: Yuchen Pei +;; 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 . + +;;; 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 -- cgit v1.2.3