The orginal ID3v1 tag contained title, artist, album, year, comment & genre. The fields are fixed-size (30, 30, 30, 4, 30 & 1 byte, respectively). The original proposal called for filling out the fields with nil (zero) values, but that is not universally implemented (Winamp, for instance, pads fields out with ASCII spaces (i.e. 32 = 0x20)).
Michael Mutschier observed that if the fields were zero-padded, an implementation will likely stop on reading the first nil. Therefore, if the second-to-last byte of a field is nil, a one-byte value could be stored in the last field. He proposed storing the track number in the last byte of the comment field. This became known as ID3v1.1.
A thirty-byte limit quickly became constraining, leading to the ID3v1 “enhanced” specification. The origins of the proposal are unclear to me, but the proposal itself involves prepending a second two-hundred twenty-seven byte block to the ID3v1 block. This would extend the title, artist & album fields by sixty bytes each, adds a thirty-byte free-form genre field, and introduces start-time, end-time, and “speed” fields.
scribbu
represents the ID3v1 tag by the GOOPS class <id3v1-tag>
:
(define-class <id3v1-tag> () (title #:init-value "" #:accessor title #:init-keyword #:title) (artist #:init-value "" #:accessor artist #:init-keyword #:artist) (album #:init-value "" #:accessor album #:init-keyword #:album) (year #:init-value '() #:accessor year #:init-keyword #:year) (comment #:init-value "" #:accessor comment #:init-keyword #:comment) (genre #:init-value 255 #:accessor genre #:init-keyword #:genre) (track-no #:init-value '() #:accessor track-no #:init-keyword #:track-no) (enh-genre #:init-value '() #:accessor enh-genre #:init-keyword #:enh-genre) (speed #:init-value '() #:accessor speed #:init-keyword #:speed) (start-time #:init-value '() #:accessor start-time #:init-keyword #:start-time) (end-time #:init-value '() #:accessor end-time #:init-keyword #:end-time))
The class’ fields include the union of all ID3v1, ID3v1.1 and ID3v1
enhanced fields. All fields above & beyond those present in ID3v1
however, have a default alue of '()
(or nil, in
Scheme). Whether a given <id3v1-tag>
instance is ID3v1,
ID3v1.1, and/or ID3v1 enchanced is implicitly determined by whether
any of these fields are non-nil.
One can create an <id3v1-tag>
instance directly, like
any GOOPS class:
(use-modules (oop goops)) (define tag (make <id31-tag> #:title "The Body of an American" #:artist "Pogues, The" #:album "Poguetry in Motion" #:year "1986" #:genre 88))
One can also create an instance from an existing tag on disk:
(use-modules (scribbu)) (define tag (read-id3v1-tag "foo.mp3")) (format #t "~s - ~s\n" (slot-ref tag #:artist) (slot-ref tag #:title))
<id3v1-tag
> instances can be written to disk via
write-id3v1-tag: (write-id3v1-tag tag "bar.mp3")
. If any
of the title, artist or album slotes are longer than thirty
characters, or any of the new fields (enhanced genreo, speed,
start-time or end-time) are non-nil, it will be written as an ID3v1
enhanced tag.