Fork of https://github.com/mv2devnul/taglib
|
|
12 лет назад | |
|---|---|---|
| .gitignore | 12 лет назад | |
| README.md | 12 лет назад | |
| UNLICENSE | 12 лет назад | |
| abstract-tag.lisp | 12 лет назад | |
| audio-streams.lisp | 12 лет назад | |
| flac-frame.lisp | 12 лет назад | |
| id3-frame.lisp | 12 лет назад | |
| iso-639-2.lisp | 12 лет назад | |
| mp4-atom.lisp | 12 лет назад | |
| mpeg.lisp | 12 лет назад | |
| packages.lisp | 12 лет назад | |
| profile.lisp | 12 лет назад | |
| taglib-tests.asd | 12 лет назад | |
| taglib-tests.lisp | 12 лет назад | |
| taglib.asd | 12 лет назад | |
| tree.lisp | 12 лет назад | |
| utils.lisp | 12 лет назад |
Copyright (c) 2013, Mark VandenBrink. All rights reserved.
A pure Lisp implementation for reading audio tags and audio information.
Currently reads MP3/MP4/FLAC audio files.
Runs (in single-thread mode) under CCL, SBCL, CLISP, and ABCL. Note: my primary Lisp variant is CCL, so it's the most tested; however, this code should run on any Lisp that is supported by FLEXI-STREAMS
All avalailable via quicklisp
Note: There a lot of good (some great) audio file resources out there. Here are a few of them that I found useful:
Things to consider adding/changing:
(let (foo)
(unwind-protect
(setf foo (parse-mp4-file "01 Keep Yourself Alive.m4a"))
(when foo
(mp4-tag:show-tags foo)
(stream-close foo)))
Yields:
01 Keep Yourself Alive.m4a
sample rate: 44100.0 Hz, # channels: 2, bits-per-sample: 16, max bit-rate: 314 Kbps, avg bit-rate: 256 Kbps, duration: 4:03
album: Queen I
album-artist: Queen
artist: Queen
compilation: no
disk: (1 1)
genre: 80 (Hard Rock)
title: Keep Yourself Alive
track: (1 11)
year: 1973
The show-tags methods also have a "raw" capability. Example:
(let (foo)
(unwind-protect
(setf foo (parse-mp3-file "Queen/At the BBC/06 Great King Rat.mp3"))
(when foo
(mp3-tag:show-tags foo :raw t)
(stream-close foo)))
Yields:
Queen/At the BBC/06 Great King Rat.mp3: MPEG 1, Layer III, VBR, sample rate: 44,100 Hz, bit rate: 128 Kbps, duration: 5:60
Header: version/revision: 3/0, flags: 0x00: 0/0/0/0, size = 11,899 bytes; No extended header; No V21 tag
Frames[9]:
frame-text-info: flags: 0x0000: 0/0/0/0/0/0, offset: 0, version = 3, id: TIT2, len: 15, NIL, encoding = 0, info = <Great King Rat>
frame-text-info: flags: 0x0000: 0/0/0/0/0/0, offset: 25, version = 3, id: TPE1, len: 6, NIL, encoding = 0, info = <Queen>
frame-text-info: flags: 0x0000: 0/0/0/0/0/0, offset: 41, version = 3, id: TPE2, len: 6, NIL, encoding = 0, info = <Queen>
frame-text-info: flags: 0x0000: 0/0/0/0/0/0, offset: 57, version = 3, id: TALB, len: 11, NIL, encoding = 0, info = <At the BBC>
frame-text-info: flags: 0x0000: 0/0/0/0/0/0, offset: 78, version = 3, id: TRCK, len: 4, NIL, encoding = 0, info = <6/8>
frame-text-info: flags: 0x0000: 0/0/0/0/0/0, offset: 92, version = 3, id: TPOS, len: 4, NIL, encoding = 0, info = <1/1>
frame-text-info: flags: 0x0000: 0/0/0/0/0/0, offset: 106, version = 3, id: TYER, len: 5, NIL, encoding = 0, info = <1995>
frame-text-info: flags: 0x0000: 0/0/0/0/0/0, offset: 121, version = 3, id: TCON, len: 5, NIL, encoding = 0, info = <(79)>
frame-txxx: flags: 0x0000: 0/0/0/0/0/0, offset: 136, version = 3, id: TXXX, len: 33, NIL, <Tagging time/2013-08-08T16:38:38>
For any class we don't want to parse (eg, haven't gotten around to it yet, etc), we create a RAW-FRAME class that can be subclassed. RAW-FRAME simply reads in the frame header, and then the frame "payload" as raw OCTETS.
Each frame class assumes that the STREAM being passed has been made sync-safe.
abstract-tag.lisp: The abstract interface for ID3 tags for audio files. The abstract interface is simply one of the following:
mp4-atom.lisp: Parses MP4 audio files. Similar logic to id-frame.lisp, but has two main differnces: first, it returns a tree structure (needed, since, that's how M4A atoms/boxes work), and secondly, has an atom-skip class that records the name and position of an atom, but seeks to the next atom rather than reading in contents.
As noted in the comments of this file, there are three kinds of "boxes/atoms":
* Pure container atoms: have no data and are only used to contain other atoms. This is akin to a UNIX filesystem's directory notion.
* Pure "data" atoms: has no nested atoms. Only has a payload.
* A mixture of both.
In previous versions of this library, if we ran into a Xing header with the number of frames set to zero, I just set the duration as 0. This was fast, but not really "nice." In new revs, I decided that if I hit a bad Xing header, the correct thing to do is to read every frame and calculate the duration/avg bit-rate that way. The resulting calculations match those of iTunes, but it can really slow things down.
More info later...
I've recently added some (very) rudimentary multi-threading (see taglib-tests.lisp) using the CHANL package.
CURRENTLY BROKEN
First, the filesystem walker (main thread) walks the requested directory, adding each filename to an unbounded channel (*channel*). The main thread then sends *MAX-THREADS* *END-THREAD* symbols, creates *MAX-THREADS* worker threads who read from the channel, and then sits in a loop reading from *dead-channel* until it has done *MAX-THREADS* recv's.
The worker threads parse the filename they retrieve from *channel* until they get the *END-THREAD* symbol, whereupon they write their thread id to *dead-channel* and return (ie exit). Here are some preliminary timings:
| # Threads | Time (seconds) |
|---|---|
| 10 | ~28 |
| 5 | ~18 |
| 2 | ~17 |
| 1 | ~25 |
Note: threading does NOT currently work on ECL (missing support in bordeaux threads) or on my custom built CLISP with POSIX-THREADS turned on (fails with RECURSIVE MUTEX error). ABCL kinda/sorta works (seems to be a problem with ATOM-TYPE being unbound