Home > voicebox > writewav.m

writewav

PURPOSE ^

WRITEWAV Creates .WAV format sound files FIDX=(D,FS,FILENAME,MODE,NSKIP,MASK,MAD)

SYNOPSIS ^

function fidx=writewav(d,fs,filename,mode,nskip,mask,mad)

DESCRIPTION ^

WRITEWAV Creates .WAV format sound files FIDX=(D,FS,FILENAME,MODE,NSKIP,MASK,MAD)

   The input arguments for WRITEWAV are as follows (use [] for null arguments):

       D           The sampled data to save
       FS          The rate at which the data was sampled
       FILENAME    A string containing the name of the .WAV file to create or
                        alternatively the FIDX output from a previous writewav call
       MODE        String containing any reasonable mixture of flags below (*=default):
       NSKIP       Number of samples to skip before writing or -1[default] to continue from previous write
                   Only valid if FIDX is specified for FILENAME
       MASK        specifies the speaker positions included as a bit mask giving the channels present in the following order (0 is LSB):
                   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
       MAD         specifies the input scale for 's' option [default: max(abs(d(:)))]

 MODE flags (*=default):
  Precision: 'a'    for 8-bit A-law PCM
             'u'    for 8-bit mu-law PCM
            '16' *     for 16 bit PCM data
             '8'    for 8 bit PCM data
             ...    any number in the range 2 to 32 for PCM
             'v'    32-bit floating point
             'V'    64-bit floating point
             'c'    embed in 16 bits
             'C'    embed in 24 bits
             'L'    embed in 32 bits
      Dither: 'w'    White triangular dither of amplitude +-1 LSB (PCM modes only)
             'h'    High pass dither (filtered by 1-1/z) (PCM modes only)
             'l'    Low pass dither (filtered by 1+1/z) (PCM modes only)
    Scaling: 's' *  Auto scale to make data peak (or MAD input value) = +-1
             'r'    Raw unscaled data (integer values)
             'q'    Scaled to make unity mean square correspond to 0dBm according to G.711
             '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'    Include a gain factor so that "readwav" will restore the correct level
     Offset: 'y' *     Correct for offset in <=8 bit PCM data
             'z'    Do not apply offset correction
     Format: 'x'    use WAVEFORMATEX format (default for non PCM)
             'X'    use WAVEFORMATEXTENSIBLE (default if MASK input is non-zero)
             'e'    use original WAVEFORMAT (default for PCM)
             'E'    include a 'fact' chunk (default for non-PCM)
   File I/O: 'f'    Do not close file on exit
             'd'    Look in data directory: voicebox('dir_data')


 Output Parameter:

    FIDX     Information row vector containing the element listed below.

           (1)  file id
            (2)  current position in file (in samples, 0=start of file)
           (3)  dataoff    length of file header in bytes
           (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, 6=A-law, 7=Mu-law
           (9)  fs    sample frequency
           (10) dither state variable
           (11) gain in dB (in INST chunk)

   Note: WRITEWAV will create an 16-bit PCM, auto-scaled wave file by default.
   For stereo data, d(:,1) is the left channel and d(:,2) the right

   See also READWAV

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 function fidx=writewav(d,fs,filename,mode,nskip,mask,mad)
0002 %WRITEWAV Creates .WAV format sound files FIDX=(D,FS,FILENAME,MODE,NSKIP,MASK,MAD)
0003 %
0004 %   The input arguments for WRITEWAV are as follows (use [] for null arguments):
0005 %
0006 %       D           The sampled data to save
0007 %       FS          The rate at which the data was sampled
0008 %       FILENAME    A string containing the name of the .WAV file to create or
0009 %                        alternatively the FIDX output from a previous writewav call
0010 %       MODE        String containing any reasonable mixture of flags below (*=default):
0011 %       NSKIP       Number of samples to skip before writing or -1[default] to continue from previous write
0012 %                   Only valid if FIDX is specified for FILENAME
0013 %       MASK        specifies the speaker positions included as a bit mask giving the channels present in the following order (0 is LSB):
0014 %                   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
0015 %                   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
0016 %       MAD         specifies the input scale for 's' option [default: max(abs(d(:)))]
0017 %
0018 % MODE flags (*=default):
0019 %  Precision: 'a'    for 8-bit A-law PCM
0020 %             'u'    for 8-bit mu-law PCM
0021 %            '16' *     for 16 bit PCM data
0022 %             '8'    for 8 bit PCM data
0023 %             ...    any number in the range 2 to 32 for PCM
0024 %             'v'    32-bit floating point
0025 %             'V'    64-bit floating point
0026 %             'c'    embed in 16 bits
0027 %             'C'    embed in 24 bits
0028 %             'L'    embed in 32 bits
0029 %      Dither: 'w'    White triangular dither of amplitude +-1 LSB (PCM modes only)
0030 %             'h'    High pass dither (filtered by 1-1/z) (PCM modes only)
0031 %             'l'    Low pass dither (filtered by 1+1/z) (PCM modes only)
0032 %    Scaling: 's' *  Auto scale to make data peak (or MAD input value) = +-1
0033 %             'r'    Raw unscaled data (integer values)
0034 %             'q'    Scaled to make unity mean square correspond to 0dBm according to G.711
0035 %             'p'       Scaled to make +-1 equal full scale
0036 %             'o'    Scale to bin centre rather than bin edge (e.g. 127 rather than 127.5 for 8 bit values)
0037 %                     (can be combined with n+p,r,s modes)
0038 %             'n'    Scale to negative peak rather than positive peak (e.g. 128.5 rather than 127.5 for 8 bit values)
0039 %                     (can be combined with o+p,r,s modes)
0040 %             'g'    Include a gain factor so that "readwav" will restore the correct level
0041 %     Offset: 'y' *     Correct for offset in <=8 bit PCM data
0042 %             'z'    Do not apply offset correction
0043 %     Format: 'x'    use WAVEFORMATEX format (default for non PCM)
0044 %             'X'    use WAVEFORMATEXTENSIBLE (default if MASK input is non-zero)
0045 %             'e'    use original WAVEFORMAT (default for PCM)
0046 %             'E'    include a 'fact' chunk (default for non-PCM)
0047 %   File I/O: 'f'    Do not close file on exit
0048 %             'd'    Look in data directory: voicebox('dir_data')
0049 %
0050 %
0051 % Output Parameter:
0052 %
0053 %    FIDX     Information row vector containing the element listed below.
0054 %
0055 %           (1)  file id
0056 %            (2)  current position in file (in samples, 0=start of file)
0057 %           (3)  dataoff    length of file header in bytes
0058 %           (4)  nsamp    number of samples
0059 %           (5)  nchan    number of channels
0060 %           (6)  nbyte    bytes per data value
0061 %           (7)  bits    number of bits of precision
0062 %           (8)  code    Data format: 1=PCM, 2=ADPCM, 6=A-law, 7=Mu-law
0063 %           (9)  fs    sample frequency
0064 %           (10) dither state variable
0065 %           (11) gain in dB (in INST chunk)
0066 %
0067 %   Note: WRITEWAV will create an 16-bit PCM, auto-scaled wave file by default.
0068 %   For stereo data, d(:,1) is the left channel and d(:,2) the right
0069 %
0070 %   See also READWAV
0071 
0072 %   *** Note on scaling ***
0073 %   If we want to scale signal values in the range +-1 to an integer in the
0074 %   range [-128,127] then we have four plausible choices corresponding to
0075 %   scale factors of (a) 127, (b) 127.5, (c) 128 or (d) 128.5 but each choice
0076 %   has disadvantages.
0077 %   For forward scaling: (c) and (d) cause clipping on inputs of +1.
0078 %   For reverse scaling: (a) and (b) can generate output values < -1.
0079 %   Any of these scalings can be selected via the mode input: (a) 'o', (b) default, (c) 'on', (d) 'n'
0080 
0081 %       Copyright (C) Mike Brookes 1998-2011
0082 %      Version: $Id: writewav.m 10480 2018-06-04 06:48:31Z dmb $
0083 %
0084 %   VOICEBOX is a MATLAB toolbox for speech processing.
0085 %   Home page: http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html
0086 %
0087 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0088 %   This program is free software; you can redistribute it and/or modify
0089 %   it under the terms of the GNU General Public License as published by
0090 %   the Free Software Foundation; either version 2 of the License, or
0091 %   (at your option) any later version.
0092 %
0093 %   This program is distributed in the hope that it will be useful,
0094 %   but WITHOUT ANY WARRANTY; without even the implied warranty of
0095 %   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0096 %   GNU General Public License for more details.
0097 %
0098 %   You can obtain a copy of the GNU General Public License from
0099 %   http://www.gnu.org/copyleft/gpl.html or by writing to
0100 %   Free Software Foundation, Inc.,675 Mass Ave, Cambridge, MA 02139, USA.
0101 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0102 
0103 % Acknowledgements
0104 %   Thanks to Hugh Barnes for sorting out seek problems with MATLAB 6.5
0105 
0106 % Bugs/suggestions
0107 %  Save the following factors in FIDX: (a) scale factor, (b) offset (c) low/high clip limits
0108 %       (d) dither position  (e) rounding position
0109 
0110 
0111 if nargin<3
0112     error('Usage: WRITEWAV(data,fs,filename,mode,nskip)');
0113 end
0114 if nargin<6
0115     mask=0;
0116 end
0117 info=zeros(1,11);
0118 info(9)=fs;
0119 if nargin<4
0120     mode='s';
0121 else
0122     mode = [mode(:).' 's'];  % ensure that there is always a scaling option specified
0123 end
0124 info(8)=1;     % default mode is PCM
0125 mno=all(mode~='o');                      % scale to input limits not output limits
0126 k=find((mode>='0') & (mode<='9'),1);
0127 if k
0128     info(7)=sscanf(mode(k:end),'%d');  % valid bits per data value
0129 else
0130     info(7)=16;
0131 end
0132 if any(mode=='c')
0133     info(6)=2;       % bytes per data value = 2
0134 elseif any(mode=='C')
0135     info(6)=3;       % bytes per data value = 3
0136 elseif any(mode=='L')
0137     info(6)=4;       % bytes per data value = 4
0138 else
0139     info(6)=ceil(info(7)/8);       % bytes per data value
0140 end
0141 lo=-pow2(0.5,info(7));
0142 hi=-1-lo;
0143 pk=pow2(0.5,8*info(6))*(1-(mno/2-all(mode~='n'))/lo);  % use modes o and n to determine effective peak
0144 % should perhaps have another variable besides info(7) to control dither position (or set info(7) later)
0145 % for A and mu law the dither position is not the same as the number of bits.
0146 if any(mode=='a')
0147     info(8)=6;
0148     pk=4032+mno*64;
0149     info(7)=8;  % Some sources say this should be listed as 16 valid bits
0150     info(6)=1;
0151 elseif any(mode=='u')
0152     info(8)=7;
0153     pk=8031+mno*128;
0154     info(7)=8;  % Some sources say this should be listed as 16 valid bits
0155     info(6)=1;
0156 elseif any(mode=='v')
0157     pk=1;
0158     mode(end)='r';  % default scaling is 'r'
0159     info(6)=4;  % bytes
0160     info(7)=32; % bits
0161     info(8)=3;  % WAVE type
0162 elseif any(mode=='V')
0163     pk=1;
0164     mode(end)='r';   % default scaling is 'r'
0165     info(6)=8; % bytes
0166     info(7)=64; % bits
0167     info(8)=3; % WAVE type
0168 end            % is this pk value correct ?
0169 sc=mode(find((mode>='p') & (mode<='s'),1)); % find the first scaling option (always exists)
0170 z=128*all(mode~='z');
0171 if any(mode=='w')
0172     di='w';                       % select dither mode
0173 elseif any(mode=='h')
0174     di='h';
0175 elseif any(mode=='l')
0176     di='l';
0177 else
0178     di='n';
0179 end
0180 
0181 % Now sort out which wave format to use
0182 if any(mode=='e')
0183     wavtype=1;              % original WAVEFORMAT format
0184 elseif any(mode=='x')
0185     wavtype=2;              % WAVEFORMATEX format
0186 elseif any(mode=='X') || (nargin>=6 && ~isempty(mask) && mask~=0)
0187     wavtype=3;              % WAVEFORMATEXTENSIBLE format
0188 else
0189     wavtype=2-(info(8)==1);
0190 end
0191 wavfmt=info(8)*(wavtype<3)+(pow2(16)-2)*(wavtype==3);
0192 fmtlen=[16 18 40]; % length of format chunk
0193 factlen=12*(any(mode=='E') || info(8)~=1);
0194 instlen=16*any(mode=='g');  % length of INST chunk (force to be even since some readers do not like odd lengths)
0195 wavlen=[36 38 60]+factlen+instlen; % length of entire WAVE chunk except for the data (not including 8 byte RIFF header)
0196 
0197 
0198 [n,nc]=size(d);
0199 if n==1
0200     n=nc;
0201     nc=1;
0202 else
0203     d = d.';
0204 end;
0205 if nc>32
0206     error('WRITEWAV: attempt to write a sound file with >32 channels');
0207 end
0208 nc=max(nc,1);
0209 ncy=nc*info(6);                     % bytes per sample time
0210 nyd=n*ncy;                          % bytes to write
0211 if ischar(filename)
0212     if any(mode=='d')
0213         filename=fullfile(voicebox('dir_data'),filename);
0214     end
0215     ny=nyd;
0216     if isempty(findstr(filename,'.'))
0217         filename=[filename,'.wav'];
0218     end
0219     fid=fopen(filename,'wb+','l');
0220     if fid == -1
0221         error('Can''t open %s for output',filename);
0222     end
0223     info(1)=fid;
0224     fwrite(fid,'RIFF','uchar');  % main RIFF header
0225     fwrite(fid,wavlen(wavtype)+2*ceil(ny/2),'ulong');  %
0226     fwrite(fid,'WAVEfmt ','uchar');   % write "WAVE" ID and "fmt" chunk
0227     fwrite(fid,[fmtlen(wavtype) 0 wavfmt nc],'ushort'); % chunk size, format code, number of channels
0228     fwrite(fid,[fs fs*ncy],'ulong');        % sample rate, bytes per sec
0229     switch wavtype
0230         case 1
0231             fwrite(fid,[ncy info(7)],'ushort');     % block size, bits-per-sample
0232         case 2
0233             fwrite(fid,[ncy info(7)],'ushort');     % block size, bits-per-sample
0234             fwrite(fid,0,'ushort');     % size of the extension=0
0235         case 3
0236             fwrite(fid,[ncy 8*info(6)],'ushort');     % block size, bits-per-sample (aways a multiple of 8)
0237             fwrite(fid,[22 info(7)],'ushort');     % size of the extension=22, valid bits
0238             fwrite(fid,[mask info(8)],'ulong');        % speaker position mask, encoding format
0239             fwrite(fid,[0 16 128 43520 14336 29083],'ushort');                % GUID
0240     end
0241     if factlen
0242         fwrite(fid,'fact','uchar');   % fact chunk header
0243         fwrite(fid,[4 n],'ulong');       % length in bytes + number of samples
0244     end
0245     if instlen
0246         fwrite(fid,'inst','uchar');   % fact chunk header
0247         fwrite(fid,instlen-8,'ulong');       % length in bytes
0248         fwrite(fid,zeros(1,instlen-8),'uchar');   % inst data (zero for now)
0249     end
0250     fwrite(fid,'data','uchar');   % data header
0251     fwrite(fid,ny,'ulong');       % data length in bytes
0252     nskip=0;                        % over-ride any nskip argument
0253     info(3)=8+wavlen(wavtype);      % length of all header information
0254     info(4)=n;                      % number of samples (per channel)
0255     info(2)=n;                      % current file position (in samples)
0256     info(10)=rand(1);                       % seed for dither generation
0257 else
0258     info=filename;
0259     fid=info(1);
0260     fseek(fid,0,1); % go to end of file
0261     if nargin<5 || nskip<0
0262         nskip=info(2);                      % use previous high water mark
0263     end
0264     info(2)=n+nskip;                          % file position following this write operation (in samples)
0265     ny=nyd+nskip*ncy;                       % file position following this write operation (in bytes following header)
0266     if n && (info(2)>info(4))               % update high water mark
0267         if ~info(4)                           % if no data written previously
0268             fseek(fid,22,-1); fwrite(fid,nc,'ushort');   % update number of channels
0269             fseek(fid,28,-1); fwrite(fid,fs*ncy,'ulong'); % update bytes/second
0270             fwrite(fid,ncy,'ushort'); % update bytes/sample
0271         end
0272         fseek(fid,4,-1); fwrite(fid,wavlen(wavtype)+2*ceil(ny/2),'ulong'); % update RIFF length
0273         if factlen
0274             fseek(fid,wavlen(wavtype)-4-instlen,-1); fwrite(fid,n,'ulong');  % update FACT number of samples
0275         end
0276         fseek(fid,4+wavlen(wavtype),-1); fwrite(fid,ny,'ulong');  % update DATA length
0277         info(4)=info(2);
0278     end
0279 end
0280 info(5)=nc;
0281 if n                            % if there are any data values
0282     if sc~='r'                  % 'r' = no scaling
0283         if sc=='s'              % 's' = scale to peak signal
0284             if nargin>=7 && ~isempty(mad) && mad>0
0285                 pd=mad;
0286             else
0287                 pd=max(abs(d(:)));
0288                 pd=pd+(pd==0);      % scale to 1 if data is all zero
0289             end
0290         elseif sc=='p'          % 'p' = scale to +-1 = full scale
0291             pd=1;
0292         else                    % 'q' = scale to 0dBm
0293             if info(8)==7       % mu-law
0294                 pd=2.03761563;
0295             else                % A-law or anything else
0296                 pd=2.03033976;
0297             end
0298         end
0299         if instlen
0300             info(11)=min(max(ceil(20*log10(pd)),-128),127);
0301             d=pk*10^(-0.05*info(11))*d;
0302             if fseek(fid,0,-1)  % MATLAB V6.5 fails if this is omitted
0303                 error('Cannot rewind file');
0304             end
0305             if fseek(fid,info(3)-instlen+2,-1)
0306                 error('Cannot seek to INST chunk gain byte');
0307             end
0308             fwrite(fid,info(11),'schar');   % write the INST gain in dB
0309         else
0310             d=pk/pd*d;
0311         end
0312     end
0313     if fseek(fid,0,-1)  % MATLAB V6.5 fails if this is omitted
0314         error('Cannot rewind file');
0315     end
0316     if fseek(fid,info(3)+nskip*nc*info(6),-1)
0317         error('Cannot seek to byte %d in output file',info(3)+nskip*nc*info(6));
0318     end
0319     if info(8)==3 % floating point
0320         if info(6)==4
0321             fwrite(fid,d,'float32');
0322         else
0323             fwrite(fid,d,'float64');
0324         end
0325     else                        % integer data
0326         if info(8)<6            % PCM
0327             if di=='n'
0328                 d=round(d);
0329             else
0330                 [d,info(10)]=ditherq(d,di,info(10));
0331             end
0332             d=min(max(d,lo),hi)*pow2(1,8*info(6)-info(7));       % clip data and shift to most significant bits
0333         else                    % mu or A law
0334             z=0;
0335             if info(8) < 7
0336                 d=lin2pcma(d,213,1);
0337             else
0338                 d=lin2pcmu(d,1);
0339             end
0340         end
0341         if info(6)<3
0342             if info(6)<2
0343                 fwrite(fid,d+z,'uchar');
0344             else
0345                 fwrite(fid,d,'short');
0346             end
0347         else
0348             if info(6)<4
0349                 d=d(:)';
0350                 d2=floor(d/65536);
0351                 d=d-65536*d2;
0352                 fwrite(fid,[rem(d,256); floor(d/256); d2+256*(d2<0)],'uchar');
0353             else
0354                 fwrite(fid,d,'long');
0355             end
0356         end
0357         if rem(ny,2) % pad to an even number of bytes
0358             fwrite(fid,0,'uchar');
0359         end
0360     end
0361 end
0362 if all(mode~='f')
0363     fclose(fid);
0364 end
0365 if nargout
0366     fidx=info;
0367 end

Generated on Mon 06-Aug-2018 14:48:32 by m2html © 2003