|
|
@@ -9,7 +9,7 @@
|
|
|
(deftype octet () '(unsigned-byte 8))
|
|
|
(defmacro make-octets (len) `(make-array ,len :element-type 'octet))
|
|
|
|
|
|
-(defclass base-stream ()
|
|
|
+(defclass base-file-stream ()
|
|
|
((filename :accessor filename :initarg :filename)
|
|
|
(instream :accessor instream :initform nil)
|
|
|
(endian :accessor endian :initarg :endian :initform nil) ; controls endian-ness of read/writes
|
|
|
@@ -17,17 +17,17 @@
|
|
|
(file-size :accessor file-size))
|
|
|
(: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
|
|
|
(setf instream (if read-only
|
|
|
(open filename :direction :input :element-type 'octet)
|
|
|
(open filename :direction :io :if-exists :overwrite :element-type 'octet)))
|
|
|
(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."
|
|
|
(with-slots (instream modified) me
|
|
|
(when modified
|
|
|
@@ -37,23 +37,19 @@
|
|
|
(close instream)
|
|
|
(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.
|
|
|
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
|
|
|
(ecase from
|
|
|
(: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))
|
|
|
- (ecase endian
|
|
|
+ (ecase endian
|
|
|
(:big-endian
|
|
|
(loop with value = 0
|
|
|
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))
|
|
|
finally (return value)))))
|
|
|
|
|
|
-(defmethod stream-read-u8 ((me base-stream))
|
|
|
+(defmethod stream-read-u8 ((me base-file-stream))
|
|
|
"read 1 byte from file"
|
|
|
(with-slots (endian instream) me
|
|
|
(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"
|
|
|
(with-slots (endian instream) me
|
|
|
(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"
|
|
|
(with-slots (endian instream) me
|
|
|
(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"
|
|
|
(with-slots (endian instream) me
|
|
|
(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.
|
|
|
If terminators is supplied, it is a list of characters that can terminate a string (and hence stop read)"
|
|
|
(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))
|
|
|
(setf byte (read-byte instream))
|
|
|
(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 #'=)
|
|
|
(setf terminated t))
|
|
|
(when (not terminated)
|
|
|
(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))
|
|
|
(:documentation "Class to access m4a/mp4 files"))
|
|
|
|
|
|
(defun make-mp4-stream (filename read-only &key)
|
|
|
"Convenience function to create an instance of MP4-FILE with appropriate init args"
|
|
|
(log5:with-context "make-mp4-stream"
|
|
|
- (log-mp4-stream "opening ~a" filename)
|
|
|
+ (log-stream "make-mp4-stream is opening ~a" filename)
|
|
|
(let (handle)
|
|
|
(handler-case
|
|
|
(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
|
|
|
- (log-mp4-stream "getting atoms")
|
|
|
+ (log-stream "getting atoms")
|
|
|
(setf mp4-atoms (mp4-atom:find-mp4-atoms handle))))
|
|
|
(condition (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)))
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 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))
|
|
|
(: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.
|
|
|
NB: we assume non-syncsafe as default"
|
|
|
(log5:with-context "make-mp3-stream"
|
|
|
- (log-mp3-stream "opening ~a" filename)
|
|
|
+ (log-stream "opening ~a, read-only = ~a" filename read-only)
|
|
|
(let (handle)
|
|
|
- (handler-case
|
|
|
+ (handler-case
|
|
|
(progn
|
|
|
(setf handle (make-instance 'mp3-stream :filename filename :endian :big-endian :read-only read-only))
|
|
|
(with-slots (mp3-header) handle
|
|
|
- (log-mp3-stream "getting frames")
|
|
|
+ (log-stream "getting frames")
|
|
|
(setf mp3-header (mp3-frame:find-mp3-frames handle))))
|
|
|
(condition (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))
|
|
|
"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)))))))
|
|
|
+|#
|