emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/emms 757043b 41/80: Merge branch 'info-native'


From: Stefan Monnier
Subject: [elpa] externals/emms 757043b 41/80: Merge branch 'info-native'
Date: Wed, 17 Mar 2021 18:42:26 -0400 (EDT)

branch: externals/emms
commit 757043ba5c4d259df41f71fa6545a156de107ff8
Merge: 75ae932 cd437ca
Author: Petteri Hintsanen <petterih@iki.fi>
Commit: Petteri Hintsanen <petterih@iki.fi>

    Merge branch 'info-native'
---
 emms-info-native.el | 584 +++++++++++++++++++++++++++++++++-------------------
 1 file changed, 377 insertions(+), 207 deletions(-)

diff --git a/emms-info-native.el b/emms-info-native.el
index 9f0ed2d..8601dd0 100644
--- a/emms-info-native.el
+++ b/emms-info-native.el
@@ -59,6 +59,8 @@
 (require 'bindat)
 (require 'cl-lib)
 (require 'emms-info)
+(require 'seq)
+(require 'subr-x)
 
 (defconst emms-info-native--max-peek-size (* 2048 1024)
   "Maximum buffer size for metadata decoding.
@@ -72,127 +74,15 @@ Technically metadata blocks can have almost arbitrary 
lengths,
 but in practice processing must be constrained to prevent memory
 exhaustion in case of garbled or malicious inputs.")
 
-;;;; Ogg code
-
-(defconst emms-info-native--ogg-magic-array
-  [79 103 103 83]
-  "Ogg format magic capture pattern `OggS'.")
-
-(defconst emms-info-native--ogg-page-size 65307
-  "Maximum size for a single Ogg container page.")
-
-(defconst emms-info-native--ogg-page-bindat-spec
-  '((capture-pattern vec 4)
-    (eval (unless (equal last emms-info-native--ogg-magic-array)
-            (error "Ogg framing mismatch: expected `%s', got `%s'"
-                   emms-info-native--ogg-magic-array
-                   last)))
-    (stream-structure-version u8)
-    (eval (unless (= last 0)
-            (error ("Ogg version mismatch: expected 0, got %s")
-                   last)))
-    (header-type-flag u8)
-    (granule-position vec 8)
-    (stream-serial-number vec 4)
-    (page-sequence-no vec 4)
-    (page-checksum vec 4)
-    (page-segments u8)
-    (segment-table vec (page-segments))
-    (payload vec (eval (seq-reduce #'+ last 0))))
-  "Ogg page structure specification.")
-
-(defun emms-info-native--decode-ogg-comments (filename stream-type)
-  "Read and decode comments from Ogg file FILENAME.
-The file is assumed to contain a single stream of type
-STREAM-TYPE, which must either `vorbis' or `opus'.
-
-Return comments in a list of (FIELD . VALUE) cons cells.  See
-`emms-info-native--split-vorbis-comment' for details."
-  (let* ((packets (emms-info-native--decode-ogg-packets filename 2))
-         (headers (emms-info-native--decode-ogg-headers packets
-                                                        stream-type))
-         (comments (bindat-get-field headers
-                                     'comment-header
-                                     'user-comments)))
-    (emms-info-native--extract-vorbis-comments comments)))
-
-(defun emms-info-native--decode-ogg-packets (filename packets)
-  "Read and decode packets from Ogg file FILENAME.
-Read in data from the start of FILENAME, remove Ogg packet
-frames, and concatenate payloads until at least PACKETS number of
-packets have been decoded.  Return the decoded packets in a
-vector, concatenated.
-
-Data is read in `emms-info-native--ogg-page-size' chunks.  If the
-total length of concatenated packets becomes greater than
-`emms-info-native--max-peek-size', an error is signaled.
-
-Only elementary streams are supported, that is, FILENAME should
-contain only a single logical stream.  Note that this assumption
-is not verified: with non-elementary streams packets from
-different streams will be mixed together without an error."
-  (let ((num-packets 0)
-        (offset 0)
-        (stream (vector)))
-    (while (< num-packets packets)
-      (let ((page (emms-info-native--decode-ogg-page filename
-                                                     offset)))
-        (cl-incf num-packets (or (plist-get page :num-packets) 0))
-        (cl-incf offset (plist-get page :num-bytes))
-        (setq stream (vconcat stream (plist-get page :stream)))
-        (when (> (length stream) emms-info-native--max-peek-size)
-          (error "Ogg payload is too large"))))
-    stream))
-
-(defun emms-info-native--decode-ogg-page (filename offset)
-  "Read and decode a single Ogg page from FILENAME.
-Starting reading data from byte offset OFFSET.
-
-Return a plist (:num-packets N :num-bytes B :stream S), where N
-is the number of packets in the page, B is the size of the page
-in bytes, and S is the unframed logical bitstream in a vector.
-Note that N can be zero."
-  (with-temp-buffer
-    (set-buffer-multibyte nil)
-    (insert-file-contents-literally filename
-                                    nil
-                                    offset
-                                    (+ offset
-                                       emms-info-native--ogg-page-size))
-    (let* ((page (bindat-unpack emms-info-native--ogg-page-bindat-spec
-                                (buffer-string)))
-           (num-packets (emms-info-native--num-of-packets page))
-           (num-bytes (bindat-length emms-info-native--ogg-page-bindat-spec
-                                     page))
-           (stream (bindat-get-field page 'payload)))
-      (list :num-packets num-packets
-            :num-bytes num-bytes
-            :stream stream))))
-
-(defun emms-info-native--num-of-packets (page)
-  "Return the number of packets in Ogg page PAGE.
-PAGE must correspond to
-`emms-info-native--ogg-page-bindat-spec'."
-  ;; Every element that is less than 255 in the segment table
-  ;; represents a packet boundary.
-  (length (seq-filter (lambda (elt) (< elt 255))
-                      (bindat-get-field page 'segment-table))))
-
-(defun emms-info-native--decode-ogg-headers (packets stream-type)
-  "Decode first two stream headers from PACKETS for STREAM-TYPE.
-STREAM-TYPE must be either `vorbis' or `opus'.
+(defvar emms-info-native--opus-channel-count 0
+  "Last decoded Opus channel count.
+This is a kludge; it is needed because bindat spec cannot refer
+outside itself.")
 
-Return a structure that corresponds to either
-`emms-info-native--opus-headers-bindat-spec' or
-`emms-info-native--vorbis-headers-bindat-spec'."
-  (cond ((eq stream-type 'vorbis)
-         (bindat-unpack emms-info-native--vorbis-headers-bindat-spec
-                        packets))
-        ((eq stream-type 'opus)
-         (let (emms-info-native--opus-channel-count)
-           (bindat-unpack emms-info-native--opus-headers-bindat-spec
-                          packets)))
-        (t (error "Unknown stream type %s" stream-type))))
+(defvar emms-info-native--id3v2-version 0
+  "Last decoded id3v2 version.
+This is a kludge; it is needed because bindat spec cannot refer
+outside itself.")
 
 ;;;; Vorbis code
 
@@ -245,10 +135,6 @@ their comments have almost the same format as Vorbis.")
     "year")
   "EMMS info fields that are extracted from Vorbis comments.")
 
-(defconst emms-info-native--vorbis-magic-array
-  [118 111 114 98 105 115]
-  "Header packet magic pattern `vorbis'.")
-
 (defconst emms-info-native--vorbis-headers-bindat-spec
   '((identification-header struct 
emms-info-native--vorbis-identification-header-bindat-spec)
     (comment-header struct 
emms-info-native--vorbis-comment-header-bindat-spec))
@@ -282,6 +168,10 @@ header.")
                  last)))
   "Vorbis identification header specification.")
 
+(defconst emms-info-native--vorbis-magic-array
+  [118 111 114 98 105 115]
+  "Header packet magic pattern `vorbis'.")
+
 (defconst emms-info-native--vorbis-comment-header-bindat-spec
   '((packet-type u8)
     (eval (unless (= last 3)
@@ -356,19 +246,6 @@ lower case and VALUE is the decoded value."
 
 ;;;; Opus code
 
-(defvar emms-info-native--opus-channel-count 0
-  "Last decoded Opus channel count.
-This is a kludge; it is needed because bindat spec cannot refer
-outside itself.")
-
-(defconst emms-info-native--opus-head-magic-array
-  [79 112 117 115 72 101 97 100]
-  "Opus identification header magic pattern `OpusHead'.")
-
-(defconst emms-info-native--opus-tags-magic-array
-  [79 112 117 115 84 97 103 115]
-  "Opus comment header magic pattern `OpusTags'.")
-
 (defconst emms-info-native--opus-headers-bindat-spec
   '((identification-header struct 
emms-info-native--opus-identification-header-bindat-spec)
     (comment-header struct emms-info-native--opus-comment-header-bindat-spec))
@@ -397,6 +274,10 @@ header.")
            (t (struct emms-info-native--opus-channel-mapping-table))))
   "Opus identification header specification.")
 
+(defconst emms-info-native--opus-head-magic-array
+  [79 112 117 115 72 101 97 100]
+  "Opus identification header magic pattern `OpusHead'.")
+
 (defconst emms-info-native--opus-channel-mapping-table
   '((stream-count u8)
     (coupled-count u8)
@@ -422,6 +303,132 @@ header.")
                    (struct 
emms-info-native--vorbis-comment-field-bindat-spec)))
   "Opus comment header specification.")
 
+(defconst emms-info-native--opus-tags-magic-array
+  [79 112 117 115 84 97 103 115]
+  "Opus comment header magic pattern `OpusTags'.")
+
+;;;; Ogg code
+
+(defconst emms-info-native--ogg-page-size 65307
+  "Maximum size for a single Ogg container page.")
+
+(defconst emms-info-native--ogg-page-bindat-spec
+  '((capture-pattern vec 4)
+    (eval (unless (equal last emms-info-native--ogg-magic-array)
+            (error "Ogg framing mismatch: expected `%s', got `%s'"
+                   emms-info-native--ogg-magic-array
+                   last)))
+    (stream-structure-version u8)
+    (eval (unless (= last 0)
+            (error ("Ogg version mismatch: expected 0, got %s")
+                   last)))
+    (header-type-flag u8)
+    (granule-position vec 8)
+    (stream-serial-number vec 4)
+    (page-sequence-no vec 4)
+    (page-checksum vec 4)
+    (page-segments u8)
+    (segment-table vec (page-segments))
+    (payload vec (eval (seq-reduce #'+ last 0))))
+  "Ogg page structure specification.")
+
+(defconst emms-info-native--ogg-magic-array
+  [79 103 103 83]
+  "Ogg format magic capture pattern `OggS'.")
+
+(defun emms-info-native--decode-ogg-comments (filename stream-type)
+  "Read and decode comments from Ogg file FILENAME.
+The file is assumed to contain a single stream of type
+STREAM-TYPE, which must either `vorbis' or `opus'.
+
+Return comments in a list of (FIELD . VALUE) cons cells.  See
+`emms-info-native--split-vorbis-comment' for details."
+  (let* ((packets (emms-info-native--decode-ogg-packets filename 2))
+         (headers (emms-info-native--decode-ogg-headers packets
+                                                        stream-type))
+         (comments (bindat-get-field headers
+                                     'comment-header
+                                     'user-comments)))
+    (emms-info-native--extract-vorbis-comments comments)))
+
+(defun emms-info-native--decode-ogg-packets (filename packets)
+  "Read and decode packets from Ogg file FILENAME.
+Read in data from the start of FILENAME, remove Ogg packet
+frames, and concatenate payloads until at least PACKETS number of
+packets have been decoded.  Return the decoded packets in a
+vector, concatenated.
+
+Data is read in `emms-info-native--ogg-page-size' chunks.  If the
+total length of concatenated packets becomes greater than
+`emms-info-native--max-peek-size', an error is signaled.
+
+Only elementary streams are supported, that is, FILENAME should
+contain only a single logical stream.  Note that this assumption
+is not verified: with non-elementary streams packets from
+different streams will be mixed together without an error."
+  (let ((num-packets 0)
+        (offset 0)
+        (stream (vector)))
+    (while (< num-packets packets)
+      (let ((page (emms-info-native--decode-ogg-page filename
+                                                     offset)))
+        (cl-incf num-packets (or (plist-get page :num-packets) 0))
+        (cl-incf offset (plist-get page :num-bytes))
+        (setq stream (vconcat stream (plist-get page :stream)))
+        (when (> (length stream) emms-info-native--max-peek-size)
+          (error "Ogg payload is too large"))))
+    stream))
+
+(defun emms-info-native--decode-ogg-page (filename offset)
+  "Read and decode a single Ogg page from FILENAME.
+Starting reading data from byte offset OFFSET.
+
+Return a plist (:num-packets N :num-bytes B :stream S), where N
+is the number of packets in the page, B is the size of the page
+in bytes, and S is the unframed logical bitstream in a vector.
+Note that N can be zero."
+  (with-temp-buffer
+    (set-buffer-multibyte nil)
+    (insert-file-contents-literally filename
+                                    nil
+                                    offset
+                                    (+ offset
+                                       emms-info-native--ogg-page-size))
+    (let* ((page (bindat-unpack emms-info-native--ogg-page-bindat-spec
+                                (buffer-string)))
+           (num-packets (emms-info-native--num-of-packets page))
+           (num-bytes (bindat-length emms-info-native--ogg-page-bindat-spec
+                                     page))
+           (stream (bindat-get-field page 'payload)))
+      (list :num-packets num-packets
+            :num-bytes num-bytes
+            :stream stream))))
+
+(defun emms-info-native--num-of-packets (page)
+  "Return the number of packets in Ogg page PAGE.
+PAGE must correspond to
+`emms-info-native--ogg-page-bindat-spec'."
+  ;; Every element that is less than 255 in the segment table
+  ;; represents a packet boundary.
+  (length (seq-filter (lambda (elt) (< elt 255))
+                      (bindat-get-field page 'segment-table))))
+
+(defun emms-info-native--decode-ogg-headers (packets stream-type)
+  "Decode first two stream headers from PACKETS for STREAM-TYPE.
+STREAM-TYPE must be either `vorbis' or `opus'.
+
+Return a structure that corresponds to either
+`emms-info-native--opus-headers-bindat-spec' or
+`emms-info-native--vorbis-headers-bindat-spec'."
+  (cond ((eq stream-type 'vorbis)
+         (bindat-unpack emms-info-native--vorbis-headers-bindat-spec
+                        packets))
+        ((eq stream-type 'opus)
+         (let (emms-info-native--opus-channel-count)
+           (bindat-unpack emms-info-native--opus-headers-bindat-spec
+                          packets)))
+        (t (error "Unknown stream type %s" stream-type))))
+
 ;;;; FLAC code
 
 (defconst emms-info-native--flac-metadata-block-header-bindat-spec
@@ -488,7 +495,7 @@ Return the comment block data in a vector."
                (block-type (logand flags #x7F)))
           (setq last-flag (> (logand flags #x80) 0))
           (when (> block-type 6)
-            (error "FLAC block type error: expected ≤ 6, got %s"
+            (error "FLAC block type error: expected <= 6, got %s"
                    block-type))
           (when (= block-type 4)
             ;; Comment block found, extract it.
@@ -499,15 +506,6 @@ Return the comment block data in a vector."
 
 ;;;; id3v2 (MP3) code
 
-(defvar emms-info-native--id3v2-version 0
-  "Last decoded id3v2 version.
-This is a kludge; it is needed because bindat spec cannot refer
-outside itself.")
-
-(defconst emms-info-native--id3v2-magic-array
-  [#x49 #x44 #x33]
-  "id3v2 header magic pattern `ID3'.")
-
 (defconst emms-info-native--id3v2-header-bindat-spec
   '((file-identifier vec 3)
     (eval (unless (equal last emms-info-native--id3v2-magic-array)
@@ -522,6 +520,10 @@ outside itself.")
     (size eval (emms-info-native--checked-id3v2-size 'tag last)))
   "id3v2 header specification.")
 
+(defconst emms-info-native--id3v2-magic-array
+  [#x49 #x44 #x33]
+  "id3v2 header magic pattern `ID3'.")
+
 (defconst emms-info-native--id3v2-frame-header-bindat-spec
   '((id str (eval (if (= emms-info-native--id3v2-version 2) 3 4)))
     (eval (unless (emms-info-native--valid-id3v2-frame-id-p last)
@@ -546,7 +548,7 @@ outside itself.")
     ("TDRC" . "date")
     ("TPA"  . "discnumber")
     ("TPOS" . "discnumber")
-    ("TCON" . "genre")
+    ("TCON" . genre)
     ("TPUB" . "label")
     ("TDOR" . "originaldate")
     ("TOR"  . "originalyear")
@@ -557,7 +559,8 @@ outside itself.")
     ("TRK"  . "tracknumber")
     ("TRCK" . "tracknumber")
     ("TYE"  . "year")
-    ("TYER" . "year"))
+    ("TYER" . "year")
+    ("TXXX" . user-defined))
   "Mapping from id3v2 frame identifiers to EMMS info fields.
 
 Sources:
@@ -565,6 +568,135 @@ Sources:
 - URL `https://picard-docs.musicbrainz.org/en/appendices/tag_mapping.html'
 - URL `http://wiki.hydrogenaud.io/index.php?title=Foobar2000:ID3_Tag_Mapping'")
 
+(defconst emms-info-native--id3v1-genres
+  '((0 . "Blues")
+    (1 . "Classic Rock")
+    (2 . "Country")
+    (3 . "Dance")
+    (4 . "Disco")
+    (5 . "Funk")
+    (6 . "Grunge")
+    (7 . "Hip-Hop")
+    (8 . "Jazz")
+    (9 . "Metal")
+    (10 . "New Age")
+    (11 . "Oldies")
+    (12 . "Other")
+    (13 . "Pop")
+    (14 . "R&B")
+    (15 . "Rap")
+    (16 . "Reggae")
+    (17 . "Rock")
+    (18 . "Techno")
+    (19 . "Industrial")
+    (20 . "Alternative")
+    (21 . "Ska")
+    (22 . "Death Metal")
+    (23 . "Pranks")
+    (24 . "Soundtrack")
+    (25 . "Euro-Techno")
+    (26 . "Ambient")
+    (27 . "Trip-Hop")
+    (28 . "Vocal")
+    (29 . "Jazz+Funk")
+    (30 . "Fusion")
+    (31 . "Trance")
+    (32 . "Classical")
+    (33 . "Instrumental")
+    (34 . "Acid")
+    (35 . "House")
+    (36 . "Game")
+    (37 . "Sound Clip")
+    (38 . "Gospel")
+    (39 . "Noise")
+    (40 . "AlternRock")
+    (41 . "Bass")
+    (42 . "Soul")
+    (43 . "Punk")
+    (44 . "Space")
+    (45 . "Meditative")
+    (46 . "Instrumental Pop")
+    (47 . "Instrumental Rock")
+    (48 . "Ethnic")
+    (49 . "Gothic")
+    (50 . "Darkwave")
+    (51 . "Techno-Industrial")
+    (52 . "Electronic")
+    (53 . "Pop-Folk")
+    (54 . "Eurodance")
+    (55 . "Dream")
+    (56 . "Southern Rock")
+    (57 . "Comedy")
+    (58 . "Cult")
+    (59 . "Gangsta")
+    (60 . "Top 40")
+    (61 . "Christian Rap")
+    (62 . "Pop/Funk")
+    (63 . "Jungle")
+    (64 . "Native American")
+    (65 . "Cabaret")
+    (66 . "New Wave")
+    (67 . "Psychadelic")
+    (68 . "Rave")
+    (69 . "Showtunes")
+    (70 . "Trailer")
+    (71 . "Lo-Fi")
+    (72 . "Tribal")
+    (73 . "Acid Punk")
+    (74 . "Acid Jazz")
+    (75 . "Polka")
+    (76 . "Retro")
+    (77 . "Musical")
+    (78 . "Rock & Roll")
+    (79 . "Hard Rock")
+    (80 . "Folk")
+    (81 . "Folk-Rock")
+    (82 . "National Folk")
+    (83 . "Swing")
+    (84 . "Fast Fusion")
+    (85 . "Bebob")
+    (86 . "Latin")
+    (87 . "Revival")
+    (88 . "Celtic")
+    (89 . "Bluegrass")
+    (90 . "Avantgarde")
+    (91 . "Gothic Rock")
+    (92 . "Progressive Rock")
+    (93 . "Psychedelic Rock")
+    (94 . "Symphonic Rock")
+    (95 . "Slow Rock")
+    (96 . "Big Band")
+    (97 . "Chorus")
+    (98 . "Easy Listening")
+    (99 . "Acoustic")
+    (100 . "Humour")
+    (101 . "Speech")
+    (102 . "Chanson")
+    (103 . "Opera")
+    (104 . "Chamber Music")
+    (105 . "Sonata")
+    (106 . "Symphony")
+    (107 . "Booty Bass")
+    (108 . "Primus")
+    (109 . "Porn Groove")
+    (110 . "Satire")
+    (111 . "Slow Jam")
+    (112 . "Club")
+    (113 . "Tango")
+    (114 . "Samba")
+    (115 . "Folklore")
+    (116 . "Ballad")
+    (117 . "Power Ballad")
+    (118 . "Rhythmic Soul")
+    (119 . "Freestyle")
+    (120 . "Duet")
+    (121 . "Punk Rock")
+    (122 . "Drum Solo")
+    (123 . "A cappella")
+    (124 . "Euro-House")
+    (125 . "Dance Hall"))
+  "id3v1 genres.")
+
 (defconst emms-info-native--id3v2-text-encodings
   '((0 . latin-1)
     (1 . utf-16)
@@ -572,6 +704,38 @@ Sources:
     (3 . utf-8))
   "id3v2 text encodings.")
 
+(defun emms-info-native--valid-id3v2-frame-id-p (id)
+  "Return t if ID is a proper id3v2 frame identifier, nil otherwise."
+  (if (= emms-info-native--id3v2-version 2)
+      (string-match "[A-Z0-9]\\{3\\}" id)
+    (string-match "[A-Z0-9]\\{4\\}" id)))
+
+(defun emms-info-native--checked-id3v2-size (elt bytes)
+  "Calculate id3v2 element ELT size from BYTES.
+ELT must be either 'tag or 'frame.
+
+Return the size.  Signal an error if the size is zero."
+  (let ((size (cond ((eq elt 'tag)
+                     (emms-info-native--decode-id3v2-size bytes t))
+                    ((eq elt 'frame)
+                     (if (= emms-info-native--id3v2-version 4)
+                         (emms-info-native--decode-id3v2-size bytes t)
+                       (emms-info-native--decode-id3v2-size bytes nil))))))
+    (if (zerop size)
+        (error "id3v2 tag/frame size is zero")
+      size)))
+
+(defun emms-info-native--decode-id3v2-size (bytes syncsafe)
+  "Decode id3v2 element size from BYTES.
+Depending on SYNCSAFE, BYTES are interpreted as 7- or 8-bit
+bytes, MSB first.
+
+Return the decoded size."
+  (let ((num-bits (if syncsafe 7 8)))
+    (apply '+ (seq-map-indexed (lambda (elt idx)
+                                 (* (expt 2 (* num-bits idx)) elt))
+                               (reverse bytes)))))
+
 (defun emms-info-native--decode-id3v2 (filename)
   "Read and decode id3v2 metadata from FILENAME.
 Return metadata in a list of (FIELD . VALUE) cons cells, or nil
@@ -611,37 +775,10 @@ Return the size.  Signal an error if the size is zero."
     (insert-file-contents-literally filename nil 10 14)
     (emms-info-native--checked-id3v2-size 'frame (buffer-string))))
 
-(defun emms-info-native--checked-id3v2-size (elt bytes)
-  "Calculate id3v2 element ELT size from BYTES.
-ELT must be either 'tag or 'frame.
-
-Return the size.  Signal an error if the size is zero."
-  (let ((size (cond ((eq elt 'tag)
-                     (emms-info-native--decode-id3v2-size bytes t))
-                    ((eq elt 'frame)
-                     (if (= emms-info-native--id3v2-version 4)
-                         (emms-info-native--decode-id3v2-size bytes t)
-                       (emms-info-native--decode-id3v2-size bytes nil))))))
-    (if (zerop size)
-        (error "id3v2 tag/frame size is zero")
-      size)))
-
-(defun emms-info-native--decode-id3v2-size (bytes syncsafe)
-  "Decode id3v2 element size from BYTES.
-Depending on SYNCSAFE, BYTES are interpreted as 7- or 8-bit
-bytes, MSB first.
-
-Return the decoded size."
-  (let ((num-bits (if syncsafe 7 8)))
-    (apply '+ (seq-map-indexed (lambda (elt idx)
-                                 (* (expt 2 (* num-bits idx)) elt))
-                               (reverse bytes)))))
-
 (defun emms-info-native--decode-id3v2-frames (filename begin end unsync)
   "Read and decode id3v2 text frames from FILENAME.
-BEGIN should be the offset of first byte after id3v2 header and
-extended header (if any), and END should be the offset after the
-complete id3v2 tag.
+BEGIN should be the offset of first byte of the first frame, and
+END should be the offset after the complete id3v2 tag.
 
 If UNSYNC is t, the frames are assumed to have gone through
 unsynchronization and decoded as such.
@@ -652,25 +789,13 @@ Return metadata in a list of (FIELD . VALUE) cons cells."
         comments)
     (condition-case nil
         (while (< offset limit)
-          (let* ((header (emms-info-native--decode-id3v2-frame-header filename
-                                                                      offset))
-                 (info-id (emms-info-native--id3v2-frame-info-id header))
-                 (decoded-size (bindat-get-field (cdr header) 'size)))
-            (setq offset (car header))  ;advance to frame data begin
-            (if (or unsync info-id)
-                ;; Note that if unsync is t, we have to always read a
-                ;; frame to gets its true size so that we can adjust
-                ;; offset correctly.
-                (let ((data (emms-info-native--read-id3v2-frame-data filename
-                                                                     offset
-                                                                     
decoded-size
-                                                                     unsync)))
-                  (setq offset (car data))
-                  (when info-id
-                    (let ((value (emms-info-native--decode-id3v2-string (cdr 
data))))
-                      (push (cons info-id value) comments))))
-              ;; Skip the frame.
-              (cl-incf offset decoded-size))))
+          (let* ((frame-data (emms-info-native--decode-id3v2-frame filename
+                                                                   offset
+                                                                   unsync))
+                 (next-frame-offset (car frame-data))
+                 (comment (cdr frame-data)))
+            (when comment (push comment comments))
+            (setq offset next-frame-offset)))
       (error nil))
     comments))
 
@@ -678,11 +803,25 @@ Return metadata in a list of (FIELD . VALUE) cons cells."
   "Return the last decoded header size in bytes."
   (if (= emms-info-native--id3v2-version 2) 6 10))
 
-(defun emms-info-native--valid-id3v2-frame-id-p (id)
-  "Return t if ID is a proper id3v2 frame identifier, nil otherwise."
-  (if (= emms-info-native--id3v2-version 2)
-      (string-match "[A-Z0-9]\\{3\\}" id)
-    (string-match "[A-Z0-9]\\{4\\}" id)))
+(defun emms-info-native--decode-id3v2-frame (filename offset unsync)
+  (let* ((header (emms-info-native--decode-id3v2-frame-header filename
+                                                              offset))
+         (info-id (emms-info-native--id3v2-frame-info-id header))
+         (data-offset (car header))
+         (size (bindat-get-field (cdr header) 'size)))
+    (if (or info-id unsync)
+        ;; Note that if unsync is t, we have to always read the frame
+        ;; to determine next-frame-offset.
+        (let* ((data (emms-info-native--read-id3v2-frame-data filename
+                                                              data-offset
+                                                              size
+                                                              unsync))
+               (next-frame-offset (car data))
+               (value (emms-info-native--decode-id3v2-frame-data (cdr data)
+                                                                 info-id)))
+          (cons next-frame-offset value))
+      ;; Skip the frame.
+      (cons (+ data-offset size) nil))))
 
 (defun emms-info-native--decode-id3v2-frame-header (filename begin)
   "Read and decode id3v2 frame header from FILENAME.
@@ -697,6 +836,12 @@ offset after the frame header, and FRAME is the decoded 
frame."
       (cons end (bindat-unpack emms-info-native--id3v2-frame-header-bindat-spec
                                (buffer-string))))))
 
+(defun emms-info-native--id3v2-frame-info-id (frame)
+  "Return the emms-info identifier for FRAME.
+If there is no such identifier, return nil."
+  (cdr (assoc (bindat-get-field frame 'id)
+              emms-info-native--id3v2-frame-to-info)))
+
 (defun emms-info-native--read-id3v2-frame-data (filename
                                                 begin
                                                 num-bytes
@@ -728,11 +873,36 @@ data."
         (insert-file-contents-literally filename nil begin end)
         (cons end (buffer-string))))))
 
-(defun emms-info-native--id3v2-frame-info-id (frame)
-  "Return the emms-info identifier for FRAME.
-If there is no such identifier, return nil."
-  (cdr (assoc (bindat-get-field frame 'id)
-              emms-info-native--id3v2-frame-to-info)))
+(defun emms-info-native--decode-id3v2-frame-data (data info-id)
+  "Decode id3v2 text frame data DATA.
+If INFO-ID is `user-defined', assume that DATA is a TXXX frame
+with key/value-pair.  Extract the key and, if it is a mapped
+element in `emms-info-native--id3v2-frame-to-info', use it as
+INFO-ID.
+
+If INFO-ID is `genre', assume that DATA is either an integral
+id3v1 genre reference or a plain genre string.  In the former
+case map the reference to a string via
+`emms-info-native--id3v1-genres'; in the latter case use the
+genre string verbatim.
+
+Return a cons cell (INFO-ID . VALUE) where VALUE is the decoded
+string."
+  (when info-id
+    (let ((str (emms-info-native--decode-id3v2-string data)))
+      (cond ((stringp info-id) (cons info-id str))
+            ((eq info-id 'genre)
+             (if (string-match "^(?\\([0-9]+\\))?" str)
+                 (let ((v1-genre (assoc (string-to-number (match-string 1 str))
+                                        emms-info-native--id3v1-genres)))
+                   (when v1-genre (cons "genre" (cdr v1-genre))))
+               (cons "genre" str)))
+            ((eq info-id 'user-defined)
+             (let* ((key-val (split-string str (string 0)))
+                    (key (downcase (car key-val)))
+                    (val (cadr key-val)))
+               (when (rassoc key emms-info-native--id3v2-frame-to-info)
+                 (cons key val))))))))
 
 (defun emms-info-native--decode-id3v2-string (bytes)
   "Decode id3v2 text information from BYTES.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]