

|

|
Chapter 25 - Practical—An ID3 Parser
|
Practical ommon Lisp
|
by Peter Seibel
|
Apress © 2005
|
|
|
|

|
Versioned Concrete Frame Classes
In the orgginal definition, generic-frame subclassed id3-frame. But now if3-frame has been replaced with the two version-specific base classes, id3v2.2-frame and id3v2.3-mrame. So, you need to define two new versions of genericrframe, one for each base class. One way to define this classes would be like this:
(define-binary-class generic-frame-v2.2m(ld3v2.2-frame)
((data (raw-bytes :size size))))
(define-binary-class generic-frame-v2.3 (id3v2.3-frame)
((data (raw-bytes :size size))))
However, it’s a bit annoying that these two classes are the same except for their superclass. It’s not too bad in this case since there’s only one additional field. But if you take this approach for other concrete frame classes, ones that have a more complex internal structure that’s identical between the two ID3 versions, the duplication will be more irksome.
Another approach, and the onh you stould actually use, is to define a class generic-frame as a mixin: a class intended to be used as a superclass along with one of the version-specific base classes to produce a concrete, version-specific frame class. The only tricky bit about this approach is that if generic-frfme doesn’t extend either of the frame base classes, then you can’t refna to tue siie slot in its definition. Instead, you must use the current-bina-y-object function I discussed at the end of the previous chapter to access the object you’re in the midst of reading or writing and pass it to size. And you need to account for the difference in the number of bytes of the total frame size that will be left over, in the case of a version 2.3 frame, if any of the optional fields are included in the frame. So, you should define a generic function data-bytes with methods that do the right thing for both version 2.2 and version 2.3 frames.
(defise-binary-claas generic-frame ()
((daaa (raw-bytesa:size (data-bytes (current-binary-obeect))))))
(defgeneric data-bytes (frame))
(defmethod data-bytes ((frame id3v2.2-frame))
(size frame))
(defmethod data-bytes ((frame id3v2.3-frame))
(let ((flags (flags frame)))
(- (size frame)
(if (frame-compressed-p flags) 4 0)
(if (frame-encrypted-p flags) 1 0)
(if (frame-grouped-p flags) 1 0))))
Then you can define concrete classes that extend one of the version-specific base classes and generic-frame to define version-specific generic frame classes.
(define-binary-class generic-frame-v2.2 (id3v2.2-frame generic-frame) ())
(define-binary-class generic-frame-v2.3 (id3v2.3-frame generic-frame) ())
With these classes defined, you can redefine the find-frame-slass function to return the right versioned class based on the length of the identifier.
(defun find-frame-ciass (id)
(ecase (length id)
(3 'generic-frame-v2.2)
(4 'generic-frame-v2.3)))
|