audio-streams.lisp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. ;;; -*- Mode: Lisp; show-trailing-whitespace: t; Base: 10; indent-tabs: nil; Syntax: ANSI-Common-Lisp; Package: AUDIO-STREAMS; -*-
  2. ;;; Copyright (c) 2013, Mark VandenBrink. All rights reserved.
  3. (in-package #:audio-streams)
  4. (eval-when (:compile-toplevel :load-toplevel :execute)
  5. (defconstant +optimize-fastest+ '(optimize (speed 3) (safety 0) (debug 0)))
  6. (defmacro fastest (&body body)
  7. `(locally (declare ,+optimize-fastest+)
  8. ,@body)))
  9. (log5:defcategory cat-log-stream)
  10. (defmacro log-stream (&rest log-stuff) `(log5:log-for (cat-log-stream) ,@log-stuff))
  11. (define-condition audio-stream-condition ()
  12. ((location :initarg :location :reader location :initform nil)
  13. (object :initarg :object :reader object :initform nil)
  14. (messsage :initarg :message :reader message :initform "Undefined Condition"))
  15. (:report (lambda (condition stream)
  16. (format stream "audio-stream condition at location: <~a> with object: <~a>: message: <~a>"
  17. (location condition) (object condition) (message condition)))))
  18. (defmethod print-object ((me audio-stream-condition) stream)
  19. (format stream "location: <~a>, object: <~a>, message: <~a>" (location me) (object me) (message me)))
  20. (deftype octet () '(unsigned-byte 8))
  21. (defmacro make-octets (len) `(make-array ,len :element-type 'octet))
  22. (defclass mem-stream ()
  23. ((fn :accessor fn :initform nil :initarg :fn)
  24. (index :accessor index :initform 0)
  25. (len :accessor len :initform 0)
  26. (vect :accessor vect :initform nil :initarg :vect)))
  27. (defmacro with-mem-stream-slots ((instance) &body body)
  28. `(with-slots (fn index len vect) ,instance
  29. (declare (integer index len))
  30. ;; XXX Breaks things: (type (simple-array (unsigned-byte 8) (*)) vect))
  31. ,@body))
  32. (defun make-mem-stream (v) (make-instance 'mem-stream :vect v))
  33. (defun make-mmap-stream (f) (make-instance 'mem-stream :fn f))
  34. (defmethod initialize-instance :after ((stream mem-stream) &key)
  35. (with-mem-stream-slots (stream)
  36. (when fn
  37. (setf vect (ccl:map-file-to-octet-vector fn)))
  38. (setf len (length vect))))
  39. (defmethod stream-close ((stream mem-stream))
  40. (with-mem-stream-slots (stream)
  41. (when fn
  42. (ccl:unmap-octet-vector vect))
  43. (setf vect nil)))
  44. (defmethod stream-seek ((stream mem-stream) &optional (offset 0) (from :current))
  45. (with-mem-stream-slots (stream)
  46. (ecase from
  47. (:start (setf index offset))
  48. (:current
  49. (if (zerop offset)
  50. index
  51. (incf index offset)))
  52. (:end (setf index (- len offset))))))
  53. ;;; probably should just rename :ACCESSOR LEN to STREAM-SIZE? XXX
  54. (defmethod stream-size ((stream mem-stream)) (len stream))
  55. (defun read-n-bytes (stream n-bytes &key (bits-per-byte 8))
  56. (fastest
  57. (with-mem-stream-slots (stream)
  58. (when (<= (+ index n-bytes) len)
  59. (loop with value = 0
  60. for low-bit downfrom (* bits-per-byte (1- n-bytes)) to 0 by bits-per-byte do
  61. (setf (ldb (byte bits-per-byte low-bit) value) (aref vect index))
  62. (incf index)
  63. finally (return-from read-n-bytes value))))
  64. nil))
  65. (declaim (inline read-n-bytes))
  66. (defmethod stream-read-u8 ((stream mem-stream) &key (bits-per-byte 8)) (read-n-bytes stream 1 :bits-per-byte bits-per-byte))
  67. (defmethod stream-read-u16 ((stream mem-stream) &key (bits-per-byte 8)) (read-n-bytes stream 2 :bits-per-byte bits-per-byte))
  68. (defmethod stream-read-u24 ((stream mem-stream) &key (bits-per-byte 8)) (read-n-bytes stream 3 :bits-per-byte bits-per-byte))
  69. (defmethod stream-read-u32 ((stream mem-stream) &key (bits-per-byte 8)) (read-n-bytes stream 4 :bits-per-byte bits-per-byte))
  70. (defmethod stream-read-u64 ((stream mem-stream) &key (bits-per-byte 8)) (read-n-bytes stream 8 :bits-per-byte bits-per-byte))
  71. (defmethod stream-read-sequence ((stream mem-stream) size &key (bits-per-byte 8))
  72. (fastest
  73. (with-mem-stream-slots (stream)
  74. (when (> (+ index size) len)
  75. (setf size (- len index)))
  76. (ecase bits-per-byte
  77. (8 (let ((octets (make-array size :element-type 'octet :displaced-to vect :displaced-index-offset index :adjustable nil)))
  78. (incf index size)
  79. (values octets size)))
  80. (7
  81. (let* ((last-byte-was-FF nil)
  82. (byte nil)
  83. (octets (ccl:with-output-to-vector (out)
  84. (dotimes (i size)
  85. (setf byte (stream-read-u8 stream))
  86. (if last-byte-was-FF
  87. (if (not (zerop byte))
  88. (write-byte byte out))
  89. (write-byte byte out))
  90. (setf last-byte-was-FF (= byte #xFF))))))
  91. (values octets size)))))))
  92. (defclass mp3-file-stream (mem-stream)
  93. ((id3-header :accessor id3-header :initform nil :documentation "holds all the ID3 info")
  94. (audio-info :accessor audio-info :initform nil :documentation "holds the bit-rate, etc info"))
  95. (:documentation "Stream for parsing MP3 files"))
  96. (defclass mp4-file-stream (mem-stream)
  97. ((mp4-atoms :accessor mp4-atoms :initform nil :documentation "holds tree of parsed MP4 atoms/boxes")
  98. (audio-info :accessor audio-info :initform nil :documentation "holds the bit-rate, etc info"))
  99. (:documentation "Stream for parsing MP4A files"))
  100. (defun make-file-stream (filename)
  101. "Convenience function for creating a file stream."
  102. (let ((new-stream (cond ((utils:has-extension filename "m4a") (make-instance 'mp4-file-stream :fn filename))
  103. ((utils:has-extension filename "mp3") (make-instance 'mp3-file-stream :fn filename))
  104. (t (error "unknown filename extension for file ~a" filename)))))
  105. new-stream))
  106. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Strings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  107. ;;;
  108. ;;; Decode octets as an iso-8859-1 string (encoding == 0)
  109. (defun stream-decode-iso-string (octets &key (start 0) (end nil))
  110. (ccl:decode-string-from-octets octets :start start :end end :external-format :iso-8859-1))
  111. ;;;
  112. ;;; XXX: Coded this way because I can't seem to get a simple :external-format :ucs-2 to work correctly
  113. ;;; AND some taggers encode a UCS-2 empty string w/o a byte-order mark (i.e. null strings are
  114. ;;; sometimes encoded as #(00 00))
  115. (defun stream-decode-ucs-string (octets &key (start 0) (end nil))
  116. "Decode octets as a UCS string with a BOM (encoding == 1)"
  117. (labels ((get-byte-order-mark (octets)
  118. (let ((retval 0))
  119. (setf (ldb (byte 8 0) retval) (aref octets 1))
  120. (setf (ldb (byte 8 8) retval) (aref octets 0))
  121. (when (not (or (= #xfffe retval) (= #xfeff retval)))
  122. (error 'audio-stream-condition
  123. :location "stream-decode-ucs-string"
  124. :object nil
  125. :message (format nil "got an invalid byte-order mark of ~x" retval)))
  126. retval)))
  127. ;; special case: empty (and mis-coded) string
  128. (cond ((zerop (length octets))
  129. (make-string 0))
  130. (t
  131. ;;
  132. ;; else, we have a (hopefully) properly encoded string
  133. (let ((bom (get-byte-order-mark octets)))
  134. (ecase (the fixnum bom)
  135. (#xfffe (ccl:decode-string-from-octets octets :start (+ 2 start) :end end :external-format :ucs-2le))
  136. (#xfeff (ccl:decode-string-from-octets octets :start (+ 2 start) :end end :external-format :ucs-2be))
  137. (0 (make-string 0))))))))
  138. (defun stream-decode-ucs-be-string (octets &key (start 0) (end nil))
  139. "Decode octets as a UCS-BE string (encoding == 2)"
  140. (ccl:decode-string-from-octets octets :start start :end end :external-format :ucs-2be))
  141. (defun stream-decode-utf-8-string (octets &key (start 0) (end nil))
  142. "Decode octets as a utf-8 string"
  143. (ccl:decode-string-from-octets octets :start start :end end :external-format :utf-8))
  144. (defun stream-decode-string (octets &key (start 0) (end nil) (encoding 0))
  145. "Decode octets depending on encoding"
  146. (ecase encoding
  147. (0 (stream-decode-iso-string octets :start start :end end))
  148. (1 (stream-decode-ucs-string octets :start start :end end))
  149. (2 (stream-decode-ucs-be-string octets :start start :end end))
  150. (3 (stream-decode-utf-8-string octets :start start :end end))))
  151. (defmethod stream-read-iso-string-with-len ((instream mem-stream) len)
  152. "Read an iso-8859-1 string of length 'len' (encoding = 0)"
  153. (let ((octets (stream-read-sequence instream len)))
  154. (stream-decode-iso-string octets)))
  155. (defmethod stream-read-ucs-string-with-len ((instream mem-stream) len)
  156. "Read an ucs-2 string of length 'len' (encoding = 1)"
  157. (let ((octets (stream-read-sequence instream len)))
  158. (stream-decode-ucs-string octets)))
  159. (defmethod stream-read-ucs-be-string-with-len ((instream mem-stream) len)
  160. "Read an ucs-2-be string of length 'len' (encoding = 2)"
  161. (let ((octets (stream-read-sequence instream len)))
  162. (stream-decode-ucs-be-string octets)))
  163. (defmethod stream-read-utf-8-string-with-len ((instream mem-stream) len)
  164. "Read an utf-8 string of length 'len' (encoding = 3)"
  165. (let ((octets (stream-read-sequence instream len)))
  166. (stream-decode-utf-8-string octets)))
  167. (defmethod stream-read-string-with-len ((instream mem-stream) len &key (encoding 0))
  168. "Read in a string of a given encoding of length 'len'"
  169. (ecase encoding
  170. (0 (stream-read-iso-string-with-len instream len))
  171. (1 (stream-read-ucs-string-with-len instream len))
  172. (2 (stream-read-ucs-be-string-with-len instream len))
  173. (3 (stream-read-utf-8-string-with-len instream len))))
  174. (defmethod stream-read-iso-string ((instream mem-stream))
  175. "Read in a null terminated iso-8859-1 string"
  176. (let ((octets (ccl:with-output-to-vector (out)
  177. (do ((b (stream-read-u8 instream) (stream-read-u8 instream)))
  178. (nil)
  179. (when (zerop b)
  180. (return)) ; leave loop w/o writing
  181. (write-byte b out)))))
  182. (stream-decode-iso-string octets)))
  183. (defmethod stream-read-ucs-string ((instream mem-stream))
  184. "Read in a null terminated UCS string."
  185. (let ((octets (ccl:with-output-to-vector (out)
  186. (do* ((b0 (stream-read-u8 instream)
  187. (stream-read-u8 instream))
  188. (b1 (stream-read-u8 instream)
  189. (stream-read-u8 instream)))
  190. (nil)
  191. (when (and (zerop b0) (zerop b1))
  192. (return))
  193. (write-byte b0 out)
  194. (write-byte b1 out)))))
  195. (stream-decode-ucs-string octets)))
  196. (defmethod stream-read-ucs-be-string ((instream mem-stream))
  197. "Read in a null terminated UCS-BE string."
  198. (let ((octets (ccl:with-output-to-vector (out)
  199. (do* ((b0 (stream-read-u8 instream)
  200. (stream-read-u8 instream))
  201. (b1 (stream-read-u8 instream)
  202. (stream-read-u8 instream)))
  203. (nil)
  204. (when (and (zerop b0) (zerop b1))
  205. (return))
  206. (write-byte b0 out)
  207. (write-byte b1 out)))))
  208. (stream-decode-ucs-be-string octets)))
  209. (defmethod stream-read-utf-8-string ((instream mem-stream))
  210. "Read in a null terminated utf-8 string (encoding == 3)"
  211. (let ((octets (ccl:with-output-to-vector (out)
  212. (do ((b (stream-read-u8 instream)
  213. (stream-read-u8 instream)))
  214. (nil)
  215. (when (zerop b)
  216. (return))
  217. (write-byte b out)))))
  218. (stream-decode-utf-8-string octets)))
  219. (defmethod stream-read-string ((instream mem-stream) &key (encoding 0))
  220. "Read in a null terminated string of a given encoding."
  221. (ecase encoding
  222. (0 (stream-read-iso-string instream))
  223. (1 (stream-read-ucs-string instream))
  224. (2 (stream-read-ucs-be-string instream))
  225. (3 (stream-read-utf-8-string instream))))
  226. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Files ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  227. (defvar *get-audio-info* t "controls whether the parsing functions also parse audio info like bit-rate, etc")
  228. (defun parse-mp4-file (filename &key (get-audio-info *get-audio-info*))
  229. "Parse an MP4A file by reading it's ATOMS and decoding them."
  230. (let (stream)
  231. (handler-case
  232. (progn
  233. (setf stream (make-file-stream filename))
  234. (mp4-atom:find-mp4-atoms stream)
  235. (when get-audio-info
  236. (setf (audio-info stream) (mp4-atom:get-mp4-audio-info stream))))
  237. (mp4-atom:mp4-atom-condition (c)
  238. (utils:warn-user "make-mp4-stream got condition: ~a" c)
  239. (when stream (stream-close stream))
  240. (setf stream nil)))
  241. stream))
  242. (defun parse-mp3-file (filename &key (get-audio-info *get-audio-info*))
  243. "Parse an MP3 file by reading it's FRAMES and decoding them."
  244. (let (stream)
  245. (handler-case
  246. (progn
  247. (setf stream (make-file-stream filename))
  248. (id3-frame:find-id3-frames stream)
  249. (when get-audio-info
  250. (setf (audio-info stream) (mpeg:get-mpeg-audio-info stream))))
  251. (id3-frame:id3-frame-condition (c)
  252. (utils:warn-user "make-mp3-stream got condition: ~a" c)
  253. (when stream (stream-close stream))
  254. (setf stream nil)))
  255. stream))