Peter Siebel - Practical Common Lisp

Здесь есть возможность читать онлайн «Peter Siebel - Practical Common Lisp» весь текст электронной книги совершенно бесплатно (целиком полную версию без сокращений). В некоторых случаях можно слушать аудио, скачать через торрент в формате fb2 и присутствует краткое содержание. Год выпуска: 2005, ISBN: 2005, Издательство: Apress, Жанр: Программирование, на английском языке. Описание произведения, (предисловие) а так же отзывы посетителей доступны на портале библиотеки ЛибКат.

Practical Common Lisp: краткое содержание, описание и аннотация

Предлагаем к чтению аннотацию, описание, краткое содержание или предисловие (зависит от того, что написал сам автор книги «Practical Common Lisp»). Если вы не нашли необходимую информацию о книге — напишите в комментариях, мы постараемся отыскать её.

Practical Common Lisp — читать онлайн бесплатно полную книгу (весь текст) целиком

Ниже представлен текст книги, разбитый по страницам. Система сохранения места последней прочитанной страницы, позволяет с удобством читать онлайн бесплатно книгу «Practical Common Lisp», без необходимости каждый раз заново искать на чём Вы остановились. Поставьте закладку, и сможете в любой момент перейти на страницу, на которой закончили чтение.

Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать

With the basic primitive types done, you're ready to switch to a high-level view and start defining binary classes to represent first the ID3 tag as a whole and then the individual frames.

If you turn first to the ID3v2.2 specification, you'll see that the basic structure of the tag is this header:

ID3/file identifier "ID3"

ID3 version $02 00

ID3 flags %xx000000

ID3 size 4 * %0xxxxxxx

followed by frame data and padding. Since you've already defined binary types to read and write all the fields in the header, defining a class that can read the header of an ID3 tag is just a matter of putting them together.

(define-binary-class id3-tag ()

((identifier (iso-8859-1-string :length 3))

(major-version u1)

(revision u1)

(flags u1)

(size id3-tag-size)))

If you have some MP3 files lying around, you can test this much of the code and also see what version of ID3 tags your MP3s contain. First you can write a function that reads an id3-tag, as just defined, from the beginning of a file. Be aware, however, that ID3 tags aren't required to appear at the beginning of a file, though these days they almost always do. To find an ID3 tag elsewhere in a file, you can scan the file looking for the sequence of bytes 73, 68, 51 (in other words, the string "ID3"). [275] The 2.4 version of the ID3 format also supports placing a footer at the end of a tag, which makes it easier to find a tag appended to the end of a file. For now you can probably get away with assuming the tags are the first thing in the file.

