Skip to content

Instantly share code, notes, and snippets.

@kingcons
Created February 16, 2012 00:00
Show Gist options
  • Save kingcons/1840215 to your computer and use it in GitHub Desktop.
Save kingcons/1840215 to your computer and use it in GitHub Desktop.
;; A naive translation of http://citizen428.net/blog/2010/09/04/erlang-bit-syntax-and-id3/
; Tested at the REPL with a file from the ID3V1 test suite: http://www.id3.org/Developer_Information
;; Obviously macrology could give something more akin to Erlang's bit syntax w/error handling, etc.
; See PCL chapters 24 and 25 for example...
; http://www.gigamonkeys.com/book/practical-parsing-binary-files.html
; http://www.gigamonkeys.com/book/practical-an-id3-parser.html
(defpackage :id3
(:use :cl)
(:export #:get-id3
#:get-tags))
(defun bytevector->string (bytes &optional (start 0) end)
"They're *IN* the computer? It's so simple..."
;; TODO: Potentially conditionalize the REMOVE call with kwarg.
(map 'string #'code-char (remove 0 (subseq bytes start end))))
(defun get-tags (id3 &optional tags)
"Create a plist out of the id3 data and return the selected tags if given."
; C-style indentation here, but use regions and M-x align-regexp RET ",("
(let ((result `(:title ,(bytevector->string id3 3 33)
:artist ,(bytevector->string id3 33 63)
:album ,(bytevector->string id3 63 93)
:year ,(bytevector->string id3 93 97)
:comment ,(bytevector->string id3 97 127)
:genre ,(aref id3 127)))) ; since it's just an int...
(if tags
(loop for tag in tags collecting (getf result tag))
result)))
(defun get-id3 (file)
"A naive implementation. We explicitly ignore id3 1.1 and 2.0 support."
(with-open-file (in file :element-type '(unsigned-byte 8))
(file-position in (- (file-length in) 128))
(let ((data (make-array 128 :element-type '(unsigned-byte 8))))
(read-sequence data in)
(if (string= "TAG" (bytevector->string data 0 3))
(get-tags data)
:no-id3v1-tag))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment