Mark VandenBrink 12 лет назад
Родитель
Сommit
c9e96f1d36
5 измененных файлов с 83 добавлено и 94 удалено
  1. 14 25
      mp3-frame.lisp
  2. 4 3
      mp4-atom.lisp
  3. 2 2
      packages.lisp
  4. 62 63
      streams.lisp
  5. 1 1
      taglib.asd

+ 14 - 25
mp3-frame.lisp

@@ -162,7 +162,8 @@
 		(setf revision (stream-read-u8 instream))
 		(setf revision (stream-read-u8 instream))
 		(setf flags (stream-read-u8 instream))
 		(setf flags (stream-read-u8 instream))
 		(setf size (stream-read-sync-safe-u32 instream))
 		(setf size (stream-read-sync-safe-u32 instream))
-		(when (header-unsynchronized-p flags) (log-mp3-frame "unsync"))
+		(when (header-unsynchronized-p flags)
+		  (log-mp3-frame "unsync"))
 		(assert (not (header-footer-p flags)) () "Can't decode ID3 footer's yet")
 		(assert (not (header-footer-p flags)) () "Can't decode ID3 footer's yet")
 		(when (header-extended-p flags)
 		(when (header-extended-p flags)
 		  (setf ext-header (make-instance 'mp3-extended-header :instream instream))))
 		  (setf ext-header (make-instance 'mp3-extended-header :instream instream))))
@@ -175,7 +176,7 @@
    (id      :accessor id      :initarg :id)
    (id      :accessor id      :initarg :id)
    (len     :accessor len     :initarg :len)
    (len     :accessor len     :initarg :len)
    (flags   :accessor flags   :initarg :flags :initform nil))
    (flags   :accessor flags   :initarg :flags :initform nil))
-  (:documentation   "Base class for an ID3 frame"))
+  (:documentation "Base class for an ID3 frame"))
 
 
 (defmacro frame-23-altertag-p  (frame-flags) `(logbitp 15 ,frame-flags))
 (defmacro frame-23-altertag-p  (frame-flags) `(logbitp 15 ,frame-flags))
 (defmacro frame-23-alterfile-p (frame-flags) `(logbitp 14 ,frame-flags))
 (defmacro frame-23-alterfile-p (frame-flags) `(logbitp 14 ,frame-flags))
@@ -246,7 +247,7 @@
 
 
 
 
 (defun find-mp3-frames (mp3-file)
 (defun find-mp3-frames (mp3-file)
-  "With an open mp3-file, make sure it is in fact an MP3 file, then read it's header and frames, returning both"
+  "With an open mp3-file, make sure it is in fact an MP3 file, then read it's header and frames"
   (log5:with-context "find-mp3-frames"
   (log5:with-context "find-mp3-frames"
 	(when (not (is-valid-mp3-file mp3-file))
 	(when (not (is-valid-mp3-file mp3-file))
 	  (log-mp3-frame "~a is not an mp3 file" (filename mp3-file))
 	  (log-mp3-frame "~a is not an mp3 file" (filename mp3-file))
@@ -254,25 +255,13 @@
 
 
 	(log-mp3-frame "~a is a valid mp3 file" (filename mp3-file))
 	(log-mp3-frame "~a is a valid mp3 file" (filename mp3-file))
 
 
-	(let ((header (make-instance 'mp3-id3-header :instream mp3-file))
-		  (mem-stream)
-		  (this-frame)
-		  (frames))
-	  (declare (ignore mem-stream this-frame frames))
-	  (setf (slot-value mp3-file 'mp3-header) header)
-	  (assert header () "Must have a header to continue!")
-	  header)))
-
-	  ;; (if (header-unsynchronized-p header)
-	  ;; 	  (setf mem-stream (stream-read-sync-safe-octets instream (size header)))
-	  ;; 		(setf mem-stream instream))
-
-	  ;; 	;; NB from this point, always read from mem-stream (see IF above)
-	  ;; 	(block read-loop
-	  ;; 	  (loop
-	  ;; 		(setf this-frame (make-frame header mem-stream))
-	  ;; 		(when (null this-frame)
-	  ;; 		  (return-from read-loop nil))
-	  ;; 		(push this-frame frames)))
-	  ;; 	(setf (slot-value (slot-value mp3-header 'header) 'frames) frames)
-	  ;; 	(log-mp3-frame "~a" (vpprint (slot-value mp3-header 'header) nil))))))
+	(setf (mp3-header mp3-file) (make-instance 'mp3-id3-header :instream mp3-file))
+	(ccl:with-input-from-vector (v (stream-read-octets mp3-file (size (mp3-header mp3-file))
+													   :bits-per-byte (if (header-unsynchronized-p (flags (mp3-header mp3-file))) 7 8)))
+
+	  (block read-loop
+		(loop
+	  		(let ((this-frame (make-frame v)))
+			  (when (null this-frame)
+				(return-from read-loop nil))
+			  (push this-frame (frames (mp3-header mp3-file)))))))))

+ 4 - 3
mp4-atom.lisp

@@ -258,9 +258,9 @@
 
 
 (defun make-mp4-atom (mp4-file &optional atom-parent-type)
 (defun make-mp4-atom (mp4-file &optional atom-parent-type)
   "Get current file position, read in size/type, then construct the correct atom.
   "Get current file position, read in size/type, then construct the correct atom.
- If type is an ilst type, read it all it.  If it is a container atom of interest,
- leave file position as is, since caller will want to read in nested atoms.  Otherwise,
- seek forward past end of this atom."
+If type is an ilst type, read it all it.  If it is a container atom of interest,
+leave file position as is, since caller will want to read in nested atoms.  Otherwise,
+seek forward past end of this atom."
   (log5:with-context "make-mp4-atom"
   (log5:with-context "make-mp4-atom"
 	(let* ((pos (stream-seek mp4-file 0 :current))
 	(let* ((pos (stream-seek mp4-file 0 :current))
 		   (siz (stream-read-u32 mp4-file))
 		   (siz (stream-read-u32 mp4-file))
@@ -368,6 +368,7 @@ The 'right' atoms are those in *atoms-of-interest*"
 		  (new-atom))
 		  (new-atom))
 
 
 	  (log-mp4-atom "before read-file loop, file-position = ~:d, end = ~:d" (stream-seek mp4-file 0 :current) (file-size mp4-file))
 	  (log-mp4-atom "before read-file loop, file-position = ~:d, end = ~:d" (stream-seek mp4-file 0 :current) (file-size mp4-file))
+
 	  (block stream-read-file
 	  (block stream-read-file
 		(do ()
 		(do ()
 			((> (+ 8 (stream-seek mp4-file 0 :current)) (file-size mp4-file)))
 			((> (+ 8 (stream-seek mp4-file 0 :current)) (file-size mp4-file)))

+ 2 - 2
packages.lisp

@@ -4,14 +4,14 @@
 
 
 (defpackage #:audio-streams
 (defpackage #:audio-streams
   (:export #:octets #:make-octets
   (:export #:octets #:make-octets
-		   #:base-stream
+		   #:base-file-stream
 		   #:filename #:instream #:file-size #:endian
 		   #:filename #:instream #:file-size #:endian
 		   #:stream-read-u8 #:stream-read-u16 #:stream-read-u24 #:stream-read-u32
 		   #:stream-read-u8 #:stream-read-u16 #:stream-read-u24 #:stream-read-u32
 		   #:stream-read-string #:stream-read-octets
 		   #:stream-read-string #:stream-read-octets
 		   #:stream-seek #:stream-close
 		   #:stream-seek #:stream-close
 		   #:mp4-stream #:make-mp4-stream #:mp4-atoms
 		   #:mp4-stream #:make-mp4-stream #:mp4-atoms
 		   #:mp3-stream #:make-mp3-stream #:mp3-header
 		   #:mp3-stream #:make-mp3-stream #:mp3-header
-		   #:stream-read-sync-safe-u32 #:stream-read-sync-safe-octets)
+		   #:stream-read-sync-safe-u32)
   (:use #:common-lisp))
   (:use #:common-lisp))
 
 
 (defpackage #:mp4-atom
 (defpackage #:mp4-atom

+ 62 - 63
streams.lisp

@@ -9,7 +9,7 @@
 (deftype octet () '(unsigned-byte 8))
 (deftype octet () '(unsigned-byte 8))
 (defmacro make-octets (len) `(make-array ,len :element-type 'octet))
 (defmacro make-octets (len) `(make-array ,len :element-type 'octet))
 
 
-(defclass base-stream ()
+(defclass base-file-stream ()
   ((filename  :accessor filename  :initarg :filename)
   ((filename  :accessor filename  :initarg :filename)
    (instream  :accessor instream  :initform nil)
    (instream  :accessor instream  :initform nil)
    (endian    :accessor endian    :initarg :endian :initform nil)   ; controls endian-ness of read/writes
    (endian    :accessor endian    :initarg :endian :initform nil)   ; controls endian-ness of read/writes
@@ -17,17 +17,17 @@
    (file-size :accessor file-size))
    (file-size :accessor file-size))
   (:documentation "Base class for all audio file types"))
   (:documentation "Base class for all audio file types"))
 
 
-(defmethod initialize-instance :after ((me base-stream) &key read-only &allow-other-keys)
-  (log5:with-context "base-stream-initializer"
+(defmethod initialize-instance :after ((me base-file-stream) &key read-only &allow-other-keys)
+  (log5:with-context "base-file-stream-initializer"
   (with-slots (instream filename file-size endian) me
   (with-slots (instream filename file-size endian) me
 	(setf instream (if read-only
 	(setf instream (if read-only
 					   (open filename :direction :input :element-type 'octet)
 					   (open filename :direction :input :element-type 'octet)
 					   (open filename :direction :io :if-exists :overwrite :element-type 'octet)))
 					   (open filename :direction :io :if-exists :overwrite :element-type 'octet)))
 	(setf file-size (file-length instream))
 	(setf file-size (file-length instream))
-	(log-stream "stream = ~a, name = ~a, size = ~:d~%endian = ~a"
-				   instream filename file-size endian))))
+	(log-stream "base-file-stream-initializer built stream = ~a, name = ~a, size = ~:d, endian = ~a"
+				instream filename file-size endian))))
 
 
-(defmethod stream-close ((me base-stream))
+(defmethod stream-close ((me base-file-stream))
   "Close an open stream."
   "Close an open stream."
   (with-slots (instream modified) me
   (with-slots (instream modified) me
 	(when modified
 	(when modified
@@ -37,23 +37,19 @@
 	  (close instream)
 	  (close instream)
 	  (setf instream nil))))
 	  (setf instream nil))))
 
 
-(defmethod stream-seek ((me base-stream) offset from)
+(defmethod stream-seek ((me base-file-stream) offset from)
   "C-library like seek function. from can be one of :current, :start, :end.
   "C-library like seek function. from can be one of :current, :start, :end.
 Returns the current offset into the stream"
 Returns the current offset into the stream"
-  (assert (member from '(:current :start :end)) () "seek takes one of :current, :start, :end")
   (with-slots (instream file-size) me
   (with-slots (instream file-size) me
 	(ecase from
 	(ecase from
 	  (:start (file-position instream offset))
 	  (:start (file-position instream offset))
-	  (:current
-	   (let ((current (file-position instream)))
-		 (file-position instream (+ current offset))))
-	  (:end
-	   (file-position instream (- file-size offset))))))
+	  (:current (file-position instream (+ (file-position instream) offset)))
+	  (:end (file-position instream (- file-size offset))))))
 
 
 ;;
 ;;
-;; From Practical Common Lisp by Peter Seibel.
+;; Based on a function from Practical Common Lisp by Peter Seibel.
 (defun read-octets (instream bytes &key (bits-per-byte 8) (endian :little-endian))
 (defun read-octets (instream bytes &key (bits-per-byte 8) (endian :little-endian))
-  (ecase endian 
+  (ecase endian
 	(:big-endian
 	(:big-endian
 	 (loop with value = 0
 	 (loop with value = 0
 		   for low-bit from 0 to (* bits-per-byte (1- bytes)) by bits-per-byte do
 		   for low-bit from 0 to (* bits-per-byte (1- bytes)) by bits-per-byte do
@@ -65,27 +61,27 @@ Returns the current offset into the stream"
 			 (setf (ldb (byte bits-per-byte low-bit) value) (read-byte instream))
 			 (setf (ldb (byte bits-per-byte low-bit) value) (read-byte instream))
 		   finally (return value)))))
 		   finally (return value)))))
 
 
-(defmethod stream-read-u8 ((me base-stream))
+(defmethod stream-read-u8 ((me base-file-stream))
   "read 1 byte from file"
   "read 1 byte from file"
   (with-slots (endian instream) me
   (with-slots (endian instream) me
 	(read-octets instream 1 :endian endian)))
 	(read-octets instream 1 :endian endian)))
 
 
-(defmethod stream-read-u16 ((me base-stream))
+(defmethod stream-read-u16 ((me base-file-stream))
   "read 2 bytes from file"
   "read 2 bytes from file"
   (with-slots (endian instream) me
   (with-slots (endian instream) me
 	(read-octets instream 2 :endian endian)))
 	(read-octets instream 2 :endian endian)))
 
 
-(defmethod stream-read-u24 ((me base-stream))
+(defmethod stream-read-u24 ((me base-file-stream))
   "read 3 bytes from file"
   "read 3 bytes from file"
   (with-slots (endian instream) me
   (with-slots (endian instream) me
 	(read-octets instream 3 :endian endian)))
 	(read-octets instream 3 :endian endian)))
 
 
-(defmethod stream-read-u32 ((me base-stream))
+(defmethod stream-read-u32 ((me base-file-stream))
   "read 4 bytes from file"
   "read 4 bytes from file"
   (with-slots (endian instream) me
   (with-slots (endian instream) me
 	(read-octets instream 4 :endian endian)))
 	(read-octets instream 4 :endian endian)))
 
 
-(defmethod stream-read-string ((me base-stream) &key size (terminators nil))
+(defmethod stream-read-string ((me base-file-stream) &key size (terminators nil))
   "Read normal string from file. If size is provided, read exactly that many octets.
   "Read normal string from file. If size is provided, read exactly that many octets.
 If terminators is supplied, it is a list of characters that can terminate a string (and hence stop read)"
 If terminators is supplied, it is a list of characters that can terminate a string (and hence stop read)"
   (with-output-to-string (s)
   (with-output-to-string (s)
@@ -97,38 +93,48 @@ If terminators is supplied, it is a list of characters that can terminate a stri
 		  (when (if size (= count size) terminated) (return))
 		  (when (if size (= count size) terminated) (return))
 		  (setf byte (read-byte instream))
 		  (setf byte (read-byte instream))
 		  (incf count)
 		  (incf count)
-		  (log-stream "count = ~d, terminators = ~a, byte-read was ~c" count terminators (code-char byte))
+		  ;;;(log-stream "count = ~d, terminators = ~a, byte-read was ~c" count terminators (code-char byte))
 		  (when (member byte terminators :test #'=)
 		  (when (member byte terminators :test #'=)
 			(setf terminated t))
 			(setf terminated t))
 		  (when (not terminated)
 		  (when (not terminated)
 			(write-char (code-char byte) s)))))))
 			(write-char (code-char byte) s)))))))
 
 
-(defmethod stream-read-octets ((me base-stream) size)
-  "Read SIZE octets from input-file"
-  (let* ((octets (make-octets size))
-		 (read-len (read-sequence octets (slot-value me 'instream))))
-	(assert (= read-len size))
-	octets))
+(defmethod stream-read-octets ((me base-file-stream) size &key (bits-per-byte 8))
+  "Read SIZE octets from input-file.  If bits-per-byte"
+  (ecase bits-per-byte
+	(8
+	 (let ((octets (make-octets size)))
+	   (read-sequence octets (slot-value me 'instream))))
+	(7
+	 (let* ((last-byte-was-FF nil)
+			(byte nil)
+			(octets (ccl:with-output-to-vector (out)
+					  (dotimes (i size)
+						(setf byte (stream-read-u8 me))
+						(if last-byte-was-FF
+							(if (not (zerop byte))
+								(write-byte byte out))
+							(write-byte byte out))
+						(setf last-byte-was-FF (= byte #xFF))))))
+	   octets))))
 
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MP4 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
-(log5:defcategory cat-log-mp4-stream)
-(defmacro log-mp4-stream (&rest log-stuff) `(log5:log-for (cat-log-mp4-stream) ,@log-stuff))
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MP4 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
-(defclass mp4-stream (base-stream)
+(defclass mp4-stream (base-file-stream)
   ((mp4-atoms :accessor mp4-atoms :initform nil))
   ((mp4-atoms :accessor mp4-atoms :initform nil))
   (:documentation "Class to access m4a/mp4 files"))
   (:documentation "Class to access m4a/mp4 files"))
 
 
 (defun make-mp4-stream (filename read-only &key)
 (defun make-mp4-stream (filename read-only &key)
   "Convenience function to create an instance of MP4-FILE with appropriate init args"
   "Convenience function to create an instance of MP4-FILE with appropriate init args"
   (log5:with-context "make-mp4-stream"
   (log5:with-context "make-mp4-stream"
-	(log-mp4-stream "opening ~a" filename)
+	(log-stream "make-mp4-stream is opening ~a" filename)
 	(let (handle)
 	(let (handle)
 	  (handler-case 
 	  (handler-case 
 		  (progn
 		  (progn
-			(setf handle (make-instance 'mp4-stream :filename filename :endian :big-endian :read-only read-only))
+			(setf handle (make-instance 'mp4-stream :filename filename :endian :little-endian :read-only read-only))
 			(with-slots (mp4-atoms) handle
 			(with-slots (mp4-atoms) handle
-			  (log-mp4-stream "getting atoms")
+			  (log-stream "getting atoms")
 			  (setf mp4-atoms (mp4-atom:find-mp4-atoms handle))))
 			  (setf mp4-atoms (mp4-atom:find-mp4-atoms handle))))
 		(condition (c)
 		(condition (c)
 		  (warn "make-mp4-stream got condition: ~a" c)
 		  (warn "make-mp4-stream got condition: ~a" c)
@@ -137,11 +143,7 @@ If terminators is supplied, it is a list of characters that can terminate a stri
 		handle)))
 		handle)))
 
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MP3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MP3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(log5:defcategory cat-log-mp3-stream)
-
-(defmacro log-mp3-stream (&rest log-stuff) `(log5:log-for (cat-log-mp3-stream) ,@log-stuff))
-
-(defclass mp3-stream (base-stream)
+(defclass mp3-stream (base-file-stream)
   ((mp3-header :accessor mp3-header :initform nil))
   ((mp3-header :accessor mp3-header :initform nil))
   (:documentation "Class to access mp3 files"))
   (:documentation "Class to access mp3 files"))
 
 
@@ -149,13 +151,13 @@ If terminators is supplied, it is a list of characters that can terminate a stri
   "Convenience function to create an instance of MP3-FILE with appropriate init args.
   "Convenience function to create an instance of MP3-FILE with appropriate init args.
 NB: we assume non-syncsafe as default"
 NB: we assume non-syncsafe as default"
   (log5:with-context "make-mp3-stream"
   (log5:with-context "make-mp3-stream"
-	(log-mp3-stream "opening ~a" filename)
+	(log-stream "opening ~a, read-only = ~a" filename read-only)
 	(let (handle)
 	(let (handle)
-	  (handler-case 
+	  (handler-case
 		  (progn
 		  (progn
 			(setf handle (make-instance 'mp3-stream :filename filename :endian :big-endian :read-only read-only))
 			(setf handle (make-instance 'mp3-stream :filename filename :endian :big-endian :read-only read-only))
 			(with-slots (mp3-header) handle
 			(with-slots (mp3-header) handle
-			  (log-mp3-stream "getting frames")
+			  (log-stream "getting frames")
 			  (setf mp3-header (mp3-frame:find-mp3-frames handle))))
 			  (setf mp3-header (mp3-frame:find-mp3-frames handle))))
 		(condition (c)
 		(condition (c)
 		  (warn "make-mp3-stream got condition: ~a" c)
 		  (warn "make-mp3-stream got condition: ~a" c)
@@ -165,24 +167,21 @@ NB: we assume non-syncsafe as default"
 
 
 (defmethod stream-read-sync-safe-u32 ((me mp3-stream))
 (defmethod stream-read-sync-safe-u32 ((me mp3-stream))
   "Read a sync-safe integer from file.  Used by mp3 files"
   "Read a sync-safe integer from file.  Used by mp3 files"
-  (let* ((ret 0))
-	(setf (ldb (byte 7 21) ret) (stream-read-u8 me))
-	(setf (ldb (byte 7 14) ret) (stream-read-u8 me))
-	(setf (ldb (byte 7 7) ret)  (stream-read-u8 me))
-	(setf (ldb (byte 7 0) ret)  (stream-read-u8 me))
-	ret))
-
-(defmethod stream-read-sync-safe-octets ((me mp3-stream) len)
-  "Used to undo sync-safe read of file"
-  (let* ((last-byte-was-FF nil)
-		 (byte nil)
-		 (de-synced-data (flexi-streams:with-output-to-sequence (out :element-type 'octet)
-						   (dotimes (i len)
-							 (setf byte (stream-read-u8 me))
-							 (if last-byte-was-FF
-								 (if (not (zerop byte))
-									 (write-byte byte out))
-								 (write-byte byte out))
-							 (setf last-byte-was-FF (= byte #xFF))))))
-	de-synced-data))
-
+  (read-octets (slot-value me 'instream) 4 :bits-per-byte 7 :endian :little-endian))
+
+
+#|
+(defun tst ()
+  (let ((foo (ccl:with-output-to-vector (f) 
+			   (write-byte #xDE f)
+			   (write-byte #xAD f)
+			   (write-byte #xBE f)
+			   (write-byte #xEF f))))
+	(ccl:with-input-from-vector (f foo)
+	  (format t "Length is ~d~%" (ccl::stream-length f))
+	  (dotimes (j 2)
+		(format t "Iteration ~d~%" j)
+		(ccl::stream-position f 0)
+		(dotimes (i (ccl::stream-length f))
+		  (format t "~d: ~x~%" i (read-byte f)))))))
+|#

+ 1 - 1
taglib.asd

@@ -5,7 +5,7 @@
   :description "Pure Lisp implementation to read (and write?) tags"
   :description "Pure Lisp implementation to read (and write?) tags"
   :author "Mark VandenBrink"
   :author "Mark VandenBrink"
   :license "Public Domain"
   :license "Public Domain"
-  :depends-on (#:log5 #:flexi-streams #:alexandria)
+  :depends-on (#:log5 #:alexandria)
   :components ((:file "packages")
   :components ((:file "packages")
 			   (:file "tag"       :depends-on ("packages"))
 			   (:file "tag"       :depends-on ("packages"))
 			   (:file "streams"   :depends-on ("packages"))
 			   (:file "streams"   :depends-on ("packages"))