anchor.diff:             (loop for i from 0 below length
                 do (write-value type out (char value i)))))
   
  -(define-binary-type general-terminate-string (termintor type)
  +(define-binary-type general-terminate-string (terminator type)
     (:reader (in)
              (with-output-to-string (str)
                (loop for char = (code-char (read-byte in))
  -                until (char= char termintor)
  +                until (char= char terminator)
                   do (write-char char str))))
     (:writer (out val)
              (with-input-from-string (str val)
                (loop for char = (read-char str)
  -                until (char= char termintor)
  +                until (char= char terminator)
                   do (write-byte (char-code char) out)
  -                finally (write-byte (char-code termintor) out)))))
  +                finally (write-byte (char-code terminator) out)))))
   
   (define-binary-type iso-8859-1 ()
     (:reader (in)
  
positive.code:              (write-value 'general-string out val
                            :length (length val)
                            :type (ucs-2-char-type #xfffe)))))
  
  (define-binary-type iso-8859-1-string (length)
    (:reader (in)
  
negative.code:    (extra-flags (optional :type 'u2 :if (extended-p flags)))
     (padding-size (optional :type 'u4 :if (extended-p flags)))
     (crc (optional :type 'u4 :if (crc-p flags extra-flags)))
     (frames (id3-frames :size tag-size :frame-type 'id3-v2.3-frame))))
  
  (defun extended-p (flag)
    (logbitp 6 flag))
  
  (defun crc-p (flags extra)
    (and (extended-p flags) (logbitp 15 extra)))
  
  (defgeneric frame-header-size (frame))
  
  (defun frame-types (file)
    (delete-duplicates (mapcar #'id (frames (read-id3 file))) :test #'string=))
  
  (defun non-terminated-type (encoding)
    (ecase encoding
      (0 'iso-8859-1-string)
      (1 'ucs-2-string)))
  
  (defun terminated-type (encoding)
    (ecase encoding
      (0 'iso-8859-1-terminated-string)
      (1 'ucs-2-terminated-string)))
  
  (defun string-args (encoding length terminator)
    (cond
      (length
       (values (non-terminated-type encoding) :length length))
      (terminator
       (values (terminated-type encoding) :terminator terminator))))
  
  (define-binary-type id3-encoded-string (encoding length terminator)
    (:reader (in)
             (multiple-value-bind (type keyword arg)
                 (string-args encoding length terminator)
               (read-value type in keyword arg)))
    (:writer (out val)
             (multiple-value-bind (type keyword arg)
                 (string-args encoding length terminator)
               (write-value type out val keyword arg))))
  
  (define-binary-class text-info-frame ()
    ((encoding u1)
     (information (id3-encoded-string :encoding encoding :length (bytes-left 1)))))
  
  (defun bytes-left (bytes-read)
    (- (size (current-binary-object)) bytes-read))
  
  (define-binary-class text-info-frame-v2.2 (id3-v2.2-frame text-info-frame) ())
  (define-binary-class text-info-frame-v2.3 (id3-v2.3-frame text-info-frame) ())
  
  (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 3 (encoded-string-length description encoding t)))))))
  
  (defun encoded-string-length (string encoding terminated)
    (let ((characters (+ (length string)
                         (if terminated 1 0)
                         (ecase encoding (0 0) (1 1)))))
      (* characters (ecase encoding (0 1) (1 2)))))
  
  (define-binary-class comment-frame-v2.2 (id3-v2.2-frame comment-frame) ())
  (define-binary-class comment-frame-v2.3 (id3-v2.3-frame comment-frame) ())
  
  (defun find-frame (id3 ids)
    (find-if #'(lambda (x) (find (id x) ids :test #'string=)) (frames id3)))
  
  (defun get-text-info (id3 &rest ids)
    (let ((frame (find-frame id3 ids)))
      (when frame (information frame))))
  
  (defun song   (id3) (get-text-info id3 "TIT2"))
  (defun album  (id3) (get-text-info id3 "TAL" "TALB"))
  (defun artist (id3) (get-text-info id3 "TP1" "TPE1"))
  (defun track  (id3) (get-text-info id3 "TRK" "TRCK"))
  (defun year   (id3) (get-text-info id3 "TYE" "TYER" "TDRC"))
  (defun genre  (id3) (get-text-info id3 "TCO" "TCON"))
  
  (defun all-info (id3)
    (let ((f-list '(song album artist track year genre)))
      (mapcar #'(lambda (f)
                          (format t "~a~15t~a~%" (symbol-name f)
                                  (funcall (symbol-function f) id3)))
              f-list)))
  
  (defun translated-genre (id3)
    (let ((genre (genre id3)))
      (if (and genre (char= #\( (char genre 0)))
          (translate-v1-genre genre))))
  
  (defun translate-v1-genre (genre)
    (aref *id3-v1-genres* (parse-integer genre :start 1 :junk-allowed t)))
  
  (defparameter *id3-v1-genres*
    #(
      ;; These are the official ID3v1 genres.
      "Blues" "Classic Rock" "Country" "Dance" "Disco" "Funk" "Grunge"
      "Hip-Hop" "Jazz" "Metal" "New Age" "Oldies" "Other" "Pop" "R&B" "Rap"
      "Reggae" "Rock" "Techno" "Industrial" "Alternative" "Ska"
      "Death Metal" "Pranks" "Soundtrack" "Euro-Techno" "Ambient"
      "Trip-Hop" "Vocal" "Jazz+Funk" "Fusion" "Trance" "Classical"
      "Instrumental" "Acid" "House" "Game" "Sound Clip" "Gospel" "Noise"
      "AlternRock" "Bass" "Soul" "Punk" "Space" "Meditative"
      "Instrumental Pop" "Instrumental Rock" "Ethnic" "Gothic" "Darkwave"
      "Techno-Industrial" "Electronic" "Pop-Folk" "Eurodance" "Dream"
      "Southern Rock" "Comedy" "Cult" "Gangsta" "Top 40" "Christian Rap"
      "Pop/Funk" "Jungle" "Native American" "Cabaret" "New Wave"
      "Psychadelic" "Rave" "Showtunes" "Trailer" "Lo-Fi" "Tribal"
      "Acid Punk" "Acid Jazz" "Polka" "Retro" "Musical" "Rock & Roll"
      "Hard Rock"
  
      ;; These were made up by the authors of Winamp but backported into
      ;; the ID3 spec.
      "Folk" "Folk-Rock" "National Folk" "Swing" "Fast Fusion"
      "Bebob" "Latin" "Revival" "Celtic" "Bluegrass" "Avantgarde"
      "Gothic Rock" "Progressive Rock" "Psychedelic Rock" "Symphonic Rock"
      "Slow Rock" "Big Band" "Chorus" "Easy Listening" "Acoustic" "Humour"
      "Speech" "Chanson" "Opera" "Chamber Music" "Sonata" "Symphony"
      "Booty Bass" "Primus" "Porn Groove" "Satire" "Slow Jam" "Club"
      "Tango" "Samba" "Folklore" "Ballad" "Power Ballad" "Rhythmic Soul"
      "Freestyle" "Duet" "Punk Rock" "Drum Solo" "A capella" "Euro-House"
      "Dance Hall"
  
      ;; These were also invented by the Winamp folks but ignored by the
      ;; ID3 authors.
      "Goa" "Drum & Bass" "Club-House" "Hardcore" "Terror" "Indie"
      "BritPop" "Negerpunk" "Polsk Punk" "Beat" "Christian Gangsta Rap"
      "Heavy Metal" "Black Metal" "Crossover" "Contemporary Christian"
      "Christian Rock" "Merengue" "Salsa" "Thrash Metal" "Anime" "Jpop"
      "Synthpop"))
  