streams.lisp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. ;;; -*- Mode: Lisp; show-trailing-whitespace: t; Base: 10; indent-tabs: nil; Syntax: ANSI-Common-Lisp; Package: STREAMS; -*-
  2. ;;; Copyright (c) 2013, Mark VandenBrink. All rights reserved.
  3. (in-package #:audio-streams)
  4. (log5:defcategory cat-log-stream)
  5. (defmacro log-stream (&rest log-stuff) `(log5:log-for (cat-log-stream) ,@log-stuff))
  6. (deftype octet () '(unsigned-byte 8))
  7. (defmacro make-octets (len) `(make-array ,len :element-type 'octet))
  8. (defclass base-file-stream ()
  9. ((filename :accessor filename :initarg :filename)
  10. (instream :accessor instream :initform nil)
  11. (endian :accessor endian :initarg :endian :initform nil) ; controls endian-ness of read/writes
  12. (modified :accessor modified :initform nil) ; for when we implement writing tags
  13. (file-size :accessor file-size))
  14. (:documentation "Base class for all audio file types"))
  15. (defmethod initialize-instance :after ((me base-file-stream) &key read-only &allow-other-keys)
  16. (log5:with-context "base-file-stream-initializer"
  17. (with-slots (instream filename file-size endian) me
  18. (setf instream (if read-only
  19. (open filename :direction :input :element-type 'octet)
  20. (open filename :direction :io :if-exists :overwrite :element-type 'octet)))
  21. (setf file-size (file-length instream))
  22. (log-stream "base-file-stream-initializer built stream = ~a, name = ~a, size = ~:d, endian = ~a"
  23. instream filename file-size endian))))
  24. (defmethod stream-close ((me base-file-stream))
  25. "Close an open stream."
  26. (with-slots (instream modified) me
  27. (when modified
  28. (warn "at some point, should I add code to auto-write modified audio-files?")
  29. (setf modified nil))
  30. (when instream
  31. (close instream)
  32. (setf instream nil))))
  33. (defmethod stream-seek ((me base-file-stream) offset from)
  34. "C-library like seek function. from can be one of :current, :start, :end.
  35. Returns the current offset into the stream"
  36. (with-slots (instream file-size) me
  37. (ecase from
  38. (:start (file-position instream offset))
  39. (:current (file-position instream (+ (file-position instream) offset)))
  40. (:end (file-position instream (- file-size offset))))))
  41. ;;
  42. ;; Based on a function from Practical Common Lisp by Peter Seibel.
  43. (defun read-octets (instream bytes &key (bits-per-byte 8) (endian :little-endian))
  44. (ecase endian
  45. (:big-endian
  46. (loop with value = 0
  47. for low-bit from 0 to (* bits-per-byte (1- bytes)) by bits-per-byte do
  48. (setf (ldb (byte bits-per-byte low-bit) value) (read-byte instream))
  49. finally (return value)))
  50. (:little-endian
  51. (loop with value = 0
  52. for low-bit downfrom (* bits-per-byte (1- bytes)) to 0 by bits-per-byte do
  53. (setf (ldb (byte bits-per-byte low-bit) value) (read-byte instream))
  54. finally (return value)))))
  55. (defmethod stream-read-u8 ((me base-file-stream))
  56. "read 1 byte from file"
  57. (with-slots (endian instream) me
  58. (read-octets instream 1 :endian endian)))
  59. (defmethod stream-read-u16 ((me base-file-stream))
  60. "read 2 bytes from file"
  61. (with-slots (endian instream) me
  62. (read-octets instream 2 :endian endian)))
  63. (defmethod stream-read-u24 ((me base-file-stream))
  64. "read 3 bytes from file"
  65. (with-slots (endian instream) me
  66. (read-octets instream 3 :endian endian)))
  67. (defmethod stream-read-u32 ((me base-file-stream))
  68. "read 4 bytes from file"
  69. (with-slots (endian instream) me
  70. (read-octets instream 4 :endian endian)))
  71. (defmethod stream-read-string ((me base-file-stream) &key size (terminators nil))
  72. "Read normal string from file. If size is provided, read exactly that many octets.
  73. If terminators is supplied, it is a list of characters that can terminate a string (and hence stop read)"
  74. (with-output-to-string (s)
  75. (with-slots (instream) me
  76. (let ((terminated nil)
  77. (count 0)
  78. (byte))
  79. (loop
  80. (when (if size (= count size) terminated) (return))
  81. (setf byte (read-byte instream))
  82. (incf count)
  83. ;;;(log-stream "count = ~d, terminators = ~a, byte-read was ~c" count terminators (code-char byte))
  84. (when (member byte terminators :test #'=)
  85. (setf terminated t))
  86. (when (not terminated)
  87. (write-char (code-char byte) s)))))))
  88. (defmethod stream-read-octets ((me base-file-stream) size &key (bits-per-byte 8))
  89. "Read SIZE octets from input-file. If bits-per-byte"
  90. (ecase bits-per-byte
  91. (8
  92. (let ((octets (make-octets size)))
  93. (read-sequence octets (slot-value me 'instream))))
  94. (7
  95. (let* ((last-byte-was-FF nil)
  96. (byte nil)
  97. (octets (ccl:with-output-to-vector (out)
  98. (dotimes (i size)
  99. (setf byte (stream-read-u8 me))
  100. (if last-byte-was-FF
  101. (if (not (zerop byte))
  102. (write-byte byte out))
  103. (write-byte byte out))
  104. (setf last-byte-was-FF (= byte #xFF))))))
  105. octets))))
  106. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MP4 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  107. (defclass mp4-stream (base-file-stream)
  108. ((mp4-atoms :accessor mp4-atoms :initform nil))
  109. (:documentation "Class to access m4a/mp4 files"))
  110. (defun make-mp4-stream (filename read-only &key)
  111. "Convenience function to create an instance of MP4-FILE with appropriate init args"
  112. (log5:with-context "make-mp4-stream"
  113. (log-stream "make-mp4-stream is opening ~a" filename)
  114. (let (handle)
  115. (handler-case
  116. (progn
  117. (setf handle (make-instance 'mp4-stream :filename filename :endian :little-endian :read-only read-only))
  118. (with-slots (mp4-atoms) handle
  119. (log-stream "getting atoms")
  120. (setf mp4-atoms (mp4-atom:find-mp4-atoms handle))))
  121. (condition (c)
  122. (warn "make-mp4-stream got condition: ~a" c)
  123. (when handle (stream-close handle))
  124. (setf handle nil)))
  125. handle)))
  126. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MP3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  127. (defclass mp3-stream (base-file-stream)
  128. ((mp3-header :accessor mp3-header :initform nil))
  129. (:documentation "Class to access mp3 files"))
  130. (defun make-mp3-stream (filename read-only &key)
  131. "Convenience function to create an instance of MP3-FILE with appropriate init args.
  132. NB: we assume non-syncsafe as default"
  133. (log5:with-context "make-mp3-stream"
  134. (log-stream "opening ~a, read-only = ~a" filename read-only)
  135. (let (handle)
  136. (handler-case
  137. (progn
  138. (setf handle (make-instance 'mp3-stream :filename filename :endian :big-endian :read-only read-only))
  139. (with-slots (mp3-header) handle
  140. (log-stream "getting frames")
  141. (setf mp3-header (mp3-frame:find-mp3-frames handle))))
  142. (condition (c)
  143. (warn "make-mp3-stream got condition: ~a" c)
  144. (when handle (stream-close handle))
  145. (setf handle nil)))
  146. handle)))
  147. (defmethod stream-read-sync-safe-u32 ((me mp3-stream))
  148. "Read a sync-safe integer from file. Used by mp3 files"
  149. (read-octets (slot-value me 'instream) 4 :bits-per-byte 7 :endian :little-endian))
  150. #|
  151. (defun tst ()
  152. (let ((foo (ccl:with-output-to-vector (f)
  153. (write-byte #xDE f)
  154. (write-byte #xAD f)
  155. (write-byte #xBE f)
  156. (write-byte #xEF f))))
  157. (ccl:with-input-from-vector (f foo)
  158. (format t "Length is ~d~%" (ccl::stream-length f))
  159. (dotimes (j 2)
  160. (format t "Iteration ~d~%" j)
  161. (ccl::stream-position f 0)
  162. (dotimes (i (ccl::stream-length f))
  163. (format t "~d: ~x~%" i (read-byte f)))))))
  164. |#