# v_readwav

## PURPOSE

V_READWAV Read a .WAV format sound file [Y,FS,WMODE,FIDX]=(FILENAME,MODE,NMAX,NSKIP)

## SYNOPSIS

function [y,fs,wmode,fidx]=v_readwav(filename,mode,nmax,nskip)

## DESCRIPTION

    V_READWAV  Read a .WAV format sound file [Y,FS,WMODE,FIDX]=(FILENAME,MODE,NMAX,NSKIP)

     Input Parameters:

        FILENAME gives the name of the file (with optional .WAV extension) or alternatively
                     can be the FIDX output from a previous call to READWAV
        MODE        specifies the following (*=default):

        Scaling: 's'    Auto scale to make data peak = +-1
                 'r'    Raw unscaled data (integer values)
                 'q'    Scaled to make 0dBm0 be unity mean square
                 'p' *     Scaled to make +-1 equal full scale
                 'o'    Scale to bin centre rather than bin edge (e.g. 127 rather than 127.5 for 8 bit values)
                         (can be combined with n+p,r,s modes)
                 'n'    Scale to negative peak rather than positive peak (e.g. 128.5 rather than 127.5 for 8 bit values)
                         (can be combined with o+p,r,s modes)
                 'g'    Scale by the gain written by the "g" option in "v_writewav" to restore original level
         Offset: 'y' *     Correct for offset in <=8 bit PCM data
                 'z'    No offset correction
       File I/O: 'f'    Do not close file on exit
                 'd'    Look in data directory: v_voicebox('dir_data')
       Display;  'h'    Print header information
                 'w'    Plot waveform
                 'W'    Plot spectrogram
                 'a'    play audio (max 10 seconds)
                 'A'    play all audio even if very long

        NMAX     maximum number of samples to read (or -1 for unlimited [default])
        NSKIP    number of samples to skip from start of file
                   (or -1 to continue from previous read when FIDX is given instead of FILENAME [default])

     Output Parameters:

        Y        data matrix of dimension (samples,channels)
        FS       sample frequency in Hz
        WMODE    mode string needed for WRITEWAV to recreate the data file
        FIDX     Information row vector containing the element listed below.

               (1)  file id
                (2)  current position in file
               (3)  dataoff    byte offset in file to start of data
               (4)  nsamp    number of samples
               (5)  nchan    number of channels
               (6)  nbyte    bytes per data value
               (7)  bits    number of bits of precision
               (8)  code    Data format: 1=PCM, 2=ADPCM, 3=floating point, 6=A-law, 7=Mu-law
               (9)  fs        sample frequency
               (10) mask   channel mask
               (11) gain   gain factor in dB
               (12) riffmt 'e' original wav format, 'x' WAVEFORMATEX format, 'X' WAVEFORMATEXTENSIBLE format
               (13) factlen length of FACT chunk

       If no output parameters are specified, header information will be printed.

       For stereo data, y(:,1) is the left channel and y(:,2) the right
       The mask, if specified, is a bit field giving the channels present in the following order:
       0=FL, 1=FR, 2=FC, 3=W, 4=BL, 5=BR, 6=FLC, 7=FRC, 8=BC, 9=SL, 10=SR, 11=TC, 12=TFL, 13=TFC, 14=TFR, 15=TBL, 16=TBC, 17=TBR
       where F=front, L=left, C=centre, W=woofer (low frequency), B=back, LC=left of centre, RC=right of centre, S=side, T=top

       See also WRITEWAV.

## CROSS-REFERENCE INFORMATION

This function calls:

- v_pcma2lin V_PCMU2LIN Convert A-law PCM to linear X=(P,M,S)
- v_pcmu2lin V_PCMU2LIN Convert Mu-law PCM to linear X=(P,S)
- v_spgrambw V_SPGRAMBW Draw spectrogram [T,F,B]=(s,fs,mode,bw,fmax,db,tinc,ann)
- v_voicebox V_VOICEBOX set global parameters for Voicebox functions Y=(FIELD,VAL)