(defun read-id3 (file)

(with-open-file (in file :element-type '(unsigned-byte 8))

(read-value 'id3-tag in)))

On top of this function you can build a function that takes a filename and prints the information in the tag header along with the name of the file.

(defun show-tag-header (file)

(with-slots (identifier major-version revision flags size) (read-id3 file)

(format t "~a ~d.~d ~8,'0b ~d bytes — ~a~%"

identifier major-version revision flags size (enough-namestring file))))

It prints output that looks like this:

ID3V2> (show-tag-header "/usr2/mp3/Kitka/Wintersongs/02 Byla Cesta.mp3")

ID3 2.0 00000000 2165 bytes — Kitka/Wintersongs/02 Byla Cesta.mp3

NIL

Of course, to determine what versions of ID3 are most common in your MP3 library, it'd be handier to have a function that returns a summary of all the MP3 files under a given directory. You can write one easily enough using the walk-directoryfunction defined in Chapter 15. First define a helper function that tests whether a given filename has an mp3extension.

(defun mp3-p (file)

(and

(not (directory-pathname-p file))

(string-equal "mp3" (pathname-type file))))

Then you can combine show-tag-headerand mp3-pwith walk-directoryto print a summary of the ID3 header in each file under a given directory.

(defun show-tag-headers (dir)

(walk-directory dir #'show-tag-header :test #'mp3-p))

However, if you have a lot of MP3s, you may just want a count of how many ID3 tags of each version you have in your MP3 collection. To get that information, you might write a function like this:

(defun count-versions (dir)

(let ((versions (mapcar #'(lambda (x) (cons x 0)) '(2 3 4))))

(flet ((count-version (file)

(incf (cdr (assoc (major-version (read-id3 file)) versions)))))

(walk-directory dir #'count-version :test #'mp3-p))

versions))

Another function you'll need in Chapter 29 is one that tests whether a file actually starts with an ID3 tag, which you can define like this:

(defun id3-p (file)

(with-open-file (in file :element-type '(unsigned-byte 8))

(string= "ID3" (read-value 'iso-8859-1-string in :length 3))))

ID3 Frames

As I discussed earlier, the bulk of an ID3 tag is divided into frames. Each frame has a structure similar to that of the tag as a whole. Each frame starts with a header indicating what kind of frame it is and the size of the frame in bytes. The structure of the frame header changed slightly between version 2.2 and version 2.3 of the ID3 format, and eventually you'll have to deal with both forms. To start, you can focus on parsing version 2.2 frames.

The header of a 2.2 frame consists of three bytes that encode a three-character ISO 8859-1 string followed by a three-byte unsigned integer, which specifies the size of the frame in bytes, excluding the six-byte header. The string identifies what type of frame it is, which determines how you parse the data following the size. This is exactly the kind of situation for which you defined the define-tagged-binary-classmacro. You can define a tagged class that reads the frame header and then dispatches to the appropriate concrete class using a function that maps IDs to a class names.

(define-tagged-binary-class id3-frame ()

((id (iso-8859-1-string :length 3))

(size u3))

(:dispatch (find-frame-class id)))

Now you're ready to start implementing concrete frame classes. However, the specification defines quite a few—63 in version 2.2 and even more in later specs. Even considering frame types that share a common structure to be equivalent, you'll still find 24 unique frame types in version 2.2. But only a few of these are used "in the wild." So rather than immediately setting to work defining classes for each of the frame types, you can start by writing a generic frame class that lets you read the frames in a tag without parsing the data within the frames themselves. This will give you a way to find out what frames are actually present in the MP3s you want to process. You'll need this class eventually anyway because the specification allows for experimental frames that you'll need to be able to read without parsing.

Since the size field of the frame header tells you exactly how many bytes long the frame is, you can define a generic-frameclass that extends id3-frameand adds a single field, data, that will hold an array of bytes.

(define-binary-class generic-frame (id3-frame)

((data (raw-bytes :size size))))

The type of the data field, raw-bytes, just needs to hold an array of bytes. You can define it like this:

(define-binary-type raw-bytes (size)

(:reader (in)

(let ((buf (make-array size :element-type '(unsigned-byte 8))))

(read-sequence buf in)

buf))

(:writer (out buf)

(write-sequence buf out)))

For the time being, you'll want all frames to be read as generic-frames, so you can define the find-frame-classfunction used in id3-frame's :dispatchexpression to always return generic-frame, regardless of the frame's id.

(defun find-frame-class (id)

(declare (ignore id))

'generic-frame)

Now you need to modify id3-tagso it'll read frames after the header fields. There's only one tricky bit to reading the frame data: although the tag header tells you how many bytes long the tag is, that number includes the padding that can follow the frame data. Since the tag header doesn't tell you how many frames the tag contains, the only way to tell when you've hit the padding is to look for a null byte where you'd expect a frame identifier.

Читать дальше
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать

Похожие книги на «Practical Common Lisp»

Представляем Вашему вниманию похожие книги на «Practical Common Lisp» списком для выбора. Мы отобрали схожую по названию и смыслу литературу в надежде предоставить читателям больше вариантов отыскать новые, интересные, ещё непрочитанные произведения.


Отзывы о книге «Practical Common Lisp»

Обсуждение, отзывы о книге «Practical Common Lisp» и просто собственные мнения читателей. Оставьте ваши комментарии, напишите, что Вы думаете о произведении, его смысле или главных героях. Укажите что конкретно понравилось, а что нет, и почему Вы так считаете.

x