V_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 v_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 "v_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: v_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
0001 function fidx=v_writewav(d,fs,filename,mode,nskip,mask,mad) 0002 %V_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 v_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 "v_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: v_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: v_writewav.m 10865 2018-09-21 17:22:45Z 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(v_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)]=v_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=v_lin2pcma(d,213,1); 0337 else 0338 d=v_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