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)