Fork of https://github.com/mv2devnul/taglib
|
|
7 лет назад | |
|---|---|---|
| .gitignore | 12 лет назад | |
| README.md | 12 лет назад | |
| UNLICENSE | 12 лет назад | |
| abstract-tag.lisp | 7 лет назад | |
| audio-streams.lisp | 7 лет назад | |
| flac.lisp | 12 лет назад | |
| id3.lisp | 12 лет назад | |
| iso-639-2.lisp | 12 лет назад | |
| m4a.lisp | 12 лет назад | |
| mpeg.lisp | 12 лет назад | |
| packages.lisp | 7 лет назад | |
| profile.lisp | 12 лет назад | |
| taglib-tests.asd | 12 лет назад | |
| taglib-tests.lisp | 12 лет назад | |
| taglib.asd | 12 лет назад | |
| tree.lisp | 12 лет назад | |
| utils.lisp | 7 лет назад |
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:
(setf foo (audio-streams:open-audio-file "01 Keep Yourself Alive.m4a"))
(m4a:show-tags 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:
(setf foo (audio-streams:open-audio-file "06 Great King Rat.mp3")
(m4a:show-tags foo :raw t)
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:
m4a: 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.
I've recently added some (very) rudimentary multi-threading (see taglib-tests.lisp) using the CHANL package.
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).