base-file.lisp 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. ;;; -*- Mode: Lisp; show-trailing-whitespace: t; Base: 10; indent-tabs: nil; Syntax: ANSI-Common-Lisp; Package: BASE-FILE; -*-
  2. ;;; Copyright (c) 2013, Mark VandenBrink. All rights reserved.
  3. (in-package #:base-file)
  4. (log5:defcategory cat-log-base-file)
  5. (defmacro log-base-file (&rest log-stuff) `(log5:log-for (cat-log-base-file) ,@log-stuff))
  6. (deftype octet () '(unsigned-byte 8))
  7. (defmacro make-octets (len) `(make-array ,len :element-type 'octet))
  8. (defclass base-file ()
  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) &key read-only &allow-other-keys)
  16. (log5:with-context "base-file-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 binary-types:*endian* endian)
  22. (setf file-size (file-length instream))
  23. (log-base-file "stream = ~a, name = ~a, size = ~:d~%endian = ~a"
  24. instream filename file-size endian))))
  25. (defmethod close-audio-file ((me base-file))
  26. "Close an open file"
  27. (with-slots (instream modified) me
  28. (when modified
  29. (warn "at some point, should I add code to auto-write modified audio-files?")
  30. (setf modified nil))
  31. (when instream
  32. (close instream)
  33. (setf instream nil))))
  34. (defmethod seek ((me base-file) offset from)
  35. "C-library like seek function. from can be one of :current, :start, :end.
  36. Returns the current offset into the stream"
  37. (assert (member from '(:current :start :end)) () "seek takes one of :current, :start, :end")
  38. (with-slots (instream file-size) me
  39. (ecase from
  40. (:start (file-position instream offset))
  41. (:current
  42. (let ((current (file-position instream)))
  43. (file-position instream (+ current offset))))
  44. (:end
  45. (file-position instream (- file-size offset))))))
  46. (defmethod read-u8 ((me base-file))
  47. "read 1 byte from file"
  48. (multiple-value-bind (value size) (binary-types:read-binary 'u8 (slot-value me 'instream))
  49. (assert (= size 1) () "Expected to read 1 byte, got ~d instead" size)
  50. value))
  51. (defmethod read-u16 ((me base-file))
  52. "read 2 bytes from file"
  53. (multiple-value-bind (value size) (binary-types:read-binary 'u16 (slot-value me 'instream))
  54. (assert (= size 2) () "Expected to read 2 bytes, got ~d instead" size)
  55. value))
  56. ;;; read 3-bytes
  57. (binary-types:define-unsigned u24 3)
  58. (defmethod read-u24 ((me base-file))
  59. "read 3 bytes from file"
  60. (multiple-value-bind (value size) (binary-types:read-binary 'u24 (slot-value me 'instream))
  61. (assert (= size 3) () "Expected to read 3 bytes, got ~d instead" size)
  62. value))
  63. (defmethod read-u32 ((me base-file))
  64. "read 4 bytes from file"
  65. (multiple-value-bind (value size) (binary-types:read-binary 'u32 (slot-value me 'instream))
  66. (assert (= size 4) () "Expected to read 4 bytes, got ~d instead" size)
  67. value))
  68. (defmethod read-string ((me base-file) &key size (terminators nil))
  69. "Read normal string from file. If size is provided, read exactly that many octets.
  70. If terminators is supplied, it is a list of characters that can terminate a string (and hence stop read)"
  71. (multiple-value-bind (value read-size)
  72. (binary-types:read-binary-string (slot-value me 'instream) :size size :terminators terminators)
  73. (declare (ignore read-size))
  74. ;; what checks should happen here?
  75. value))
  76. (defmethod read-octets ((me base-file) size)
  77. "Read SIZE octets from input-file"
  78. (let* ((octets (make-octets size))
  79. (read-len (read-sequence octets (slot-value me 'instream))))
  80. (assert (= read-len size))
  81. octets))