; $Id: read_wav.pro,v 1.13 2004/04/05 16:01:17 scottm Exp $
;
; Copyright (c) 1993-2004, Research Systems, Inc.  All rights reserved.
;       Unauthorized reproduction prohibited.

FUNCTION READ_WAV, file, rate
;+
; NAME:
;   READ_WAV
;
; PURPOSE:
;   This function reads a Microsoft Windows .WAV (RIFF) file
;
; CATEGORY:
;       Input/Output
;
; CALLING SEQUENCE:
;       Result = READ_WAV(File [,Rate])
;
; INPUTS:
;   File: The full path name of the file to read.
;
; OUTPUTS:
;   This function returns an array containing the audio data
;   from the file. The data can be 8 or 16 bit.  The leading
;   dimension of the returned array is the channel selection
;   (data is returned in channel interleaved format).
;
; OPTIONAL OUTPUTS:
;   Rate : the sampling rate of the sequence in samples/second.
;
; KEYWORDS:
;   None
;
; SIDE EFFECTS:
;       IO is performed.
;
; RESTRICTIONS:
;   Only the PCM (uncompressed) data only format is supported.
;
; PROCEDURE:
;       Straightforward. Will work on both big endian and little endian
;   machines.
;
; EXAMPLE:
;
; MODIFICATION HISTORY:
;   RJF, RSI.   Sep, 1998. Original version.
;   CT, RSI, Aug 2003: Fix bug in error code if unable to open file.
;-

ON_IOERROR, bad
ON_ERROR, 2         ;Return on error

OPENR, unit, file, /GET_LUN, /BLOCK
fhdr = { WAVFILEHEADER, $
    friff: BYTARR(4), $        ;A four char string
    fsize: 0L, $
    fwave: BYTARR(4) $      ;A four char string
  }
READU, unit, fhdr           ;Read the header
IF (STRING(fhdr.friff) NE "RIFF") OR  $
    (STRING(fhdr.fwave) NE "WAVE") THEN BEGIN
    FREE_LUN, unit
    MESSAGE, 'File '+file+' is not in WAV file format'
ENDIF

big_endian = (BYTE(1,0,2))[0] EQ 0b

chdr = { WAVCHUNKHEADER, $
    cid:   BYTARR(4), $
    csize: 0L $
}
cfmt = { WAVFMTCHUNK, $
    tag: 0, $
    chan: 0, $
    rate: 0L, $
    bps: 0L, $
    balign: 0, $
    bits: 0, $
    bextra: 0 $
}

; Find the 'fmt ' chunk
READU, unit, chdr
IF (big_endian) THEN chdr = SWAP_ENDIAN(chdr)
WHILE (STRING(chdr.cid) NE 'fmt ') DO BEGIN
    POINT_LUN, -unit, pos
    POINT_LUN, unit, pos+chdr.csize
    READU, unit, chdr
    IF (big_endian) THEN chdr = SWAP_ENDIAN(chdr)
END

; Use the header size to move past fmt chunk in case
; struct size does not match.
POINT_LUN, -unit, pos
READU, unit, cfmt
POINT_LUN, unit, pos+chdr.csize
IF (big_endian) THEN cfmt = SWAP_ENDIAN(cfmt)
IF (cfmt.tag NE 1) THEN BEGIN
    FREE_LUN, unit
    MESSAGE, 'Non-PCM format WAV files not supported'
END
rate = cfmt.rate

; Find the 'data' chunk
READU, unit, chdr
IF (big_endian) THEN chdr = SWAP_ENDIAN(chdr)
WHILE (STRING(chdr.cid) NE 'data') DO BEGIN
    POINT_LUN, -unit, pos
    POINT_LUN, unit, pos+chdr.csize
    READU, unit, chdr
    IF (big_endian) THEN chdr = SWAP_ENDIAN(chdr)
END
; allocate output array
IF (cfmt.bits LE 8) THEN BEGIN
    a = BYTARR(cfmt.chan, chdr.csize/(1*cfmt.chan))
END ELSE IF (cfmt.bits LE 16) THEN BEGIN
    a = INTARR(cfmt.chan, chdr.csize/(2*cfmt.chan))
END ELSE BEGIN
    FREE_LUN, unit
    MESSAGE, 'Unsupported number of bits per sample:'+STRING(cfmt.bits)
END
; read the samples
a = REFORM(a,/OVERWRITE)
READU, unit, a
IF (big_endian) THEN a = SWAP_ENDIAN(a)

CLOSE, unit
FREE_LUN, unit
RETURN, a

bad:
if n_elements(unit) gt 0 then $
    if (unit ne 0) then free_lun, unit
MESSAGE, 'Cannot open (or read) input file.'
RETURN, 0
END

