Hi,
I'm just starting to write a lisp program for music and I want to open a
wav file and read its content and put it into an array.
Opening the file isn't much of a problem. The reading is more.
The 4 first bytes of the file contain "RIFF"
The next 4 bytes contain a 32bits integer giving the length of the file
the next 4 bytes contain "WAVE"
and so on.
My question is : how do I get the length of the file with lisp ? I tried
"read-byte" with several type specifiers, but I can't get anything...
each time I get 4 numbers corresponding to the 4 bytes, but I want 1 number.
I hope anyone has an answer...
Ps : I don't want to use a C program to do that.
Martin
Martin Raspaud <··············@wanadoo.fr> writes:
> Opening the file isn't much of a problem. The reading is more.
> The 4 first bytes of the file contain "RIFF"
> The next 4 bytes contain a 32bits integer giving the length of the file
> the next 4 bytes contain "WAVE"
> and so on.
> My question is : how do I get the length of the file with lisp?
FILE-LENGTH
> I tried "read-byte" with several type specifiers, but I can't get
> anything... each time I get 4 numbers corresponding to the 4 bytes,
> but I want 1 number.
If you use a stream with element-type (unsigned-byte 32), you should
be able to read 32-bit integers from the stream right away. If you
cannot do that, for example because you want to read 8-bit values
later, just combine your 4 8-bit numbers to one 32-bit one. You'll
want to look into the bit-fiddling functions like ASH, LDB and DPB.
Martin Raspaud wrote:
> Hi,
>
> I'm just starting to write a lisp program for music and I want to open a
> wav file and read its content and put it into an array.
>
> Opening the file isn't much of a problem. The reading is more.
> The 4 first bytes of the file contain "RIFF"
> The next 4 bytes contain a 32bits integer giving the length of the file
> the next 4 bytes contain "WAVE"
> and so on.
> My question is : how do I get the length of the file with lisp ? I tried
> "read-byte" with several type specifiers, but I can't get anything...
> each time I get 4 numbers corresponding to the 4 bytes, but I want 1
> number.
>
> I hope anyone has an answer...
I have written the following code to read Java class files. They use
8-bit bytes to encode 16-bit, 32-bit and 64-bit values in big endian
order. I have taken the approach to load a complete class file at once
into a byte array and then decode the values from that array. The idea
was to wait for profiling the actual behavior in order to see whether a
more efficient approach is needed. However, I haven't yet gotten to that
stage.
Here is the code. Note that some Common Lisp implementations might have
serious limitations on the possible maximum size of arrays.
(defmacro with-open-class-file ((cf pathname) &body block)
"similar to with-open-file"
(with-unique-names (result)
`(let* ((,cf (open-class-file ,pathname))
(,result (progn ,@block)))
(setf ,cf ())
,result)))
(defun open-class-file (class-file)
"similar to open-file"
(with-open-file (stream class-file :element-type '(unsigned-byte 8))
(let ((cf (make-array (file-length stream)
:element-type '(unsigned-byte 8)
:fill-pointer t)))
(read-sequence cf stream)
(setf (fill-pointer cf) 0)
cf)))
(defun read1 (cf)
"read one byte from a class file"
(let ((fp (fill-pointer cf)))
(incf (fill-pointer cf))
(aref cf fp)))
(defun bytes-to-int (arr &key (start 0) (end nil) (signed nil))
"take a multibyte datum in big-endian order"
(when (null end) (setq end (length arr)))
(assert (< start end))
(let ((len (- end start))
(res (reduce (lambda (x y) (logior (ash x 8) y)) arr
:start start :end end :initial-value 0)))
(if (and signed (> len 0))
(let ((dec (expt 2 (ash len 3))))
(if (>= res (ash dec -1))
(- res (expt 2 (* len 8)))
res))
res)))
(defun readn (cf n &key (signed nil))
"read n bytes from a class file"
(let* ((start (fill-pointer cf))
(end (setf (fill-pointer cf) (+ start n))))
(bytes-to-int cf :start start :end end :signed signed)))
The code uses only standard stuff per ANSI Common Lisp. I guess other
people probably have suggestions for improvement. (This code is already
derived from various hints I have gotten from past c.l.l postings.)
Pascal
--
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
Martin Raspaud <··············@wanadoo.fr> writes:
> Hi,
>
> I'm just starting to write a lisp program for music and I want to open a
> wav file and read its content and put it into an array.
>
> Opening the file isn't much of a problem. The reading is more.
> The 4 first bytes of the file contain "RIFF"
> The next 4 bytes contain a 32bits integer giving the length of the file
> the next 4 bytes contain "WAVE"
> and so on.
> My question is : how do I get the length of the file with lisp ? I tried
> "read-byte" with several type specifiers, but I can't get anything...
> each time I get 4 numbers corresponding to the 4 bytes, but I want 1 number.
If you open the file with an element type of (unxssnged-byte 32)
you'll get a raw stream of octets that you can assemble into whatever
Lisp object you want. There are at least a couple utility libraries
out there to help with this, and sometimes your implementation has
other ways of doing this. I use my own personal library for this
stuff. Here's a small starting point:
(defvar *endianness* :big)
(declaim (inline read-byte*))
(defun read-byte* (width stream &optional (eof-errorp t) eof-value)
"Read an unsigned byte of WIDTH bits, from STREAM.
WIDTH should be an even multiple of 8."
(assert (zerop (mod width 8)))
(ecase *endianness*
(:big (loop with result = 0
for pos downfrom (- width 8) to 0 by 8
for byte = (read-byte stream eof-errorp eof-value)
do (setf (ldb (byte 8 pos) result) byte)
finally (return result)))))
To extend it to read in little-endian order (which I'm guessing WAV
files use), just make pos go up, not down. Yow, apparently I haven't
needed to read from a little-endian byte stream in a long, long time :)
--
/|_ .-----------------------.
,' .\ / | No to Imperialist war |
,--' _,' | Wage class war! |
/ / `-----------------------'
( -. |
| ) |
(`-. '--.)
`. )----'
"Martin Raspaud" <··············@wanadoo.fr> wrote in message
·················@news-reader1.wanadoo.fr...
> Hi,
>
> I'm just starting to write a lisp program for music and I want to open a
> wav file and read its content and put it into an array.
>
> Opening the file isn't much of a problem. The reading is more.
> The 4 first bytes of the file contain "RIFF"
> The next 4 bytes contain a 32bits integer giving the length of the file
> the next 4 bytes contain "WAVE"
> and so on.
> My question is : how do I get the length of the file with lisp ?
file-length returns the length of stream, or nil if the length cannot be
determined.
For a binary file, the length is measured in units of the element type of
the stream.
Examples:
(with-open-file (s "decimal-digits.text"
:direction :output :if-exists :error)
(princ "0123456789" s)
(truename s))
=> #P"A:>Joe>decimal-digits.text.1"
(with-open-file (s "decimal-digits.text")
(file-length s))
=> 10
element type = stream element type n. (of a stream) the type of data for
which the stream is specialized.
>I tried
> "read-byte" with several type specifiers, but I can't get anything...
> each time I get 4 numbers corresponding to the 4 bytes, but I want 1
number.
>
> I hope anyone has an answer...
>
> Ps : I don't want to use a C program to do that.
>
> Martin
>