Home > voicebox > writewav.m

writewav

PURPOSE ^

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

SYNOPSIS ^

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

DESCRIPTION ^

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

   The input arguments for WRITEWAV are as follows:

       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 (see readwav)

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

Generated on Fri 22-Sep-2017 19:37:38 by m2html © 2003