audio-streams.lisp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  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. (deftype octet () '(unsigned-byte 8))
  5. (deftype octets () '(simple-array octet (*)))
  6. (defmacro make-octets (len) `(make-array ,len :element-type 'octet))
  7. (defun make-audio-stream (arg)
  8. "Creates a stream for ARG"
  9. (declare #.utils:*standard-optimize-settings*)
  10. (labels ((make-file-stream (name)
  11. (let ((fd (open name :direction :input :element-type 'octet)))
  12. (if fd
  13. (flex:make-flexi-stream fd :element-type 'octet)
  14. nil))))
  15. (etypecase arg
  16. (string (make-file-stream arg))
  17. (pathname (make-file-stream arg))
  18. (octets (flex:make-in-memory-input-stream arg)))))
  19. (defgeneric stream-size (stream))
  20. (defmethod stream-size ((stream flex:flexi-input-stream))
  21. (declare #.utils:*standard-optimize-settings*)
  22. (file-length (flex:flexi-stream-stream stream)))
  23. (defmethod stream-size ((stream flex:in-memory-stream))
  24. (declare #.utils:*standard-optimize-settings*)
  25. (flex::vector-stream-end stream))
  26. (defgeneric stream-filename (stream))
  27. (defmethod stream-filename ((stream flex:flexi-stream))
  28. (declare #.utils:*standard-optimize-settings*)
  29. (pathname (flex:flexi-stream-stream stream)))
  30. (defgeneric stream-seek (stream &optional offset from))
  31. (defmethod stream-seek ((stream flex:flexi-stream)
  32. &optional (offset 0) (from :current))
  33. "Move the FILE-POSITION of a file"
  34. (declare #.utils:*standard-optimize-settings*)
  35. (declare (fixnum offset))
  36. (ecase from
  37. (:start (file-position stream offset))
  38. (:current (file-position stream (+ (file-position stream) offset)))
  39. (:end (file-position stream (- (stream-size stream)
  40. offset)))))
  41. (defmethod stream-seek ((stream flex:in-memory-input-stream)
  42. &optional (offset 0) (from :current))
  43. "Move the index of an in-memory stream"
  44. (declare #.utils:*standard-optimize-settings*)
  45. (ecase from
  46. (:start (file-position stream offset))
  47. (:current (file-position stream (+ (file-position stream) offset)))
  48. (:end (file-position stream (- (stream-size stream) offset)))))
  49. (declaim (inline read-n-bytes))
  50. (defun read-n-bytes (stream n-bytes
  51. &key (bits-per-byte 8) (endian :little-endian))
  52. "Returns a FIXNUM constructed by reading N-BYTES. BITS-PER-BYTE controls how
  53. many bits should be used from each read byte."
  54. (declare #.utils:*standard-optimize-settings*)
  55. (declare (fixnum n-bytes))
  56. (ecase endian
  57. (:little-endian
  58. (loop with value = 0
  59. for low-bit downfrom (* bits-per-byte (1- n-bytes)) to 0
  60. by bits-per-byte do
  61. (awhen (read-byte stream nil nil)
  62. (setf (ldb (byte bits-per-byte low-bit) value) it))
  63. finally (return-from read-n-bytes value)))
  64. (:big-endian
  65. (loop with value = 0
  66. for low-bit upfrom 0 to (* bits-per-byte (1- n-bytes))
  67. by bits-per-byte do
  68. (awhen (read-byte stream nil nil)
  69. (setf (ldb (byte bits-per-byte low-bit) value) it))
  70. finally (return-from read-n-bytes value)))))
  71. (defun stream-read-u8 (stream)
  72. (declare #.utils:*standard-optimize-settings*)
  73. (read-byte stream nil nil))
  74. (defun stream-read-u16 (stream &key (bits-per-byte 8) (endian :little-endian))
  75. (read-n-bytes stream 2 :bits-per-byte bits-per-byte :endian endian))
  76. (defun stream-read-u24 (stream &key (bits-per-byte 8) (endian :little-endian))
  77. (read-n-bytes stream 3 :bits-per-byte bits-per-byte :endian endian))
  78. (defun stream-read-u32 (stream &key (bits-per-byte 8) (endian :little-endian))
  79. (read-n-bytes stream 4 :bits-per-byte bits-per-byte :endian endian))
  80. (defun stream-read-u64 (stream &key (bits-per-byte 8) (endian :little-endian))
  81. (read-n-bytes stream 8 :bits-per-byte bits-per-byte :endian endian))
  82. (defun stream-read-u128 (stream &key (bits-per-byte 8) (endian :little-endian))
  83. (read-n-bytes stream 16 :bits-per-byte bits-per-byte :endian endian))
  84. (defun stream-read-sequence (stream size &key (bits-per-byte 8))
  85. "Read in a sequence of octets at BITS-PER-BYTE"
  86. (declare #.utils:*standard-optimize-settings*)
  87. (ecase bits-per-byte
  88. (8 (let ((octets (make-octets size)))
  89. (values octets (read-sequence octets stream))))
  90. (7 (let* ((last-byte-was-FF nil)
  91. (byte nil)
  92. (octets (flex:with-output-to-sequence (out)
  93. (dotimes (i size)
  94. (setf byte (stream-read-u8 stream))
  95. (if last-byte-was-FF
  96. (if (not (zerop byte))
  97. (write-byte byte out))
  98. (write-byte byte out))
  99. (setf last-byte-was-FF (= byte #xFF))))))
  100. (values octets size)))))
  101. ;;;; Strings
  102. ;;; Decode octets as an iso-8859-1 string (encoding == 0)
  103. (defun stream-decode-iso-string (octets &key (start 0) (end (length octets)))
  104. (declare #.utils:*standard-optimize-settings*)
  105. (flex:octets-to-string octets :start start
  106. :end end :external-format :iso-8859-1))
  107. ;;;
  108. ;;; XXX: Coded this way because I can't seem to get a simple :external-format
  109. ;;; :ucs-2 to work correctly AND some taggers encode a UCS-2 empty string w/o
  110. ;;; a byte-order mark (i.e. null strings are sometimes encoded as #(00 00))
  111. (defun stream-decode-ucs-string (octets &key (start 0) (end (length octets)))
  112. "Decode octets as a UCS string with a BOM (encoding == 1)"
  113. (declare #.utils:*standard-optimize-settings*)
  114. (labels ((get-byte-order-mark (octets)
  115. (let ((retval 0))
  116. (setf (ldb (byte 8 0) retval) (aref octets 1)
  117. (ldb (byte 8 8) retval) (aref octets 0))
  118. (when (not (or (= #xfffe retval) (= #xfeff retval)))
  119. (error
  120. "Got invalid byte-order mark of ~x in STREAM-DECODE-UCS-STRING"
  121. retval))
  122. retval)))
  123. ;; special case: empty (and mis-coded) string
  124. (cond ((zerop (length octets))
  125. (make-string 0))
  126. (t
  127. ;;
  128. ;; else, we have a (hopefully) properly encoded string
  129. (when (oddp end)
  130. (warn-user
  131. "Malformed UCS string, length (~d) is odd---decrementing by 1"
  132. end)
  133. (setf end (1- end)))
  134. (let ((bom (get-byte-order-mark octets)))
  135. (ecase (the fixnum bom)
  136. (#xfffe (flex:octets-to-string octets
  137. :start (+ 2 start)
  138. :end end
  139. :external-format :ucs-2le))
  140. (#xfeff (flex:octets-to-string octets
  141. :start (+ 2 start)
  142. :end end
  143. :external-format :ucs-2be))
  144. (0 (make-string 0))))))))
  145. (defun stream-decode-ucs-be-string (octets &key (start 0) (end (length octets)))
  146. "Decode octets as a UCS-BE string (encoding == 2)"
  147. (declare #.utils:*standard-optimize-settings*)
  148. (flex:octets-to-string octets :start start
  149. :end end :external-format :ucs-2be))
  150. (defun stream-decode-utf-8-string (octets &key (start 0) (end (length octets)))
  151. "Decode octets as a utf-8 string"
  152. (declare #.utils:*standard-optimize-settings*)
  153. (flex:octets-to-string octets :start start :end end :external-format :utf-8))
  154. (defun stream-decode-string (octets &key (start 0)
  155. (end (length octets))
  156. (encoding 0))
  157. "Decode octets depending on encoding"
  158. (declare #.utils:*standard-optimize-settings*)
  159. (ecase encoding
  160. (0 (stream-decode-iso-string octets :start start :end end))
  161. (1 (stream-decode-ucs-string octets :start start :end end))
  162. (2 (stream-decode-ucs-be-string octets :start start :end end))
  163. (3 (stream-decode-utf-8-string octets :start start :end end))))
  164. (defun stream-read-iso-string-with-len (instream len)
  165. "Read an iso-8859-1 string of length 'len' (encoding = 0)"
  166. (declare #.utils:*standard-optimize-settings*)
  167. (stream-decode-iso-string (stream-read-sequence instream len)))
  168. (defun stream-read-ucs-string-with-len (instream len)
  169. "Read an ucs-2 string of length 'len' (encoding = 1)"
  170. (declare #.utils:*standard-optimize-settings*)
  171. (stream-decode-ucs-string (stream-read-sequence instream len)))
  172. (defun stream-read-ucs-be-string-with-len (instream len)
  173. "Read an ucs-2-be string of length 'len' (encoding = 2)"
  174. (declare #.utils:*standard-optimize-settings*)
  175. (stream-decode-ucs-be-string (stream-read-sequence instream len)))
  176. (defun stream-read-utf-8-string-with-len (instream len)
  177. "Read an utf-8 string of length 'len' (encoding = 3)"
  178. (declare #.utils:*standard-optimize-settings*)
  179. (stream-decode-utf-8-string (stream-read-sequence instream len)))
  180. (defun stream-read-string-with-len (instream len &key (encoding 0))
  181. "Read in a string of a given encoding of length 'len'"
  182. (declare #.utils:*standard-optimize-settings*)
  183. (ecase encoding
  184. (0 (stream-read-iso-string-with-len instream len))
  185. (1 (stream-read-ucs-string-with-len instream len))
  186. (2 (stream-read-ucs-be-string-with-len instream len))
  187. (3 (stream-read-utf-8-string-with-len instream len))))
  188. (defun stream-read-iso-string (instream)
  189. "Read in a null-terminated iso-8859-1 string"
  190. (declare #.utils:*standard-optimize-settings*)
  191. (let ((octets (flex:with-output-to-sequence (out)
  192. (do ((b (stream-read-u8 instream) (stream-read-u8 instream)))
  193. (nil)
  194. (when (zerop b)
  195. (return)) ; leave loop w/o writing
  196. (write-byte b out)))))
  197. (stream-decode-iso-string octets)))
  198. (defun stream-read-ucs-string (instream)
  199. "Read in a null-terminated UCS string."
  200. (declare #.utils:*standard-optimize-settings*)
  201. (let ((octets (flex:with-output-to-sequence (out)
  202. (do* ((b0 (stream-read-u8 instream)
  203. (stream-read-u8 instream))
  204. (b1 (stream-read-u8 instream)
  205. (stream-read-u8 instream)))
  206. (nil)
  207. (when (and (zerop b0) (zerop b1))
  208. (return))
  209. (write-byte b0 out)
  210. (write-byte b1 out)))))
  211. (stream-decode-ucs-string octets)))
  212. (defun stream-read-ucs-be-string (instream)
  213. "Read in a null-terminated UCS-BE string."
  214. (declare #.utils:*standard-optimize-settings*)
  215. (let ((octets (flex:with-output-to-sequence (out)
  216. (do* ((b0 (stream-read-u8 instream)
  217. (stream-read-u8 instream))
  218. (b1 (stream-read-u8 instream)
  219. (stream-read-u8 instream)))
  220. (nil)
  221. (when (and (zerop b0) (zerop b1))
  222. (return))
  223. (write-byte b0 out)
  224. (write-byte b1 out)))))
  225. (stream-decode-ucs-be-string octets)))
  226. (defun stream-read-utf-8-string (instream)
  227. "Read in a null-terminated utf-8 string (encoding == 3)"
  228. (declare #.utils:*standard-optimize-settings*)
  229. (let ((octets (flex:with-output-to-sequence (out)
  230. (do ((b (stream-read-u8 instream)
  231. (stream-read-u8 instream)))
  232. (nil)
  233. (when (zerop b)
  234. (return))
  235. (write-byte b out)))))
  236. (stream-decode-utf-8-string octets)))
  237. (defun stream-read-string (instream &key (encoding 0))
  238. "Read in a null-terminated string of a given encoding."
  239. (declare #.utils:*standard-optimize-settings*)
  240. (ecase encoding
  241. (0 (stream-read-iso-string instream))
  242. (1 (stream-read-ucs-string instream))
  243. (2 (stream-read-ucs-be-string instream))
  244. (3 (stream-read-utf-8-string instream))))
  245. ;;;; Files
  246. (defvar *get-audio-info* t
  247. "controls whether the parsing functions parse audio info like bit-rate, etc")
  248. (defun open-audio-file (filename &optional (get-audio-info *get-audio-info*))
  249. "Open and parse FILENAME"
  250. (declare #.utils:*standard-optimize-settings*)
  251. (let ((stream)
  252. (info))
  253. (unwind-protect
  254. (progn
  255. (setf stream (make-audio-stream filename))
  256. (when stream
  257. (setf info
  258. (cond ((id3-frame:is-valid-mp3-file stream)
  259. (id3-frame:parse-audio-file stream get-audio-info))
  260. ((mp4-atom:is-valid-m4-file stream)
  261. (mp4-atom:parse-audio-file stream get-audio-info))
  262. ((flac-frame:is-valid-flac-file stream)
  263. (flac-frame:parse-audio-file stream get-audio-info))
  264. (t nil)))))
  265. (when stream
  266. (close stream)))
  267. info))