This function is called by:

- v_psycdigit V_PSYCDIGIT measures psychometric function using TIDIGITS stimuli
- v_readflac V_READFLAC Read a .FLAC format sound file [Y,FS]=(FILENAME,MODE)
- wavread WAVREAD Legacy MATLAB function to read .WAV file [Y,FS,BITS]=(FILENAME,NMAX)

## SOURCE CODE

    0001 function [y,fs,wmode,fidx]=v_readwav(filename,mode,nmax,nskip)
    0002 %V_READWAV  Read a .WAV format sound file [Y,FS,WMODE,FIDX]=(FILENAME,MODE,NMAX,NSKIP)
    0003 %
    0004 % Input Parameters:
    0005 %
    0006 %    FILENAME gives the name of the file (with optional .WAV extension) or alternatively
    0007 %                 can be the FIDX output from a previous call to READWAV
    0008 %    MODE        specifies the following (*=default):
    0009 %
    0010 %    Scaling: 's'    Auto scale to make data peak = +-1
    0011 %             'r'    Raw unscaled data (integer values)
    0012 %             'q'    Scaled to make 0dBm0 be unity mean square
    0013 %             'p' *     Scaled to make +-1 equal full scale
    0014 %             'o'    Scale to bin centre rather than bin edge (e.g. 127 rather than 127.5 for 8 bit values)
    0015 %                     (can be combined with n+p,r,s modes)
    0016 %             'n'    Scale to negative peak rather than positive peak (e.g. 128.5 rather than 127.5 for 8 bit values)
    0017 %                     (can be combined with o+p,r,s modes)
    0018 %             'g'    Scale by the gain written by the "g" option in "v_writewav" to restore original level
    0019 %     Offset: 'y' *     Correct for offset in <=8 bit PCM data
    0020 %             'z'    No offset correction
    0021 %   File I/O: 'f'    Do not close file on exit
    0022 %             'd'    Look in data directory: v_voicebox('dir_data')
    0023 %   Display;  'h'    Print header information
    0024 %             'w'    Plot waveform
    0025 %             'W'    Plot spectrogram
    0026 %             'a'    play audio (max 10 seconds)
    0027 %             'A'    play all audio even if very long
    0028 %
    0029 %    NMAX     maximum number of samples to read (or -1 for unlimited [default])
    0030 %    NSKIP    number of samples to skip from start of file
    0031 %               (or -1 to continue from previous read when FIDX is given instead of FILENAME [default])
    0032 %
    0033 % Output Parameters:
    0034 %
    0035 %    Y        data matrix of dimension (samples,channels)
    0036 %    FS       sample frequency in Hz
    0037 %    WMODE    mode string needed for WRITEWAV to recreate the data file
    0038 %    FIDX     Information row vector containing the element listed below.
    0039 %
    0040 %           (1)  file id
    0041 %            (2)  current position in file
    0042 %           (3)  dataoff    byte offset in file to start of data
    0043 %           (4)  nsamp    number of samples
    0044 %           (5)  nchan    number of channels
    0045 %           (6)  nbyte    bytes per data value
    0046 %           (7)  bits    number of bits of precision
    0047 %           (8)  code    Data format: 1=PCM, 2=ADPCM, 3=floating point, 6=A-law, 7=Mu-law
    0048 %           (9)  fs        sample frequency
    0049 %           (10) mask   channel mask
    0050 %           (11) gain   gain factor in dB
    0051 %           (12) riffmt 'e' original wav format, 'x' WAVEFORMATEX format, 'X' WAVEFORMATEXTENSIBLE format
    0052 %           (13) factlen length of FACT chunk
    0053 %
    0054 %   If no output parameters are specified, header information will be printed.
    0055 %
    0056 %   For stereo data, y(:,1) is the left channel and y(:,2) the right
    0057 %   The mask, if specified, is a bit field giving the channels present in the following order:
    0058 %   0=FL, 1=FR, 2=FC, 3=W, 4=BL, 5=BR, 6=FLC, 7=FRC, 8=BC, 9=SL, 10=SR, 11=TC, 12=TFL, 13=TFC, 14=TFR, 15=TBL, 16=TBC, 17=TBR
    0059 %   where F=front, L=left, C=centre, W=woofer (low frequency), B=back, LC=left of centre, RC=right of centre, S=side, T=top
    0060 %
    0061 %   See also WRITEWAV.
    0062
    0063 %   *** Note on scaling ***
    0064 %   If we want to scale signal values in the range +-1 to an integer in the
    0065 %   range [-128,127] then we have four plausible choices corresponding to
    0066 %   scale factors of (a) 127, (b) 127.5, (c) 128 or (d) 128.5 but each choice
    0067 %   has disadvantages.
    0068 %   For forward scaling: (c) and (d) cause clipping on inputs of +1.
    0069 %   For reverse scaling: (a) and (b) can generate output values < -1.
    0070 %   Any of these scalings can be selected via the mode input: (a) 'o', (b) default, (c) 'on', (d) 'n'
    0071
    0072 %       Copyright (C) Mike Brookes 1998-2011
    0073 %      Version: $Id: v_readwav.m 10865 2018-09-21 17:22:45Z dmb $
    0074 %
    0075 %   VOICEBOX is a MATLAB toolbox for speech processing.
    0076 %   Home page: http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html
    0077 %
    0078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0079 %   This program is free software; you can redistribute it and/or modify
    0080 %   it under the terms of the GNU Lesser General Public License as published by
    0081 %   the Free Software Foundation; either version 3 of the License, or
    0082 %   (at your option) any later version.
    0083 %
    0084 %   This program is distributed in the hope that it will be useful,
    0085 %   but WITHOUT ANY WARRANTY; without even the implied warranty of
    0086 %   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    0087 %   GNU Lesser General Public License for more details.
    0088 %
    0089 %   You can obtain a copy of the GNU Lesser General Public License from
    0090 %   https://www.gnu.org/licenses/ .
    0091 %
    0092 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0093
    0094 % Bugs/suggestions:
    0095
    0096
    0097 if nargin<1
    0098     error('Usage: [y,fs,wmode,fidx]=READWAV(filename,mode,nmax,nskip)'); end
    0099 if nargin<2
    0100     mode='p';
    0101 else
    0102     mode = [mode(:).' 'p'];
    0103 end
    0104 k=find((mode>='p') & (mode<='s'));
    0105 mno=all(mode~='o');                      % scale to input limits not output limits
    0106 sc=mode(k(1));
    0107 z=128*all(mode~='z'); % data offset value
    0108 info=zeros(1,13);
    0109 if ischar(filename) || isstring(filename)
    0110     if any(mode=='d')
    0111         filename=fullfile(v_voicebox('dir_data'),filename);
    0112     end
    0113     fid=fopen(filename,'rb','l');
    0114     if fid == -1
    0115         fn=[filename,'.wav'];
    0116         fid=fopen(fn,'rb','l');
    0117         if fid ~= -1
    0118             filename=fn;
    0119         end
    0120     end
    0121     if fid == -1
    0122         error('Can''t open %s for input',filename);
    0123     end
    0124     info(1)=fid;
    0125 else                                % first argument is FIDX from previous call
    0126     info=filename;
    0127     fid=info(1);
    0128 end
    0129 getdat= nargout>0 || any(lower(mode)=='w') || any(lower(mode)=='a');
    0130 mh=any(mode=='h') || ~getdat;
    0131 if ~info(3)
    0132     fseek(fid,8,-1);                        % read riff chunk
    0133     header=fread(fid,4,'*char')';
    0134     if ~strcmp(header,'WAVE')
    0135         fclose(fid);
    0136         error('File does not begin with a WAVE chunck');
    0137     end
    0138     if mh
    0139         fprintf('\nWAVE file: %s\n',filename);
    0140     end
    0141     fmtlen=-1;
    0142     datalen=-1;
    0143     instlen=-1;
    0144     info(13)=-1;
    0145     info(12)='e';  % default is original wave file format
    0146     while datalen<0                        % loop until FMT and DATA chuncks both found
    0147         header=fread(fid,4,'*char');
    0148         len=fread(fid,1,'ulong');
    0149         if mh
    0150             fprintf('  %s chunk: %d bytes\n',header,len);
    0151         end
    0152         if strcmp(header','fmt ')                    % ******* found FMT chunk *********
    0153             fmtlen=len;                             % remember the length
    0154             if len>16
    0155                 info(12)='x';  % might be WAVEFORMATEX format
    0156             end
    0157             wavfmt=fread(fid,1,'short');            % format: 1=PCM, 6=A-law, 7-Mu-law
    0158             info(8)=wavfmt;
    0159             info(5)=fread(fid,1,'ushort');            % number of channels
    0160             fs=fread(fid,1,'ulong');                % sample rate in Hz
    0161             info(9)=fs;                             % sample rate in Hz
    0162             rate=fread(fid,1,'ulong');                % average bytes per second (ignore)
    0163             align=fread(fid,1,'ushort');            % block alignment in bytes (container size * #channels)
    0164             bps=fread(fid,1,'ushort');            % bits per sample
    0165             info(7)=bps;
    0166             %             info(6)=ceil(info(7)/8);                % round up to a byte count
    0167             info(6)=floor(align/info(5));                       % assume block size/channels = container size
    0168             if info(8)==-2   % wave format extensible
    0169                 cb=fread(fid,1,'ushort');            % extra bytes must be >=22
    0170                 info(12)='X';                         % WAVEFORMATEXTENSIBLE format
    0171                 wfxsamp=fread(fid,1,'ushort');            % samples union
    0172                 if wfxsamp>0
    0173                     info(7)=wfxsamp;     % valid bits per sample
    0174                 end
    0175                 info(10)=fread(fid,1,'ulong');                % channel mask
    0176                 wfxguida=fread(fid,1,'ulong');                % GUID
    0177                 wfxguidb=fread(fid,2,'ushort');                % GUID
    0178                 wfxguidc=fread(fid,8,'uchar');                % GUID
    0179                 if wfxguida<65536
    0180                     info(8)=wfxguida; % turn it into normal wav format
    0181                 end
    0182                 fseek(fid,len-40,0);                    % skip to end of header
    0183             else
    0184                 if align>0 && align<(info(6)+4)*info(5)
    0185                     info(6)=ceil(align/info(5));
    0186                 end
    0187                 fseek(fid,len-16,0);                    % skip to end of header
    0188             end
    0189             if mh
    0190                 fmttypes={'?' 'PCM' 'ADPCM' 'IEEE-float' '?' '?' 'A-law' 'Mu-law' '?'};
    0191                 fprintf('        Format: %d = %s',info(8),fmttypes{1+max(min(info(8),8),0)});
    0192                 if wavfmt==-2
    0193                     fprintf(' (%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)\n',wfxguida,wfxguidb,wfxguidc);
    0194                 else
    0195                     fprintf('\n');
    0196                 end
    0197                 fprintf('        %d channels at %g kHz sample rate (%d kbytes/s)\n',info(5),fs/1000,rate/1000);
    0198                 fprintf('        Mask=%x:',info(10));
    0199                 spkpos={'FL' 'FR' 'FC' 'W' 'BL' 'BR' 'FLC' 'FRC' 'BC' 'SL' 'SR' 'TC' 'TFL' 'TFC' 'TFR' 'TBL' 'TBC' 'TBR'};
    0200                 for i=1:18
    0201                     if mod(floor(info(10)*pow2(1-i)),2)
    0202                         fprintf([' ' spkpos{i}]);
    0203                     end
    0204                 end
    0205                 fprintf('\n        %d valid bits of %d per sample (%d byte block size)\n',info(7),bps,align);
    0206             end
    0207         elseif strcmp(header','fact')                % ******* found FACT chunk *********
    0208             info(13)=len;
    0209             if len<4
    0210                 error('FACT chunk too short');
    0211             end
    0212             nsamp=fread(fid,1,'ulong');                % number of samples
    0213             fseek(fid,len-4,0);                     % skip to end of header
    0214             if mh
    0215                 fprintf('        %d samples = %.3g seconds\n',nsamp,nsamp/fs);
    0216             end
    0217         elseif strcmp(header','inst')                % ******* found INST chunk *********
    0218             instlen=len;
    0219             if len<7
    0220                 error('INST chunk too short');
    0221             end
    0222             inst=fread(fid,3,'schar');
    0223             info(11)=double(inst(3));                          % gain in dB
    0224             if mh
    0225                 fprintf('        Gain = %d dB\n',info(11));
    0226             end
    0227             fseek(fid,len-3,0);                     % skip to end of header
    0228         elseif strcmp(header','data')                % ******* found DATA chunk *********
    0229             if fmtlen<0
    0230                 fclose(fid);
    0231                 error('File %s does not contain a FMT chunck',filename);
    0232             end
    0233             if info(13)>3 && nsamp >0
    0234                 info(4)=nsamp;   % take data length from FACT chunk
    0235             else
    0236                 info(4) = fix(len/(info(6)*info(5)));  % number of samples
    0237             end
    0238             info(3)=ftell(fid);                    % start of data
    0239             datalen=len;
    0240             if mh
    0241                 fprintf('        %d samples x %d channels x %d bytes/samp',info(4:6));
    0242                 if prod(info(4:6))~=len
    0243                     fprintf(' + %d padding bytes',len-prod(info(4:6)));
    0244                 end
    0245                 fprintf(' = %g sec\n',info(4)/fs);
    0246             end
    0247         else                            % ******* found unwanted chunk *********
    0248             fseek(fid,len,0);
    0249         end
    0250     end
    0251 else
    0252     fs=info(9);
    0253 end
    0254 if nargin<4 || nskip<0
    0255     nskip=info(2);  % resume at current file position
    0256 end
    0257
    0258 ksamples=info(4)-nskip; % number of samples remaining
    0259 if nargin>2
    0260     if nmax>=0
    0261         ksamples=min(nmax,ksamples); % number of samples to read
    0262     end
    0263 elseif ~getdat
    0264     ksamples=min(5,ksamples); % just read 5 samples so we can print the first few data values
    0265 end
    0266 if ksamples>0
    0267     info(2)=nskip+ksamples;
    0268     fseek(fid,info(3)+info(6)*info(5)*nskip,-1);
    0269     nsamples=info(5)*ksamples;
    0270     if any(info(8)==3)  % floating point format
    0271         pk=1;  % peak is 1
    0272         switch info(6)
    0273             case 4
    0274                 y=fread(fid,nsamples,'float32');
    0275             case 8
    0276                 y=fread(fid,nsamples,'float64');
    0277             otherwise
    0278                 error('cannot read %d-byte floating point numbers',info(6));
    0279         end
    0280     else
    0281         if ~any(info(8)==[1 6 7])
    0282             sc='r';  % read strange formats in raw integer mode
    0283         end
    0284         pk=pow2(0.5,8*info(6))*(1+(mno/2-all(mode~='n'))/pow2(0.5,info(7)));  % use modes o and n to determine effective peak
    0285         switch info(6)
    0286             case 1
    0287                 y=fread(fid,nsamples,'uchar');
    0288                 if info(8)==1
    0289                     y=y-z;
    0290                 elseif info(8)==6
    0291                     y=v_pcma2lin(y,213,1);
    0292                     pk=4032+mno*64;
    0293                 elseif info(8)==7
    0294                     y=v_pcmu2lin(y,1);
    0295                     pk=8031+mno*128;
    0296                 end
    0297             case 2
    0298                 y=fread(fid,nsamples,'short');
    0299             case 3
    0300                 y=fread(fid,3*nsamples,'uchar');
    0301                 y=reshape(y,3,nsamples);
    0302                 y=([1 256 65536]*y-pow2(fix(pow2(y(3,:),-7)),24)).';
    0303             case 4
    0304                 y=fread(fid,nsamples,'long');
    0305             otherwise
    0306                 error('cannot read %d-byte integers',info(6));
    0307         end
    0308     end
    0309     if sc ~= 'r'
    0310         if sc=='s'
    0311             sf=1/max(abs(y(:)));
    0312         elseif sc=='p'
    0313             sf=1/pk;
    0314         else
    0315             if info(8)==7
    0316                 sf=2.03761563/pk;
    0317             else
    0318                 sf=2.03033976/pk;
    0319             end
    0320         end
    0321         y=sf*y;
    0322     else                             % mode = 'r' - output raw values
    0323         if info(8)==1
    0324             y=y*pow2(1,info(7)-8*info(6));  % shift to get the bits correct
    0325         end
    0326     end
    0327     if any(mode=='g') && info(11)~=0
    0328         y=y*10^(info(11)/20);   % scale by the gain
    0329     end
    0330     if info(5)>1
    0331         y = reshape(y,info(5),ksamples).';
    0332     end
    0333 else
    0334     y=[];
    0335 end
    0336 if all(mode~='f')
    0337     fclose(fid);
    0338 end
    0339 if nargout>2  % sort out the mode input for writing this format
    0340     wmode=char([info(12) sc 'z'-z/128]);
    0341     if info(13)>0
    0342         wmode=[wmode 'E'];
    0343     end
    0344     if info(6)>1 && info(6)<5
    0345         cszc=' cCL';
    0346         wmode=[wmode cszc(info(6))];
    0347     end
    0348     switch info(8)
    0349         case 1                                    % PCM modes
    0350             if ~mno
    0351                 wmode=[wmode 'o'];
    0352             end
    0353             if any(mode=='n')
    0354                 wmode=[wmode 'n'];
    0355             end
    0356             wmode=[wmode num2str(info(7))];
    0357         case 3
    0358             if info(7)<=32
    0359                 wmode = [wmode 'v'];
    0360             else
    0361                 wmode = [wmode 'V'];
    0362             end
    0363         case 6
    0364             wmode = [wmode 'a'];
    0365         case 7
    0366             wmode = [wmode 'u'];
    0367     end
    0368     fidx=info;
    0369 end
    0370 [ns,nchan]=size(y);
    0371 if mh && ns>0
    0372     nsh=min(ns,5);  % print first few samples
    0373     for i=1:nsh
    0374         fprintf('        %d:',i);
    0375         fprintf(' %.3g',y(i,:));
    0376         fprintf('\n');
    0377     end
    0378 end
    0379
    0380 if ns>0.01*fs
    0381     if any(lower(mode)=='a')
    0382         nsh=min(ns,10*fs+ns*any(mode=='A'));
    0383         soundsc(y(1:nsh,1:min(nchan,2)),fs);
    0384     end
    0385     if any(mode=='W')
    0386         spm='pJcbf ';
    0387         if any(mode=='w')
    0388             spm(end)='w';
    0389         end
    0390         clf;
    0391         if nchan>1
    0392             for i=nchan:-1:1
    0393                 subplot(nchan,1,i)
    0394                 v_spgrambw(y(:,i),fs,spm);
    0395             end
    0396         else
    0397             v_spgrambw(y,fs,spm);
    0398         end
    0399     elseif any(mode=='w')
    0400         clf;
    0401         if nchan>1
    0402             for i=nchan:-1:1
    0403                 subplot(nchan,1,i)
    0404                 plot((1:ns)/fs,y(:,i));
    0405                 ylabel(['Chan ' num2str(i)]);
    0406                 if i==nchan
    0407                     xlabel('Time (s)');
    0408                 end
    0409             end
    0410         else
    0411             plot((1:ns)/fs,y);
    0412             xlabel('Time (s)');
    0413         end
    0414
    0415     end
    0416 end
    0417
    0418
    0419
    0420