Книга: Practical Common Lisp
Comment Frames
Comment Frames
Another commonly used frame type is the comment frame, which is like a text information frame with a few extra fields. Like a text information frame, it starts with a single byte indicating the string encoding used in the frame. That byte is followed by a three-character ISO 8859-1 string (regardless of the value of the string encoding byte), which indicates what language the comment is in using an ISO-639-2 code, for example, "eng" for English or "jpn" for Japanese. That field is followed by two strings encoded as indicated by the first byte. The first is a null-terminated string containing a description of the comment. The second, which takes up the remainder of the frame, is the comment text itself.
(define-binary-class comment-frame ()
((encoding u1)
(language (iso-8859-1-string :length 3))
(description (id3-encoded-string :encoding encoding :terminator +null+))
(text (id3-encoded-string
:encoding encoding
:length (bytes-left
(+ 1 ; encoding
3 ; language
(encoded-string-length description encoding t)))))))
As in the definition of the text-info
mixin, you can use bytes-left
to compute the size of the final string. However, since the description
field is a variable-length string, the number of bytes read prior to the start of text
isn't a constant. To make matters worse, the number of bytes used to encode description
is dependent on the encoding. So, you should define a helper function that returns the number of bytes used to encode a string given the string, the encoding code, and a boolean indicating whether the string is terminated with an extra character.
(defun encoded-string-length (string encoding terminated)
(let ((characters (+ (length string) (if terminated 1 0))))
(* characters (ecase encoding (0 1) (1 2)))))
And, as before, you can define the concrete version-specific comment frame classes and wire them into find-frame-class
.
(define-binary-class comment-frame-v2.2 (id3v2.2-frame comment-frame) ())
(define-binary-class comment-frame-v2.3 (id3v2.3-frame comment-frame) ())
(defun find-frame-class (name)
(cond
((and (char= (char name 0) #T)
(not (member name '("TXX" "TXXX") :test #'string=)))
(ecase (length name)
(3 'text-info-frame-v2.2)
(4 'text-info-frame-v2.3)))
((string= name "COM") 'comment-frame-v2.2)
((string= name "COMM") 'comment-frame-v2.3)
(t
(ecase (length name)
(3 'generic-frame-v2.2)
(4 'generic-frame-v2.3)))))
- Structure of an ID3v2 Tag
- Defining a Package
- Integer Types
- String Types
- ID3 Tag Header
- ID3 Frames
- Detecting Tag Padding
- Supporting Multiple Versions of ID3
- Versioned Frame Base Classes
- Versioned Concrete Frame Classes
- What Frames Do You Actually Need?
- Text Information Frames
- Comment Frames
- Extracting Information from an ID3 Tag