Update of /project/cl-mp3-parse/cvsroot/cl-mp3-parse
In directory common-lisp.net:/tmp/cvs-serv23120
Modified Files:
mp3-parser.lisp
Added Files:
ChangeLog
Log Message:
Date: Sun Mar 20 03:55:27 2005
Author: dbueno
Index: cl-mp3-parse/mp3-parser.lisp
diff -u cl-mp3-parse/mp3-parser.lisp:1.1.1.1 cl-mp3-parse/mp3-parser.lisp:1.2
--- cl-mp3-parse/mp3-parser.lisp:1.1.1.1 Sun Mar 20 00:19:06 2005
+++ cl-mp3-parse/mp3-parser.lisp Sun Mar 20 03:55:27 2005
@@ -1,4 +1,57 @@
-;;;; $Id: mp3-parser.lisp,v 1.1.1.1 2005/03/19 23:19:06 dbueno Exp $
+;;;; $Id: mp3-parser.lisp,v 1.2 2005/03/20 02:55:27 dbueno Exp $
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; This software is provided without warranty of any kind, and is
+;;;; released under the BSD license. I reserve the right to change
+;;;; that, but only in the "more permissive license" direction, if
+;;;; such is possible.
+;;;;
+;;;; Copyright (c) 2004, Denis Bueno
+;;;; All rights reserved.
+;;;;
+;;;; Redistribution and use in source and binary forms, with or
+;;;; without modification, are permitted provided that the following
+;;;; conditions are met:
+;;;;
+;;;; Redistributions of source code must retain the above copyright
+;;;; notice, this list of conditions and the following
+;;;; disclaimer.
+;;;;
+;;;; Redistributions in binary form must reproduce the above
+;;;; copyright notice, this list of conditions and the following
+;;;; disclaimer in the documentation and/or other materials
+;;;; provided with the distribution.
+;;;;
+;;;; The name of the Denis Bueno may not be used to endorse or
+;;;; promote products derived from this software without specific
+;;;; prior written permission.
+;;;;
+;;;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+;;;; CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+;;;; INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+;;;; MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+;;;; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+;;;; BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+;;;; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+;;;; TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+;;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+;;;; ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+;;;; OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+;;;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+;;;; POSSIBILITY OF SUCH DAMAGE.
+;;;;
+;;;; Notes: Originally a ported some code written by a fried of mine,
+;;;; Curtis Jones. After I understood the problem, I changed the the
+;;;; implementation of READ-MP3-FRAMES significantly.
+;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; TODO
+;;;;
+;;;; [ ] There's something wrong with the way the average bitrate is
+;;;; calculated. Fix that.
+;;;;
+;;;; [ ] Eventually I'd like to use Peter Seibel's CLOS generic binary type
+;;;; system. Get that working.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(in-package :mpeg)
@@ -13,8 +66,8 @@
(list 2 4)
:element-type '(unsigned-byte 32)
:initial-contents
- '((0 5 4 3) ;mpeg-2
- (0 2 1 0)) ;mpeg-1
+ '((0 5 4 3) ; mpeg-2
+ (0 2 1 0)) ; mpeg-1
)))
(unless (boundp 'mpeg-frequency)
@@ -70,25 +123,28 @@
(defmacro df (num)
`(coerce ,num 'double-float))
-;; (setq lu-debug:*dprint-print-base* 16)
-;; (lu-debug:ondebug "mpeg-load.info" "mpeg-load.debug")
-
(defun frame-header-p (arr &optional (pos 0))
- "see if there is a frame header starting at POS in ARR. checks
+ "See if there is a frame header starting at POS in ARR. checks
that all the frame sync bytes are set."
(and (= #xff (aref arr pos))
(= #xf (ldb (byte 4 4) (aref arr (1+ pos))))))
-;;; written by me
-
(define-condition invalid-frame-header ()
- ((text :initarg :text :reader text)
- (data :initarg :data :reader data)))
+ ((text :initarg :text :reader text)))
(define-condition invalid-layer-code ()
((code :initarg :code :reader code)))
(defun read-mp3-frames (filename &optional (spos 0))
+ "Read from FILENAME, an MP3 file, and return a list
+ of (simple-array (unsigned-byte 8))'s, which contain the frame
+ data for each frame in the MP3. The arrays returned include the
+ header data for each frame. If SPOS is specified, SPOS bytes
+ starting from the beginning of the file stream will be skipped
+ before attempting to parse the frames of the MP3. This is
+ primarily useful because this parser doesn't (yet) assume the
+ presence of ID3 tags. SPOS can hence be used to skip over the
+ ID3 tag(s) before attempting to parse."
(with-open-file (stream filename :element-type '(unsigned-byte 8))
(file-position stream spos)
(read-mp3-frames-rec stream)))
@@ -185,12 +241,14 @@
(make-array 4
:element-type '(unsigned-byte 8)
:initial-element 0)))
+ "Read the MP3 header from the given data buffer. The buffer
+ should contain at least 4 bytes of data (that's how long
+ headers are)."
(unless (frame-header-p data bufpos)
(error 'invalid-frame-header :data data))
;; read the 4 bytes that make up the header
(replace head data :start2 bufpos :end2 (+ bufpos 4))
- (lu-debug:dprint "read-mp3-header.debug" "raw header: ~a" head)
;; grab the coded values from the header...
(let ((bitrate (ldb (byte 4 4) (aref head 2)))
@@ -204,6 +262,7 @@
(values head bitrate layer mpegid frequ padding protect chan)))
(defun calculate-framebytes (bitrate frequ pad-code layer-code)
+ "Calculate the number of bytes in a given frame."
(case layer-code
((or #.mpeg-layer-2 #.mpeg-layer-3)
(floor
@@ -214,11 +273,12 @@
(* (+ (/ (* bitrate 1000 12) frequ)
(if (zerop pad-code) 0 4))
4)))
- (t (error 'invalid-layer-code))))
-
-;;; print the header of a frame
+ (t (error 'invalid-layer-code :code layer-code))))
(defun print-frame-header (head &optional (framenum nil))
+ "Print the values in the frame header so that they make sense
+ to a human. FRAMENUM is used if you want to label the frame
+ you're printing."
(assert (= 4 (length head)))
(multiple-value-bind (h bitrate layer mpegid frequ padding protect chan)
(read-mp3-header head)
@@ -238,18 +298,22 @@
(format t "~& channel: ~d~%" (channel-code->value chan))))
(defun bitrate-code->value (bitrate-code mpegid-code layer-code)
+ "Map a bitrate code to its value."
(aref mpeg-bitrate bitrate-code
(aref mpeg-style mpegid-code layer-code)))
(defun layer-code->value (layer-code)
+ "Map a layer code to its value."
(case layer-code
- ;; I found a use for #. notation: to evaluate a constant at read-time!
+ ;; I found a necessary use for #. notation: to evaluate a constant at
+ ;; read-time!
(#.mpeg-layer-1 'mpeg-layer-1)
(#.mpeg-layer-2 'mpeg-layer-2)
(#.mpeg-layer-3 'mpeg-layer-3)
(t 'unknown)))
(defun id-code->value (id-code)
+ "Map the mpeg id code to its value."
(case id-code
(0 'mpeg-2.5-unofficial)
(1 'reserved)
@@ -257,9 +321,11 @@
(3 'mpeg-1-iso/iec-11172-3)))
(defun frequ-code->value (frequ-code id-code)
+ "Map the frequency code to its value."
(aref mpeg-frequency frequ-code id-code))
(defun channel-code->value (channel-code)
+ "Map the channel code to its value."
(case channel-code
(0 'stereo)
(1 'joint-stereo)