id3.lisp 64 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379
  1. ;;; -*- Mode: Lisp; show-trailing-whitespace: t; Base: 10; indent-tabs: nil; Syntax: ANSI-Common-Lisp; Package: ID3; -*-
  2. ;;; Copyright (c) 2013, Mark VandenBrink. All rights reserved.
  3. (in-package #:id3)
  4. ;;;; ID3 string encoding support
  5. (defun id3-read-string (instream &key (len nil) (encoding 0))
  6. "Read in a string of a given encoding of length 'len'. Encoding
  7. is from the ID3 'spec'"
  8. (declare #.utils:*standard-optimize-settings*)
  9. (if (and len (<= len 0))
  10. nil
  11. (ecase encoding
  12. (0 (stream-read-iso-string instream len))
  13. (1 (stream-read-ucs-string instream :len len :kind :ucs-2))
  14. (2 (stream-read-ucs-string instream :len len :kind :ucs-2be))
  15. (3 (stream-read-utf-8-string instream len)))))
  16. (defun id3-decode-string (octets &key (encoding 0)
  17. (start 0)
  18. (end (length octets)))
  19. "Decode a string of a given encoding of length 'len'. Encoding
  20. is from the ID3 'spec'"
  21. (declare #.utils:*standard-optimize-settings*)
  22. (ecase encoding
  23. (0 (flex:octets-to-string octets :external-format :iso-8859-1 :start start :end end))
  24. (1 (flex:octets-to-string octets :external-format :ucs-2 :start start :end end))
  25. (2 (flex:octets-to-string octets :external-format :ucs-2be :start start :end end))
  26. (3 (flex:octets-to-string octets :external-format :utf-8 :start start :end end))))
  27. ;;;; V22 frame types:
  28. (defparameter *v22-frame-names* '(("BUF" "Recommended buffer size")
  29. ("CNT" "Play counter")
  30. ("COM" "Comments")
  31. ("CRA" "Audio encryption")
  32. ("CRM" "Encrypted meta frame")
  33. ("EQU" "Equalization")
  34. ("ETC" "Event timing codes")
  35. ("GEO" "General encapsulated object")
  36. ("IPL" "Involved people list")
  37. ("LNK" "Linked information")
  38. ("MCI" "Music CD Identifier")
  39. ("MLL" "MPEG location lookup table")
  40. ("PIC" "Attached picture")
  41. ("POP" "Popularimeter")
  42. ("REV" "Reverb")
  43. ("RVA" "Relative volume adjustment")
  44. ("SLT" "Synchronized lyric/text")
  45. ("STC" "Synced tempo codes")
  46. ("TAL" "Album/Movie/Show title")
  47. ("TBP" "BPM (Beats Per Minute)")
  48. ("TCM" "Composer")
  49. ("TCO" "Content type")
  50. ("TCR" "Copyright message")
  51. ("TDA" "Date")
  52. ("TDY" "Playlist delay")
  53. ("TEN" "Encoded by")
  54. ("TFT" "File type")
  55. ("TIM" "Time")
  56. ("TKE" "Initial key")
  57. ("TLA" "Language(s)")
  58. ("TLE" "Length")
  59. ("TMT" "Media type")
  60. ("TOA" "Original artist(s)/performer(s)")
  61. ("TOF" "Original filename")
  62. ("TOL" "Original Lyricist(s)/text writer(s)")
  63. ("TOR" "Original release year")
  64. ("TOT" "Original album/Movie/Show title")
  65. ("TP1" "Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group")
  66. ("TP2" "Band/Orchestra/Accompaniment")
  67. ("TP3" "Conductor/Performer refinement")
  68. ("TP4" "Interpreted, remixed, or otherwise modified by")
  69. ("TPA" "Part of a set")
  70. ("TPB" "Publisher")
  71. ("TRC" "ISRC (International Standard Recording Code)")
  72. ("TRD" "Recording dates")
  73. ("TRK" "Track number/Position in set")
  74. ("TSI" "Size")
  75. ("TSS" "Software/hardware and settings used for encoding")
  76. ("TT1" "Content group description")
  77. ("TT2" "Title/Songname/Content description")
  78. ("TT3" "Subtitle/Description refinement")
  79. ("TXT" "Lyricist/text writer")
  80. ("TXX" "User defined text information frame")
  81. ("TYE" "Year")
  82. ("UFI" "Unique file identifier")
  83. ("ULT" "Unsychronized lyric/text transcription")
  84. ("WAF" "Official audio file webpage")
  85. ("WAR" "Official artist/performer webpage")
  86. ("WAS" "Official audio source webpage")
  87. ("WCM" "Commercial information")
  88. ("WCP" "Copyright/Legal information")
  89. ("WPB" "Publishers official webpage")
  90. ("WXX" "User defined URL link frame")))
  91. ;;;; V23 frame names
  92. (defparameter *v23-frame-names* '(("AENC" "Audio encryption")
  93. ("APIC" "Attached picture")
  94. ("COMM" "Comments")
  95. ("COMR" "Commercial frame")
  96. ("ENCR" "Encryption method registration")
  97. ("EQUA" "Equalization")
  98. ("ETCO" "Event timing codes")
  99. ("GEOB" "General encapsulated object")
  100. ("GRID" "Group identification registration")
  101. ("IPLS" "Involved people list")
  102. ("LINK" "Linked information")
  103. ("MCDI" "Music CD identifier")
  104. ("MLLT" "MPEG location lookup table")
  105. ("OWNE" "Ownership frame")
  106. ("PCNT" "Play counter")
  107. ("POPM" "Popularimeter")
  108. ("POSS" "Position synchronisation frame")
  109. ("PRIV" "Private frame")
  110. ("RBUF" "Recommended buffer size")
  111. ("RVAD" "Relative volume adjustment")
  112. ("RVRB" "Reverb")
  113. ("SYLT" "Synchronized lyric/text")
  114. ("SYTC" "Synchronized tempo codes")
  115. ("TALB" "Album/Movie/Show title")
  116. ("TBPM" "BPM (beats per minute)")
  117. ("TCOM" "Composer")
  118. ("TCON" "Content type")
  119. ("TCOP" "Copyright message")
  120. ("TDAT" "Date")
  121. ("TDLY" "Playlist delay")
  122. ("TENC" "Encoded by")
  123. ("TEXT" "Lyricist/Text writer")
  124. ("TFLT" "File type")
  125. ("TIME" "Time")
  126. ("TIT1" "Content group description")
  127. ("TIT2" "Title/songname/content description")
  128. ("TIT3" "Subtitle/Description refinement")
  129. ("TKEY" "Initial key")
  130. ("TLAN" "Language(s)")
  131. ("TLEN" "Length")
  132. ("TMED" "Media type")
  133. ("TOAL" "Original album/movie/show title")
  134. ("TOFN" "Original filename")
  135. ("TOLY" "Original lyricist(s)/text writer(s)")
  136. ("TOPE" "Original artist(s)/performer(s)")
  137. ("TORY" "Original release year")
  138. ("TOWN" "File owner/licensee")
  139. ("TPE1" "Lead performer(s)/Soloist(s)")
  140. ("TPE2" "Band/orchestra/accompaniment")
  141. ("TPE3" "Conductor/performer refinement")
  142. ("TPE4" "Interpreted, remixed, or otherwise modified by")
  143. ("TPOS" "Part of a set")
  144. ("TPUB" "Publisher")
  145. ("TRCK" "Track number/Position in set")
  146. ("TRDA" "Recording dates")
  147. ("TRSN" "Internet radio station name")
  148. ("TRSO" "Internet radio station owner")
  149. ("TSIZ" "Size")
  150. ("TSRC" "ISRC (international standard recording code)")
  151. ("TSSE" "Software/Hardware and settings used for encoding")
  152. ("TXXX" "User defined text information frame")
  153. ("TYER" "Year")
  154. ("UFID" "Unique file identifier")
  155. ("USER" "Terms of use")
  156. ("USLT" "Unsychronized lyric/text transcription")
  157. ("WCOM" "Commercial information")
  158. ("WCOP" "Copyright/Legal information")
  159. ("WOAF" "Official audio file webpage")
  160. ("WOAR" "Official artist/performer web page")
  161. ("WOAS" "Official audio source webpage")
  162. ("WORS" "Official internet radio station homepage")
  163. ("WPAY" "Payment")
  164. ("WPUB" "Publishers official webpage")
  165. ("WXXX" "User defined URL link frame")))
  166. (defparameter *v24-frame-names* '(("AENC" "Audio encryption")
  167. ("APIC" "Attached picture")
  168. ("ASPI" "Audio seek point index")
  169. ("COMM" "Comments")
  170. ("COMR" "Commercial frame")
  171. ("ENCR" "Encryption method registration")
  172. ("EQU2" "Equalisation (2)")
  173. ("ETCO" "Event timing codes")
  174. ("GEOB" "General encapsulated object")
  175. ("GRID" "Group identification registration")
  176. ("LINK" "Linked information")
  177. ("MCDI" "Music CD identifier")
  178. ("MLLT" "MPEG location lookup table")
  179. ("OWNE" "Ownership frame")
  180. ("PCNT" "Play counter")
  181. ("POPM" "Popularimeter")
  182. ("POSS" "Position synchronisation frame")
  183. ("PRIV" "Private frame")
  184. ("RBUF" "Recommended buffer size")
  185. ("RVA2" "Relative volume adjustment (2)")
  186. ("RVRB" "Reverb")
  187. ("SEEK" "Seek frame")
  188. ("SIGN" "Signature frame")
  189. ("SYLT" "Synchronised lyric/text")
  190. ("SYTC" "Synchronised tempo codes")
  191. ("TALB" "Album/Movie/Show title")
  192. ("TBPM" "BPM (beats per minute)")
  193. ("TCOM" "Composer")
  194. ("TCON" "Content type")
  195. ("TCOP" "Copyright message")
  196. ("TDEN" "Encoding time")
  197. ("TDLY" "Playlist delay")
  198. ("TDOR" "Original release time")
  199. ("TDRC" "Recording time")
  200. ("TDRL" "Release time")
  201. ("TDTG" "Tagging time")
  202. ("TENC" "Encoded by")
  203. ("TEXT" "Lyricist/Text writer")
  204. ("TFLT" "File type")
  205. ("TIPL" "Involved people list")
  206. ("TIT1" "Content group description")
  207. ("TIT2" "Title/songname/content description")
  208. ("TIT3" "Subtitle/Description refinement")
  209. ("TKEY" "Initial key")
  210. ("TLAN" "Language(s)")
  211. ("TLEN" "Length")
  212. ("TMCL" "Musician credits list")
  213. ("TMED" "Media type")
  214. ("TMOO" "Mood")
  215. ("TOAL" "Original album/movie/show title")
  216. ("TOFN" "Original filename")
  217. ("TOLY" "Original lyricist(s)/text writer(s)")
  218. ("TOPE" "Original artist(s)/performer(s)")
  219. ("TOWN" "File owner/licensee")
  220. ("TPE1" "Lead performer(s)/Soloist(s)")
  221. ("TPE2" "Band/orchestra/accompaniment")
  222. ("TPE3" "Conductor/performer refinement")
  223. ("TPE4" "Interpreted, remixed, or otherwise modified by")
  224. ("TPOS" "Part of a set")
  225. ("TPRO" "Produced notice")
  226. ("TPUB" "Publisher")
  227. ("TRCK" "Track number/Position in set")
  228. ("TRSN" "Internet radio station name")
  229. ("TRSO" "Internet radio station owner")
  230. ("TSOA" "Album sort order")
  231. ("TSOP" "Performer sort order")
  232. ("TSOT" "Title sort order")
  233. ("TSRC" "ISRC (international standard recording code)")
  234. ("TSSE" "Software/Hardware and settings used for encoding")
  235. ("TSST" "Set subtitle")
  236. ("TXXX" "User defined text information frame")
  237. ("UFID" "Unique file identifier")
  238. ("USER" "Terms of use")
  239. ("USLT" "Unsynchronised lyric/text transcription")
  240. ("WCOM" "Commercial information")
  241. ("WCOP" "Copyright/Legal information")
  242. ("WOAF" "Official audio file webpage")
  243. ("WOAR" "Official artist/performer webpage")
  244. ("WOAS" "Official audio source webpage")
  245. ("WORS" "Official Internet radio station homepage")
  246. ("WPAY" "Payment")
  247. ("WPUB" "Publishers official webpage")
  248. ("WXXX" "User defined URL link frame")))
  249. (defstruct frame-db-entry
  250. text
  251. (is-v22 nil)
  252. (is-v23 nil)
  253. (is-v24 nil))
  254. (defparameter *frame-db* nil)
  255. (defmacro make-frame-db ()
  256. `(progn
  257. (setf *frame-db* (make-hash-table :test #'equalp))
  258. (dolist (s *v22-frame-names*)
  259. (setf (gethash (first s) *frame-db*)
  260. (make-frame-db-entry :text (second s) :is-v22 t)))
  261. (dolist (s *v23-frame-names*)
  262. (setf (gethash (first s) *frame-db*)
  263. (make-frame-db-entry :text (second s) :is-v23 t)))
  264. (dolist (s *v24-frame-names*)
  265. (multiple-value-bind (val found) (gethash (first s) *frame-db*)
  266. (setf (gethash (first s) *frame-db*)
  267. (if found
  268. (progn
  269. (setf (frame-db-entry-is-v24 val) t)
  270. val)
  271. (make-frame-db-entry :text (second s) :is-v24 t)))))))
  272. (make-frame-db)
  273. (defun get-frame-db-entry (id)
  274. "Given a frame id/name, return the associated FRAME-DB-ENTRY"
  275. (declare #.utils:*standard-optimize-settings*)
  276. (gethash id *frame-db*))
  277. ;;;; ID3 header/extended header/v2.1 header
  278. (defclass id3-header ()
  279. ((version :accessor version :initarg :version :initform 0 :documentation "ID3 version: 2, 3, or 4")
  280. (revision :accessor revision :initarg :revision :initform 0 :documentation "ID3 revision---is this ever non-zero?")
  281. (flags :accessor flags :initarg :flags :initform 0 :documentation "ID3 header flags")
  282. (size :accessor size :initarg :size :initform 0 :documentation "size of ID3 info")
  283. (padding-size :accessor padding-size :initarg :padding-size :initform 0 :documentation "padding size in tags")
  284. (ext-header :accessor ext-header :initarg :ext-header :initform nil :documentation "holds v2.3/4 extended header")
  285. (frames :accessor frames :initarg :frames :initform nil :documentation "holds ID3 frames")
  286. (v21-tag-header :accessor v21-tag-header :initarg :v21-tag-header :initform nil :documentation "old-style v2.1 header (if present)"))
  287. (:documentation "The ID3 header, found at start of file"))
  288. (defclass v21-tag-header ()
  289. ((title :accessor title :initarg :title :initform nil)
  290. (artist :accessor artist :initarg :artist :initform nil)
  291. (album :accessor album :initarg :album :initform nil)
  292. (year :accessor year :initarg :year :initform nil)
  293. (comment :accessor comment :initarg :comment :initform nil)
  294. (track :accessor track :initarg :track :initform nil :documentation "some taggers allow the last 2 bytes of comment to be used as track number")
  295. (genre :accessor genre :initarg :genre :initform nil))
  296. (:documentation "ID3 V2.1 old-style tag. If present, found in last 128 bytes of file."))
  297. (defmethod vpprint ((me v21-tag-header) stream)
  298. (with-slots (title artist album year comment track genre) me
  299. (format stream "title = <~a>, artist = <~a>, album = <~a>, year = <~a>, comment = <~a>, track = <~d>, genre = ~d (~a)"
  300. title artist album year comment track genre (abstract-tag:get-id3v1-genre genre))))
  301. ;;; NB: no ":after" here
  302. (defmethod initialize-instance ((me v21-tag-header) &key instream)
  303. "Read in a V2.1 tag. Caller will have stream-seek'ed file to correct location and ensured that TAG was present"
  304. (declare #.utils:*standard-optimize-settings*)
  305. (with-slots (title artist album year comment genre track) me
  306. (setf title (upto-null (stream-read-iso-string instream 30))
  307. artist (upto-null (stream-read-iso-string instream 30))
  308. album (upto-null (stream-read-iso-string instream 30))
  309. year (upto-null (stream-read-iso-string instream 4)))
  310. ;; In V21, a comment can be split into comment and track #
  311. ;; find the first #\Null then check to see if that index < 28. If so,
  312. ;; check the last two bytes being non-zero---if so, track can be set to
  313. ;; integer value of last two bytes
  314. (let* ((c (stream-read-sequence instream 30))
  315. (first-null (find 0 c))
  316. (trck 0))
  317. (when (and first-null (<= first-null 28))
  318. (setf (ldb (byte 8 8) trck) (aref c 28)
  319. (ldb (byte 8 0) trck) (aref c 29)))
  320. (setf comment (upto-null (map 'string #'code-char c)))
  321. (if (> trck 0)
  322. (setf track trck)
  323. (setf track nil)))
  324. (setf genre (stream-read-u8 instream))))
  325. (defclass id3-ext-header ()
  326. ((size :accessor size :initarg :size :initform 0)
  327. (flags :accessor flags :initarg :flags :initform 0)
  328. (padding :accessor padding :initarg :padding :initform 0)
  329. (crc :accessor crc :initarg :crc :initform nil)
  330. (is-update :accessor is-update :initarg :is-update :initform nil)
  331. (restrictions :accessor restrictions :initarg :restrictions :initform 0))
  332. (:documentation "Class representing a V2.3/4 extended header"))
  333. (defmethod initialize-instance :after ((me id3-ext-header) &key instream version)
  334. "Read in the extended header. Caller will have stream-seek'ed to correct
  335. location in file. Note: extended headers are subject to unsynchronization, so
  336. make sure that INSTREAM has been made sync-safe. NB: 2.3 and 2.4 extended flags
  337. are different..."
  338. (declare #.utils:*standard-optimize-settings*)
  339. (with-slots (size flags padding crc is-update restrictions) me
  340. (setf size (stream-read-u32 instream)
  341. flags (stream-read-u16 instream)) ; reading in flags fields, must discern below 2.3/2.4
  342. (ecase version
  343. (3
  344. (setf padding (stream-read-u32 instream))
  345. (when (logand flags #x8000)
  346. (if (not (= size 10))
  347. (warn-user "file ~a:~%CRC bit set in extended header, but not enough bytes to read"
  348. audio-streams:*current-file*)
  349. (setf crc (stream-read-u32 instream)))))
  350. (4
  351. (when (not (= (logand #xff00 flags) 1))
  352. (warn-user "file ~a:~%v2.4 extended flags length is not 1"
  353. audio-streams:*current-file*))
  354. (setf flags (logand flags #xff)) ; lop off type byte (the flags length)
  355. (let ((len 0))
  356. (when (logand #x3000 flags)
  357. (setf len (stream-read-u8 instream))
  358. (when (not (zerop len))
  359. (warn-user "file ~a:~%v2.4 extended header is-tag length is ~d"
  360. audio-streams:*current-file* len))
  361. (setf is-update t))
  362. (when (logand #x2000 flags)
  363. (setf len (stream-read-u8 instream))
  364. (when (not (= 5 len))
  365. (warn-user "file ~a:~%v2.4 extended header crc length is ~d"
  366. audio-streams:*current-file* len))
  367. (setf crc (stream-read-u32 instream :bits-per-byte 7)))
  368. (when (logand #x1000 flags)
  369. (setf len (stream-read-u8 instream))
  370. (when (not (= 5 1))
  371. (warn-user "file ~a:~%v2.4 extended header restrictions length is ~d"
  372. audio-streams:*current-file* len))
  373. (setf restrictions (stream-read-u8 instream))))))))
  374. (defun ext-header-restrictions-grok (r)
  375. "Return a string that shows what restrictions are in an ext-header"
  376. (declare #.utils:*standard-optimize-settings*)
  377. (if (zerop r)
  378. "No restrictions"
  379. (with-output-to-string (s)
  380. (format s "Tag size restictions: ~a/"
  381. (ecase (ash (logand #xc0 r) -6)
  382. (0 "No more than 128 frames and 1 MB total tag size")
  383. (1 "No more than 64 frames and 128 KB total tag size")
  384. (2 "No more than 32 frames and 40 KB total tag size")
  385. (3 "No more than 32 frames and 4 KB total tag size")))
  386. (format s "Tag encoding restictions: ~a/"
  387. (ecase (ash (logand #x20 r) -5)
  388. (0 "No restrictions")
  389. (1 "Strings are only encoded with ISO-8859-1 [ISO-8859-1] or UTF-8 [UTF-8]")))
  390. (format s "Tag field size restictions: ~a/"
  391. (ecase (ash (logand #x18 r) -3)
  392. (0 "No restrictions")
  393. (1 "No string is longer than 1024 characters")
  394. (2 "No string is longer than 128 characters")
  395. (3 "No string is longer than 30 characters")))
  396. (format s "Tag image encoding restrictions: ~a/"
  397. (ecase (ash (logand #x04 r) -2)
  398. (0 "No restrictions")
  399. (1 "Images are encoded only with PNG [PNG] or JPEG [JFIF]")))
  400. (format s "Tag image size restrictions: ~a"
  401. (ecase (logand #x04 r)
  402. (0 "No restrictions")
  403. (1 "All images are 256x256 pixels or smaller.")
  404. (2 "All images are 64x64 pixels or smaller.")
  405. (3 "All images are exactly 64x64 pixels, unless required otherwise."))))))
  406. (defmethod vpprint ((me id3-ext-header) stream)
  407. (with-slots (size flags padding crc is-update restrictions) me
  408. (format stream "extended header: size: ~d, flags: ~x, padding ~:d, crc = ~x is-update ~a, restrictions = ~x/~a~%"
  409. size flags padding crc is-update restrictions (ext-header-restrictions-grok restrictions))))
  410. ;;; NB: v2.2 only really defines bit-7. It does document bit-6 as being the
  411. ;;; compression flag, but then states that if it is set, the software should
  412. ;;; "ignore the entire tag if this (bit-6) is set"
  413. (defmacro header-unsynchronized-p (flags) `(logbitp 7 ,flags)) ; all share this flag
  414. (defmacro header-extended-p (flags) `(logbitp 6 ,flags)) ; 2.3/2.4
  415. (defmacro header-experimental-p (flags) `(logbitp 5 ,flags)) ; 2.3/2.4
  416. (defmacro header-footer-p (flags) `(logbitp 4 ,flags)) ; 2.4 only
  417. (defmacro print-header-flags (stream flags)
  418. `(format ,stream "0x~2,'0x: ~:[0/~;unsynchronized-frames/~]~:[0/~;extended-header/~]~:[0/~;expermental-tag/~]~:[0~;footer-present~]"
  419. ,flags
  420. (header-unsynchronized-p ,flags)
  421. (header-extended-p ,flags)
  422. (header-experimental-p ,flags)
  423. (header-footer-p ,flags)))
  424. (defmethod vpprint ((me id3-header) stream)
  425. (with-slots (version revision flags v21-tag-header padding-size size ext-header frames) me
  426. (format stream "~a"
  427. (with-output-to-string (s)
  428. (format s "Header: version/revision: ~d/~d, flags: ~a, size = ~:d bytes; padding: ~:d bytes; ~a; ~a"
  429. version revision (print-header-flags nil flags) size padding-size
  430. (if (and (header-extended-p flags) ext-header)
  431. (concatenate 'string "Extended header: " (vpprint ext-header nil))
  432. "No extended header")
  433. (if v21-tag-header
  434. (concatenate 'string "V21 tag: " (vpprint v21-tag-header nil))
  435. "No V21 tag"))
  436. (when frames
  437. (format s "~&~4tFrames[~d]:~%" (length frames))
  438. (dolist (f frames)
  439. (format s "~8t~a~%" (vpprint f nil))))))))
  440. (defmethod initialize-instance :after ((me id3-header) &key instream &allow-other-keys)
  441. "Fill in an mp3-header from INSTREAM."
  442. (declare #.utils:*standard-optimize-settings*)
  443. (with-slots (version revision flags size ext-header frames v21-tag-header) me
  444. (stream-seek instream 128 :end)
  445. (when (string= "TAG" (stream-read-iso-string instream 3))
  446. (handler-case
  447. (setf v21-tag-header (make-instance 'v21-tag-header :instream instream))
  448. (condition (c)
  449. (warn-user "file ~a:~%Initialize id3-header got condition ~a"
  450. audio-streams:*current-file* c))))
  451. (stream-seek instream 0 :start)
  452. (when (string= "ID3" (stream-read-iso-string instream 3))
  453. (setf version (stream-read-u8 instream)
  454. revision (stream-read-u8 instream)
  455. flags (stream-read-u8 instream)
  456. size (stream-read-u32 instream :bits-per-byte 7))
  457. (assert (not (header-footer-p flags)) () "Can't decode ID3 footer's yet"))))
  458. ;;;; Frames
  459. ;;;
  460. ;;; General plan: for each frame type we are interested in, DEFCLASS a
  461. ;;; class with specfic naming convention: frame-xxx/frame-xxxx, where xxx
  462. ;;; is valid ID3V2.2 frame name and xxxx is a valid ID3V2.[34] frame name.
  463. ;;; Upon finding a frame name in an MP3 file, we can then do a FIND-CLASS
  464. ;;; on the "frame-xxx", and a MAKE-INSTANCE on the found class to read in
  465. ;;; that class (each defined class is assumed to have an
  466. ;;; INITIALIZE-INSTANCE method that reads in data to build class.
  467. ;;;
  468. ;;; Each frame class assumes that the STREAM being passed has been made
  469. ;;; sync-safe.
  470. ;;;
  471. ;;; For any class we don't want to parse (eg, haven't gotten around to it
  472. ;;; yet, etc), we create a RAW-FRAME class that can be subclassed.
  473. ;;; RAW-FRAME simply reads in the frame header, and then the frame
  474. ;;; "payload" as raw OCTETS.
  475. ;;; Many ID3 tags are name/value pairs, with the name/value encoded in
  476. ;;; various ways this routine assumes that the "name" is always a string
  477. ;;; with a "normal" encoding (i.e. 0, 1, 2, or 3). The "value" field
  478. ;;; accepts "normal" encoding, but also accepts any negative number, which
  479. ;;; means read the bytes an raw octets.
  480. (defun get-name-value-pair (instream len name-encoding value-encoding)
  481. (declare #.utils:*standard-optimize-settings*)
  482. (let* ((old-pos (stream-seek instream))
  483. (name (id3-read-string instream :encoding name-encoding))
  484. (name-len (- (stream-seek instream) old-pos))
  485. (value))
  486. (setf value (if (>= value-encoding 0)
  487. (id3-read-string instream :len (- len name-len)
  488. :encoding value-encoding)
  489. (stream-read-sequence instream (- len name-len)))) ; if < 0, then just read as octets
  490. (values name value)))
  491. (defclass id3-frame ()
  492. ((pos :accessor pos :initarg :pos :documentation "the offset in the buffer were this frame was found")
  493. (id :accessor id :initarg :id :documentation "the 3-4 character name of this frame")
  494. (len :accessor len :initarg :len :documentation "the length of this frame")
  495. (version :accessor version :initarg :version :documentation "the ID3-HEADER version number stored here for convenience")
  496. (flags :accessor flags :initarg :flags :initform nil :documentation "the frame's flags"))
  497. (:documentation "Base class for an ID3 frame. Used for versions 2.2, 2.3, and 2.4"))
  498. ;;; The frame flags are the same for V22/V23
  499. (defmacro frame-23-altertag-p (frame-flags) `(logbitp 15 ,frame-flags))
  500. (defmacro frame-23-alterfile-p (frame-flags) `(logbitp 14 ,frame-flags))
  501. (defmacro frame-23-readonly-p (frame-flags) `(logbitp 13 ,frame-flags))
  502. (defmacro frame-23-compress-p (frame-flags) `(logbitp 7 ,frame-flags))
  503. (defmacro frame-23-encrypt-p (frame-flags) `(logbitp 6 ,frame-flags))
  504. (defmacro frame-23-group-p (frame-flags) `(logbitp 5 ,frame-flags))
  505. ;;; frame flags are different for 2.4. Also note, that some flags indicate that additional data
  506. ;;; follows the frame header and these must be read in the order of the flags
  507. (defmacro frame-24-altertag-p (frame-flags) `(logbitp 14 ,frame-flags)) ; no additional data
  508. (defmacro frame-24-alterfile-p (frame-flags) `(logbitp 13 ,frame-flags)) ; no additional data
  509. (defmacro frame-24-readonly-p (frame-flags) `(logbitp 12 ,frame-flags)) ; no additional data
  510. (defmacro frame-24-groupid-p (frame-flags) `(logbitp 6 ,frame-flags)) ; one byte added to frame
  511. (defmacro frame-24-compress-p (frame-flags) `(logbitp 3 ,frame-flags)) ; one byte added to frame
  512. (defmacro frame-24-encrypt-p (frame-flags) `(logbitp 2 ,frame-flags)) ; wonky case, may or may not be set, dependin on encryption type
  513. (defmacro frame-24-unsynch-p (frame-flags) `(logbitp 1 ,frame-flags)) ; *may* have a 4-byte field after header, iff datalen is set
  514. (defmacro frame-24-datalen-p (frame-flags) `(logbitp 0 ,frame-flags)) ; if unsynch is set and this too, 4-bytes are added to frame
  515. ;; NB version 2.2 does NOT have FLAGS field in a frame; hence, the ECASE
  516. (defun valid-frame-flags (header-version frame-flags)
  517. (declare #.utils:*standard-optimize-settings*)
  518. (ecase header-version
  519. (3 (zerop (logand #b0001111100011111 frame-flags)))
  520. (4 (zerop (logand #b1000111110110000 frame-flags)))))
  521. (defun print-frame-flags (version flags stream)
  522. (declare #.utils:*standard-optimize-settings*)
  523. (ecase version
  524. (2 (format stream "None"))
  525. (3 (format stream
  526. "0x~4,'0x: ~:[0/~;tag-alter-preservation/~]~:[0/~;file-alter-preservation/~]~:[0/~;read-only/~]~:[0/~;compress/~]~:[0/~;encypt/~]~:[0~;group~]"
  527. flags
  528. (frame-23-altertag-p flags)
  529. (frame-23-alterfile-p flags)
  530. (frame-23-readonly-p flags)
  531. (frame-23-compress-p flags)
  532. (frame-23-encrypt-p flags)
  533. (frame-23-group-p flags)))
  534. (4 (format stream
  535. "flags: 0x~4,'0x: ~:[0/~;tag-alter-preservation/~]~:[0/~;file-alter-preservation/~]~:[0/~;read-only/~]~:[0/~;group-id/~]~:[0/~;compress/~]~:[0/~;encypt/~]~:[0/~;unsynch/~]~:[0~;datalen~]"
  536. flags
  537. (frame-24-altertag-p flags)
  538. (frame-24-alterfile-p flags)
  539. (frame-24-readonly-p flags)
  540. (frame-24-groupid-p flags)
  541. (frame-24-compress-p flags)
  542. (frame-24-encrypt-p flags)
  543. (frame-24-unsynch-p flags)
  544. (frame-24-datalen-p flags)))))
  545. (defun vpprint-frame-header (id3-frame)
  546. (with-output-to-string (stream)
  547. (with-slots (pos version id len flags) id3-frame
  548. (format stream "offset: ~:d, version = ~d, id: ~a, description: ~a, len: ~:d, flags: ~a"
  549. pos version id
  550. (aif (get-frame-db-entry id)
  551. (frame-db-entry-text it)
  552. "Non-standard")
  553. len
  554. (if flags
  555. (print-frame-flags version flags nil)
  556. "flags: none")))))
  557. (defclass frame-raw (id3-frame)
  558. ((octets :accessor octets :initform nil))
  559. (:documentation "Frame class that slurps in frame contents w/no attempt to grok them"))
  560. (defmethod initialize-instance :after ((me frame-raw) &key instream)
  561. (declare #.utils:*standard-optimize-settings*)
  562. (with-slots (pos len octets) me
  563. (setf octets (stream-read-sequence instream len))))
  564. (defmethod vpprint ((me frame-raw) stream)
  565. (with-slots (octets) me
  566. (format stream "frame-raw: ~a, ~a" (vpprint-frame-header me) (printable-array octets))))
  567. ;;;; V2.2 frames
  568. ;;; Frames we need to implement someday
  569. (defclass frame-buf (frame-raw) ())
  570. (defclass frame-cnt (frame-raw) ())
  571. (defclass frame-cra (frame-raw) ())
  572. (defclass frame-crm (frame-raw) ())
  573. (defclass frame-equ (frame-raw) ())
  574. (defclass frame-etc (frame-raw) ())
  575. (defclass frame-geo (frame-raw) ())
  576. (defclass frame-ipl (frame-raw) ())
  577. (defclass frame-lnk (frame-raw) ())
  578. (defclass frame-mci (frame-raw) ())
  579. (defclass frame-mll (frame-raw) ())
  580. (defclass frame-pop (frame-raw) ())
  581. (defclass frame-rev (frame-raw) ())
  582. (defclass frame-rva (frame-raw) ())
  583. (defclass frame-slt (frame-raw) ())
  584. (defclass frame-waf (frame-raw) ())
  585. (defclass frame-war (frame-raw) ())
  586. (defclass frame-was (frame-raw) ())
  587. (defclass frame-wcm (frame-raw) ())
  588. (defclass frame-wcp (frame-raw) ())
  589. (defclass frame-wpb (frame-raw) ())
  590. (defclass frame-stc (frame-raw) ())
  591. ;;; V22 User defined... "WXX"
  592. ;;; Text encoding $xx
  593. ;;; Description <textstring> $00 (00)
  594. ;;; URL <textstring>
  595. ;;; Identical to TXX
  596. (defclass frame-wxx (frame-txx) ())
  597. ;; V22 COM frames
  598. ;; Comment "COM"
  599. ;; Text encoding $xx
  600. ;; Language $xx xx xx
  601. ;; Short content description <textstring> $00 (00)
  602. ;; The actual text <textstring>
  603. (defclass frame-com (id3-frame)
  604. ((encoding :accessor encoding)
  605. (lang :accessor lang)
  606. (desc :accessor desc)
  607. (val :accessor val)))
  608. (defmethod initialize-instance :after ((me frame-com) &key instream)
  609. (declare #.utils:*standard-optimize-settings*)
  610. (with-slots (len encoding lang desc val) me
  611. (setf encoding (stream-read-u8 instream)
  612. lang (stream-read-iso-string instream 3))
  613. (multiple-value-bind (n v) (get-name-value-pair instream (- len 1 3) encoding encoding)
  614. (setf desc n)
  615. ;; iTunes broken-ness... for frame-coms, there can be an additional null or two at the end
  616. (setf val (upto-null v)))))
  617. (defmethod vpprint ((me frame-com) stream)
  618. (with-slots (len encoding lang desc val) me
  619. (format stream "frame-com: ~a, encoding = ~d, lang = <~a> (~a), desc = <~a>, val = <~a>"
  620. (vpprint-frame-header me) encoding lang (get-iso-639-2-language lang) desc val)))
  621. ;;; ULT's are same format as COM's...
  622. ;;; V22 unsynced lyrics/text "ULT"
  623. ;;; Text encoding $xx
  624. ;;; Language $xx xx xx
  625. ;;; Content descriptor <textstring> $00 (00)
  626. ;;; Lyrics/text <textstring>
  627. (defclass frame-ult (frame-com) ())
  628. ;; V22 PIC frames
  629. ;; Attached picture "PIC"
  630. ;; Text encoding $xx
  631. ;; Image format $xx xx xx
  632. ;; Picture type $xx
  633. ;; Description <textstring> $00 (00)
  634. ;; Picture data <binary data>
  635. (defclass frame-pic (id3-frame)
  636. ((encoding :accessor encoding)
  637. (img-format :accessor img-format)
  638. (ptype :accessor ptype)
  639. (desc :accessor desc)
  640. (data :accessor data)))
  641. (defmethod initialize-instance :after ((me frame-pic) &key instream)
  642. (declare #.utils:*standard-optimize-settings*)
  643. (with-slots (id len encoding img-format ptype desc data) me
  644. (setf encoding (stream-read-u8 instream)
  645. img-format (stream-read-iso-string instream 3)
  646. ptype (stream-read-u8 instream))
  647. (multiple-value-bind (n v) (get-name-value-pair instream (- len 5) encoding -1)
  648. (setf desc n
  649. data v))))
  650. (defmethod vpprint ((me frame-pic) stream)
  651. (with-slots (encoding img-format ptype desc data) me
  652. (format stream "frame-pic: ~a, encoding ~d, img-format type: <~a>, picture type: ~d (~a), description <~a>, data: ~a"
  653. (vpprint-frame-header me) encoding img-format ptype (get-picture-type ptype) desc (printable-array data))))
  654. (defmethod picture-info ((me frame-pic))
  655. "Used by ABSTRACT-TAG interface to report data about V2.2 cover art"
  656. (with-slots (encoding img-format ptype desc data) me
  657. (format nil "Size: ~:d" (length data))))
  658. ;; Version 2, 3, or 4 generic text-info frames
  659. ;; Text information identifier "T00" - "TZZ", excluding "TXX", or "T000 - TZZZ", excluding "TXXX"
  660. ;; Text encoding $xx
  661. ;; Information <textstring>
  662. (defclass frame-text-info (id3-frame)
  663. ((encoding :accessor encoding)
  664. (info :accessor info))
  665. (:documentation "V2/V3/V4 T00-TZZ and T000-TZZZ frames, but not TXX or TXXX"))
  666. (defmethod initialize-instance :after ((me frame-text-info) &key instream)
  667. (declare #.utils:*standard-optimize-settings*)
  668. (with-slots (version flags len encoding info) me
  669. (let ((read-len len))
  670. ;; In version 4 frames, each frame may also have an unsync flag. since we
  671. ;; have unsynced already the only thing we need to do here is check for
  672. ;; the optional DATALEN field. If it is present then it has the actual
  673. ;; number of octets to read
  674. (when (and (= version 4) (frame-24-unsynch-p flags))
  675. (if (frame-24-datalen-p flags)
  676. (setf read-len (stream-read-u32 instream :bits-per-byte 7))))
  677. (setf encoding (stream-read-u8 instream)
  678. info (id3-read-string instream :len (1- read-len) :encoding encoding)))
  679. ;; A null is ok, but according to the "spec", you're supposed to
  680. ;; ignore anything after a 'Null'
  681. (setf info (upto-null info))))
  682. (defmethod vpprint ((me frame-text-info) stream)
  683. (with-slots (len encoding info) me
  684. (format stream "frame-text-info: ~a, encoding = ~d, info = <~a>"
  685. (vpprint-frame-header me) encoding info)))
  686. (defclass frame-tal (frame-text-info) ())
  687. (defclass frame-tbp (frame-text-info) ())
  688. (defclass frame-tcm (frame-text-info) ())
  689. (defclass frame-tco (frame-text-info) ())
  690. (defclass frame-tsa (frame-text-info) ())
  691. (defclass frame-tsc (frame-text-info) ())
  692. (defclass frame-tsp (frame-text-info) ())
  693. (defclass frame-ts2 (frame-text-info) ())
  694. (defclass frame-itunes-compilation (frame-raw)
  695. ((info :accessor info)))
  696. (defclass frame-tcp (frame-itunes-compilation) ())
  697. (defmethod initialize-instance :after ((me frame-itunes-compilation) &key &allow-other-keys)
  698. "iTunes compilation weirdness: I have seen this encoded soooo many ways..."
  699. (declare #.utils:*standard-optimize-settings*)
  700. (with-slots (len octets info) me
  701. (setf info
  702. (cond
  703. ((= 1 len) (if (= 0 (aref octets 0)) "0" "1"))
  704. ((= 2 len) (if (= #x30 (aref octets 1)) "0" "1"))
  705. ((= 3 len) (if (typep me 'frame-tcp)
  706. (upto-null (id3-decode-string octets
  707. :start 1
  708. :encoding (aref octets 0)))
  709. "0"))
  710. ((= 4 len) "0")
  711. (t (upto-null (id3-decode-string octets
  712. :start 1
  713. :encoding (aref octets 0))))))))
  714. (defmethod vpprint ((me frame-itunes-compilation) stream)
  715. (with-slots (octets info) me
  716. (format stream "frame-itunes-compilation: ~a, octets:<~a>, info:~a"
  717. (vpprint-frame-header me) (printable-array octets) info)))
  718. (defclass frame-tcr (frame-text-info) ())
  719. (defclass frame-tda (frame-text-info) ())
  720. (defclass frame-tdy (frame-text-info) ())
  721. (defclass frame-ten (frame-text-info) ())
  722. (defclass frame-tft (frame-text-info) ())
  723. (defclass frame-tim (frame-text-info) ())
  724. (defclass frame-tke (frame-text-info) ())
  725. (defclass frame-tla (frame-text-info) ())
  726. (defclass frame-tle (frame-text-info) ())
  727. (defclass frame-tmt (frame-text-info) ())
  728. (defclass frame-toa (frame-text-info) ())
  729. (defclass frame-tof (frame-text-info) ())
  730. (defclass frame-tol (frame-text-info) ())
  731. (defclass frame-tor (frame-text-info) ())
  732. (defclass frame-tot (frame-text-info) ())
  733. (defclass frame-tp1 (frame-text-info) ())
  734. (defclass frame-tp2 (frame-text-info) ())
  735. (defclass frame-tp3 (frame-text-info) ())
  736. (defclass frame-tp4 (frame-text-info) ())
  737. (defclass frame-tpa (frame-text-info) ())
  738. (defclass frame-tpb (frame-text-info) ())
  739. (defclass frame-trc (frame-text-info) ())
  740. (defclass frame-trd (frame-text-info) ())
  741. (defclass frame-trk (frame-text-info) ())
  742. (defclass frame-tsi (frame-text-info) ())
  743. (defclass frame-tss (frame-text-info) ())
  744. (defclass frame-tt1 (frame-text-info) ())
  745. (defclass frame-tt2 (frame-text-info) ())
  746. (defclass frame-tt3 (frame-text-info) ())
  747. (defclass frame-txt (frame-text-info) ())
  748. (defclass frame-tye (frame-text-info) ())
  749. ;; V22 User defined "TXX" frames
  750. ;; Text encoding $xx
  751. ;; Description <textstring> $00 (00)
  752. ;; Value <textstring>
  753. (defclass frame-txx (id3-frame)
  754. ((encoding :accessor encoding)
  755. (desc :accessor desc)
  756. (val :accessor val))
  757. (:documentation "TXX is the only frame starting with a 'T' that has a different format"))
  758. (defmethod initialize-instance :after ((me frame-txx) &key instream)
  759. (declare #.utils:*standard-optimize-settings*)
  760. (with-slots (len encoding desc val) me
  761. (setf encoding (stream-read-u8 instream))
  762. (multiple-value-bind (n v) (get-name-value-pair instream (1- len) encoding encoding)
  763. (setf desc n
  764. val v))))
  765. (defmethod vpprint ((me frame-txx) stream)
  766. (with-slots (len encoding desc val) me
  767. (format stream "frame-txx: ~a, encoding = ~d, desc = <~a>, val = <~a>" (vpprint-frame-header me) encoding desc val)))
  768. ;;; V22 unique file identifier "UFI"
  769. ;;; Owner identifier <textstring> $00
  770. ;;; Identifier <up to 64 bytes binary data>
  771. (defclass frame-ufi (id3-frame)
  772. ((name :accessor name)
  773. (value :accessor value))
  774. (:documentation "Unique File Identifier"))
  775. (defmethod initialize-instance :after ((me frame-ufi) &key instream)
  776. (declare #.utils:*standard-optimize-settings*)
  777. (with-slots (id len name value) me
  778. (multiple-value-bind (n v) (get-name-value-pair instream len 0 -1)
  779. (setf name n
  780. value v))))
  781. (defmethod vpprint ((me frame-ufi) stream)
  782. (with-slots (id len name value) me
  783. (format stream "frame-ufi: ~a, name: <~a>, value: ~a" (vpprint-frame-header me) name (printable-array value))))
  784. ;;;; V23/V24 frames
  785. ;;; Frames we need to implement someday
  786. (defclass frame-aenc (frame-raw) ())
  787. (defclass frame-aspi (frame-raw) ())
  788. (defclass frame-comr (frame-raw) ())
  789. (defclass frame-encr (frame-raw) ())
  790. (defclass frame-equ2 (frame-raw) ())
  791. (defclass frame-equa (frame-raw) ())
  792. (defclass frame-etco (frame-raw) ())
  793. (defclass frame-geob (frame-raw) ())
  794. (defclass frame-grid (frame-raw) ())
  795. (defclass frame-ipls (frame-raw) ())
  796. (defclass frame-link (frame-raw) ())
  797. (defclass frame-mcdi (frame-raw) ())
  798. (defclass frame-mllt (frame-raw) ())
  799. (defclass frame-ncon (frame-raw) ())
  800. (defclass frame-owne (frame-raw) ())
  801. (defclass frame-popm (frame-raw) ())
  802. (defclass frame-poss (frame-raw) ())
  803. (defclass frame-rbuf (frame-raw) ())
  804. (defclass frame-rgad (frame-raw) ())
  805. (defclass frame-rva2 (frame-raw) ())
  806. (defclass frame-rvad (frame-raw) ())
  807. (defclass frame-rvrb (frame-raw) ())
  808. (defclass frame-seek (frame-raw) ())
  809. (defclass frame-sign (frame-raw) ())
  810. (defclass frame-sylt (frame-raw) ())
  811. (defclass frame-sytc (frame-raw) ())
  812. (defclass frame-user (frame-raw) ())
  813. (defclass frame-xdor (frame-raw) ())
  814. (defclass frame-xsop (frame-raw) ())
  815. ;;; V23/V24 text-info frames
  816. (defclass frame-talb (frame-text-info) ())
  817. (defclass frame-tbpm (frame-text-info) ())
  818. (defclass frame-tcmp (frame-itunes-compilation) ())
  819. (defclass frame-tcom (frame-text-info) ())
  820. (defclass frame-tcon (frame-text-info) ())
  821. (defclass frame-tcop (frame-text-info) ())
  822. (defclass frame-tdat (frame-text-info) ())
  823. (defclass frame-tden (frame-text-info) ())
  824. (defclass frame-tdly (frame-text-info) ())
  825. (defclass frame-tdor (frame-text-info) ())
  826. (defclass frame-tdrc (frame-text-info) ())
  827. (defclass frame-tdrl (frame-text-info) ())
  828. (defclass frame-tdtg (frame-text-info) ())
  829. (defclass frame-tenc (frame-text-info) ())
  830. (defclass frame-text (frame-text-info) ())
  831. (defclass frame-tflt (frame-text-info) ())
  832. (defclass frame-time (frame-text-info) ())
  833. (defclass frame-tipl (frame-text-info) ())
  834. (defclass frame-tit1 (frame-text-info) ())
  835. (defclass frame-tit2 (frame-text-info) ())
  836. (defclass frame-tit3 (frame-text-info) ())
  837. (defclass frame-tkey (frame-text-info) ())
  838. (defclass frame-tlan (frame-text-info) ())
  839. (defclass frame-tlen (frame-text-info) ())
  840. (defclass frame-tmcl (frame-text-info) ())
  841. (defclass frame-tmed (frame-text-info) ())
  842. (defclass frame-tmoo (frame-text-info) ())
  843. (defclass frame-toal (frame-text-info) ())
  844. (defclass frame-tofn (frame-text-info) ())
  845. (defclass frame-toly (frame-text-info) ())
  846. (defclass frame-tope (frame-text-info) ())
  847. (defclass frame-tory (frame-text-info) ())
  848. (defclass frame-town (frame-text-info) ())
  849. (defclass frame-tpe1 (frame-text-info) ())
  850. (defclass frame-tpe2 (frame-text-info) ())
  851. (defclass frame-tpe3 (frame-text-info) ())
  852. (defclass frame-tpe4 (frame-text-info) ())
  853. (defclass frame-tpos (frame-text-info) ())
  854. (defclass frame-tpro (frame-text-info) ())
  855. (defclass frame-tpub (frame-text-info) ())
  856. (defclass frame-trda (frame-text-info) ())
  857. (defclass frame-trsn (frame-text-info) ())
  858. (defclass frame-trso (frame-text-info) ())
  859. (defclass frame-tsoa (frame-text-info) ())
  860. (defclass frame-tsop (frame-text-info) ())
  861. (defclass frame-tsot (frame-text-info) ())
  862. (defclass frame-tsst (frame-text-info) ())
  863. (defclass frame-tsse (frame-text-info) ())
  864. (defclass frame-tsrc (frame-text-info) ())
  865. (defclass frame-tsiz (frame-text-info) ())
  866. (defclass frame-tyer (frame-text-info) ())
  867. (defclass frame-trck (frame-text-info) ())
  868. (defparameter *picture-type*
  869. '("Other"
  870. "32x32 pixels 'file icon' (PNG only)"
  871. "Other file icon"
  872. "Cover (front)"
  873. "Cover (back)"
  874. "Leaflet page"
  875. "Media (e.g. lable side of CD)"
  876. "Lead artist/lead performer/soloist"
  877. "Artist/performer"
  878. "Conductor"
  879. "Band/Orchestra"
  880. "Composer"
  881. "Lyricist/text writer"
  882. "Recording Location"
  883. "During recording"
  884. "During performance"
  885. "Movie/video screen capture"
  886. "A bright coloured fish" ; how do you know the fish is intelligent? :)
  887. "Illustration"
  888. "Band/artist logotype"
  889. "Publisher/Studio logotype"))
  890. (defun get-picture-type (n)
  891. "Function to return picture types for APIC frames"
  892. (declare #.utils:*standard-optimize-settings*)
  893. (if (and (>= n 0) (< n (length *picture-type*)))
  894. (nth n *picture-type*)
  895. "Unknown"))
  896. ;; V23/V24 APIC frames
  897. ;; <Header for 'Attached picture', ID: "APIC">
  898. ;; Text encoding $xx
  899. ;; MIME type <text string> $00
  900. ;; Picture type $xx
  901. ;; Description <text string according to encoding> $00 (00)
  902. ;; Picture data <binary data>
  903. (defclass frame-apic (id3-frame)
  904. ((encoding :accessor encoding)
  905. (mime :accessor mime)
  906. (ptype :accessor ptype)
  907. (desc :accessor desc)
  908. (data :accessor data))
  909. (:documentation "Holds an attached picture (cover art)"))
  910. (defmethod initialize-instance :after ((me frame-apic) &key instream)
  911. (declare #.utils:*standard-optimize-settings*)
  912. (with-slots (id len encoding mime ptype desc data) me
  913. (setf encoding (stream-read-u8 instream)
  914. mime (stream-read-iso-string instream)
  915. ptype (stream-read-u8 instream))
  916. (multiple-value-bind (n v) (get-name-value-pair instream (- len 1 (length mime) 1 1) encoding -1)
  917. (setf desc n
  918. data v))))
  919. (defmethod vpprint ((me frame-apic) stream)
  920. (with-slots (encoding mime ptype desc data) me
  921. (format stream "frame-apic: ~a, encoding ~d, mime type: ~a, picture type: ~d (~a), description <~a>, data: ~a"
  922. (vpprint-frame-header me) encoding mime ptype (get-picture-type ptype) desc (printable-array data))))
  923. (defmethod picture-info ((me frame-apic))
  924. "Used by ABSTRACT-TAG interface to report data about V2.3/4 cover art"
  925. (with-slots (encoding mime ptype desc data) me
  926. (format nil "Size: ~:d" (length data))))
  927. ;;; V23/V24 COMM frames
  928. ;;; <Header for 'Comment', ID: "COMM">
  929. ;;; Text encoding $xx
  930. ;;; Language $xx xx xx
  931. ;;; Short content descrip. <text string according to encoding> $00 (00)
  932. ;;; The actual text <full text string according to encoding>
  933. (defclass frame-comm (id3-frame)
  934. ((encoding :accessor encoding)
  935. (lang :accessor lang)
  936. (desc :accessor desc)
  937. (val :accessor val))
  938. (:documentation "V23/4 Comment frame"))
  939. (defmethod initialize-instance :after ((me frame-comm) &key instream)
  940. (declare #.utils:*standard-optimize-settings*)
  941. (with-slots (encoding lang len desc val) me
  942. (setf encoding (stream-read-u8 instream)
  943. lang (stream-read-iso-string instream 3))
  944. (multiple-value-bind (n v) (get-name-value-pair instream (- len 1 3) encoding encoding)
  945. (setf desc n)
  946. ;; iTunes broken-ness... for frame-coms, there can be an additional null or two at the end
  947. (setf val (upto-null v)))))
  948. (defmethod vpprint ((me frame-comm) stream)
  949. (with-slots (encoding lang desc val) me
  950. (format stream "frame-comm: ~a, encoding: ~d, lang: <~a> (~a), desc = <~a>, val = <~a>"
  951. (vpprint-frame-header me) encoding lang (get-iso-639-2-language lang) desc val)))
  952. ;;; Unsynchronized lyrics frames look very much like comment frames...
  953. (defclass frame-uslt (frame-comm) ())
  954. ;;; V23/24 PCNT frames
  955. ;;; <Header for 'Play counter', ID: "PCNT">
  956. ;;; Counter $xx xx xx xx (xx ...)
  957. (defclass frame-pcnt (id3-frame)
  958. ((play-count :accessor play-count))
  959. (:documentation "Play count frame"))
  960. (defmethod initialize-instance :after ((me frame-pcnt) &key instream)
  961. (declare #.utils:*standard-optimize-settings*)
  962. (with-slots (play-count len) me
  963. (assert (= 4 len) () "Ran into a play count with ~d bytes" len)
  964. (setf play-count (stream-read-u32 instream)))) ; probably safe---play count *can* be longer than 4 bytes, but...
  965. (defmethod vpprint ((me frame-pcnt) stream)
  966. (with-slots (play-count) me
  967. (format stream "frame-pcnt: ~a, count = ~d" (vpprint-frame-header me) play-count)))
  968. ;;; V23/V24 PRIV frames
  969. ;;; <Header for 'Private frame', ID: "PRIV">
  970. ;;; Owner identifier <text string> $00
  971. ;;; The private data <binary data>
  972. (defclass frame-priv (id3-frame)
  973. ((name :accessor name)
  974. (value :accessor value))
  975. (:documentation "Private frame"))
  976. (defmethod initialize-instance :after ((me frame-priv) &key instream)
  977. (declare #.utils:*standard-optimize-settings*)
  978. (with-slots (id len name value) me
  979. (multiple-value-bind (n v) (get-name-value-pair instream len 0 -1)
  980. (setf name n
  981. value v))))
  982. (defmethod vpprint ((me frame-priv) stream)
  983. (with-slots (id len name value) me
  984. (format stream "frame-priv: ~a, name: <~a>, data: ~a" (vpprint-frame-header me) name (printable-array value))))
  985. ;; V23/V24 TXXX frames
  986. ;; <Header for 'User defined text information frame', ID: "TXXX">
  987. ;; Text encoding $xx
  988. ;; Description <text string according to encoding> $00 (00)
  989. ;; Value <text string according to encoding>
  990. (defclass frame-txxx (id3-frame)
  991. ((encoding :accessor encoding)
  992. (desc :accessor desc)
  993. (val :accessor val))
  994. (:documentation "TXXX frame"))
  995. (defmethod initialize-instance :after ((me frame-txxx) &key instream)
  996. (declare #.utils:*standard-optimize-settings*)
  997. (with-slots (encoding len desc val) me
  998. (setf encoding (stream-read-u8 instream))
  999. (multiple-value-bind (n v) (get-name-value-pair instream
  1000. (- len 1)
  1001. encoding
  1002. encoding)
  1003. (setf desc n
  1004. val v))))
  1005. (defmethod vpprint ((me frame-txxx) stream)
  1006. (format stream "frame-txxx: ~a, <~a/~a>" (vpprint-frame-header me) (desc me) (val me)))
  1007. ;; V23/V24 UFID frames
  1008. ;; <Header for 'Unique file identifier', ID: "UFID">
  1009. ;; Owner identifier <text string> $00
  1010. ;; Identifier <up to 64 bytes binary data>
  1011. (defclass frame-ufid (id3-frame)
  1012. ((name :accessor name)
  1013. (value :accessor value))
  1014. (:documentation "Unique file identifier frame"))
  1015. (defmethod initialize-instance :after ((me frame-ufid) &key instream)
  1016. (declare #.utils:*standard-optimize-settings*)
  1017. (with-slots (id len name value) me
  1018. (multiple-value-bind (n v) (get-name-value-pair instream len 0 -1)
  1019. (setf name n
  1020. value v))))
  1021. (defmethod vpprint ((me frame-ufid) stream)
  1022. (with-slots (id len name value) me
  1023. (format stream "frame-ufid: ~a, name: <~a>, value: ~a" (vpprint-frame-header me) name (printable-array value))))
  1024. ;;; V23/V24 URL frame
  1025. ;;; <Header for 'URL link frame', ID: "W000" - "WZZZ", excluding "WXXX" described in 4.3.2.>
  1026. ;;; URL <text string>
  1027. (defclass frame-url-link (id3-frame)
  1028. ((url :accessor url))
  1029. (:documentation "URL link frame"))
  1030. (defmethod initialize-instance :after ((me frame-url-link) &key instream)
  1031. (declare #.utils:*standard-optimize-settings*)
  1032. (with-slots (id len url) me
  1033. (setf url (stream-read-iso-string instream len))))
  1034. (defmethod vpprint ((me frame-url-link) stream)
  1035. (with-slots (url) me
  1036. (format stream "frame-url-link: ~a, url: ~a" (vpprint-frame-header me) url)))
  1037. ;;; V23/V24 frames URL link frames
  1038. (defclass frame-wcom (frame-url-link) ())
  1039. (defclass frame-wcop (frame-url-link) ())
  1040. (defclass frame-woaf (frame-url-link) ())
  1041. (defclass frame-woar (frame-url-link) ())
  1042. (defclass frame-woas (frame-url-link) ())
  1043. (defclass frame-wors (frame-url-link) ())
  1044. (defclass frame-wpay (frame-url-link) ())
  1045. (defclass frame-wpub (frame-url-link) ())
  1046. ;;; Identical to frame-txx
  1047. (defclass frame-wxxx (frame-txx) ())
  1048. ;;;; Frame finding/creation
  1049. (defun possibly-valid-frame-id? (frame-id)
  1050. "test to see if a string is a potentially valid frame id"
  1051. (declare #.utils:*standard-optimize-settings*)
  1052. (labels ((numeric-char-p (c)
  1053. (let ((code (char-code c)))
  1054. (and (>= code (char-code #\0))
  1055. (<= code (char-code #\9))))))
  1056. ;; test each octet to see if it is alphanumeric
  1057. (dotimes (i (length frame-id))
  1058. (let ((c (aref frame-id i)))
  1059. (when (not (or (numeric-char-p c)
  1060. (and (alpha-char-p c) (upper-case-p c))))
  1061. (return-from possibly-valid-frame-id? nil))))
  1062. t))
  1063. (memoize 'possibly-valid-frame-id?)
  1064. (defun mk-frame-class-name (id)
  1065. (declare #.utils:*standard-optimize-settings*)
  1066. (string-upcase (concatenate 'string "frame-" id)))
  1067. (memoize 'mk-frame-class-name)
  1068. (defun find-frame-class (id)
  1069. "Search by concatenating 'frame-' with ID and look for that symbol in this package"
  1070. (declare #.utils:*standard-optimize-settings*)
  1071. (let ((found-class-symbol (find-symbol (mk-frame-class-name id) :ID3))
  1072. found-class)
  1073. ;; if we found the class name, return the class (to be used for MAKE-INSTANCE)
  1074. (when found-class-symbol
  1075. (setf found-class (find-class found-class-symbol))
  1076. (return-from find-frame-class found-class))
  1077. ;; if not a "normal" frame-id, look at general cases of
  1078. ;; starting with a 'T' or a 'W'
  1079. (setf found-class (case (aref id 0)
  1080. (#\T (find-class (find-symbol "FRAME-TEXT-INFO" :ID3)))
  1081. (#\W (find-class (find-symbol "FRAME-URL-LINK" :ID3)))
  1082. (t
  1083. ;; we don't recognize the frame name. if it could
  1084. ;; possibly be a real frame name, then just read
  1085. ;; it raw
  1086. (when (possibly-valid-frame-id? id)
  1087. (warn-user "file ~a~%Unknown frame type <~a> encountered~%"
  1088. audio-streams:*current-file* id)
  1089. (find-class (find-symbol "FRAME-RAW" :ID3))))))
  1090. found-class))
  1091. (memoize 'find-frame-class)
  1092. (defun make-frame (version instream fn)
  1093. "Create an appropriate mp3 frame by reading data from INSTREAM."
  1094. (declare #.utils:*standard-optimize-settings*)
  1095. (let* ((pos (stream-seek instream))
  1096. (byte (stream-read-u8 instream))
  1097. frame-name frame-len frame-flags frame-class)
  1098. (when (zerop byte) ; XXX should this be correlated to PADDING in the extended header???
  1099. (return-from make-frame nil)) ; hit padding
  1100. ;; I have seen 3-char frame names where 4-chars were supposed to be...
  1101. (setf frame-name
  1102. (string-right-trim '(#\Space #\Null)
  1103. (concatenate 'string (string (code-char byte))
  1104. (id3-read-string instream
  1105. :len (ecase version
  1106. (2 2)
  1107. (3 3)
  1108. (4 3))))))
  1109. (setf frame-len (ecase version
  1110. (2 (stream-read-u24 instream))
  1111. (3 (stream-read-u32 instream))
  1112. (4 (stream-read-u32 instream :bits-per-byte 7))))
  1113. (when (or (= version 3) (= version 4))
  1114. (setf frame-flags (stream-read-u16 instream))
  1115. (when (not (valid-frame-flags version frame-flags))
  1116. (warn-user "file: ~a~%Invalid frame flags found: ~a; will ignore"
  1117. fn
  1118. (print-frame-flags version frame-flags nil))))
  1119. (setf frame-class (find-frame-class frame-name))
  1120. ;; edge case where found a frame name, but it is not valid or where
  1121. ;; making this frame would blow past the end of the file/buffer
  1122. (when (or (> (+ (stream-seek instream) frame-len) (stream-size instream))
  1123. (null frame-class))
  1124. (error "bad frame at position ~d found: ~a" pos frame-name))
  1125. (make-instance frame-class :pos pos :version version :id frame-name :len frame-len :flags frame-flags :instream instream)))
  1126. (defun is-valid-mp3-file (instream)
  1127. "Make sure this is an MP3 file. Look for ID3 header at begining (versions 2,
  1128. 3, 4) and/or end (version 2.1) Written in this fashion so as to be
  1129. 'crash-proof' when passed an arbitrary file."
  1130. (declare #.utils:*standard-optimize-settings*)
  1131. (let ((id3)
  1132. (valid nil)
  1133. (version)
  1134. (tag))
  1135. (when (> (stream-size instream) 4)
  1136. (stream-seek instream 0 :start)
  1137. (setf id3 (stream-read-iso-string instream 3)
  1138. version (stream-read-u8 instream))
  1139. (when (> (stream-size instream) 128)
  1140. (stream-seek instream 128 :end)
  1141. (setf tag (stream-read-iso-string instream 3)))
  1142. (setf valid (or (and (string= "ID3" id3)
  1143. (or (= 2 version) (= 3 version) (= 4 version)))
  1144. (string= tag "TAG"))))
  1145. (stream-seek instream 0 :start)
  1146. valid))
  1147. (defclass mp3-file ()
  1148. ((filename :accessor filename :initform nil :initarg :filename
  1149. :documentation "filename that was parsed")
  1150. (id3-header :accessor id3-header :initform nil
  1151. :documentation "holds all the ID3 info")
  1152. (audio-info :accessor audio-info :initform nil
  1153. :documentation "holds the bit-rate, etc info"))
  1154. (:documentation "Output of parsing MP3 files"))
  1155. (defun parse-audio-file (instream &optional get-audio-info)
  1156. "Parse an MP3 file"
  1157. (declare #.utils:*standard-optimize-settings*)
  1158. (let ((parsed-info))
  1159. (labels ((read-loop (version stream)
  1160. (let (frames this-frame)
  1161. (do ()
  1162. ((>= (stream-seek stream) (stream-size stream)))
  1163. (handler-case
  1164. (progn
  1165. (setf this-frame (make-frame version stream
  1166. (stream-filename instream)))
  1167. (when (null this-frame)
  1168. (setf (padding-size (id3-header parsed-info))
  1169. (- (stream-size stream)
  1170. (stream-seek stream)))
  1171. (return-from read-loop (values t (nreverse frames))))
  1172. (push this-frame frames))
  1173. (condition (c)
  1174. (warn-user "file ~a:~%Id3 parse-audio-file got condition ~a"
  1175. audio-streams:*current-file* c)
  1176. (return-from read-loop (values nil (nreverse frames))))))
  1177. (values t (nreverse frames))))) ; frames in "file order"
  1178. (setf parsed-info (make-instance 'mp3-file
  1179. :filename (stream-filename instream)))
  1180. (setf (id3-header parsed-info) (make-instance 'id3-header :instream instream))
  1181. (with-slots (size ext-header frames flags version) (id3-header parsed-info)
  1182. ;; At this point, we switch from reading the file stream and create a
  1183. ;; memory stream rationale: it may need to be unsysnc'ed and it helps
  1184. ;; prevent run-away reads with mis-formed frames
  1185. (when (not (zerop size))
  1186. (let ((mem-stream
  1187. (make-audio-stream (stream-read-sequence
  1188. instream size
  1189. :bits-per-byte
  1190. (if (header-unsynchronized-p flags) 7 8)))))
  1191. ;; Make extended header here since it is subject to unsynchronization.
  1192. (when (header-extended-p flags)
  1193. (setf ext-header (make-instance 'id3-ext-header
  1194. :instream mem-stream
  1195. :version version)))
  1196. ;; Start reading frames from memory stream
  1197. (multiple-value-bind (_ok _frames) (read-loop version mem-stream)
  1198. (if (not _ok)
  1199. (warn-user
  1200. "file ~a:~%Had errors finding mp3 frames. potentially missed frames!"
  1201. (stream-filename instream)))
  1202. (setf frames _frames))))
  1203. (when get-audio-info
  1204. (mpeg:get-mpeg-audio-info instream parsed-info))
  1205. parsed-info))))
  1206. (defun map-id3-frames (mp3 &key (func (constantly t)))
  1207. "Iterates through the ID3 frames found in an MP3 file"
  1208. (declare #.utils:*standard-optimize-settings*)
  1209. (mapcar func (frames (id3-header mp3))))
  1210. (defun get-frames (mp3 names)
  1211. "Given a MP3 file's info, search its frames for NAMES.
  1212. Return file-order list of matching frames"
  1213. (declare #.utils:*standard-optimize-settings*)
  1214. (let (found-frames)
  1215. (map-id3-frames mp3
  1216. :func (lambda (f)
  1217. (when (member (id f) names :test #'string=)
  1218. (push f found-frames))))
  1219. (nreverse found-frames)))