annotate toolboxes/bioakustik_tools/sp/seq_wavwrite.m @ 0:e9a9cd732c1e tip

first hg version after svn
author wolffd
date Tue, 10 Feb 2015 15:05:51 +0000
parents
children
rev   line source
wolffd@0 1 function seq_wavwrite(in_files,wavefile,varargin)
wolffd@0 2 % Combines wav-data from folder or specific files in one file.
wolffd@0 3 %
wolffd@0 4 % seq_wavwrite(path_string,wavefile) combines all .wav files in
wolffd@0 5 % path_string in the file wavefile.
wolffd@0 6 % i.e.: seq_wavwrite('C:\wav\','C:\combined.wav')
wolffd@0 7 %
wolffd@0 8 % The file format will depend on the first file processed, the other
wolffd@0 9 % files will be transfered to this format by resampling, dithering and
wolffd@0 10 % channel repetition/rejection. You may use some of the extra-options 'Fs','nbits'
wolffd@0 11 % or 'channels' to override these settings.
wolffd@0 12 % i.e.: seq_wavwrite('C:\wav\','C:\combined.wav','Fs',44100,'nbits',16,'channels',1)
wolffd@0 13 % will produce a mono file with 44.1 kHz samle rate and 16bits per sample
wolffd@0 14 %
wolffd@0 15 % seq_wavwrite(files_cellarray,wavefile) only combines the files specified
wolffd@0 16 % in files_cellarray.
wolffd@0 17 % i.e.: files={'C:\wav\test1.wav','C:\other_wav\test2.wav'};
wolffd@0 18 % seq_wavwrite(files,'C:\combined.wav');
wolffd@0 19 %
wolffd@0 20 % You may want to copy only some parts of the files.
wolffd@0 21 % Therefore use the extra-option 'sequences':
wolffd@0 22 % seq_wavwrite(files,'C:\combined','sequences',segments);
wolffd@0 23 % ,where segments is an cell array the same size as the files_cellarray,
wolffd@0 24 % witch contains the position of the parts for every file.
wolffd@0 25 % Every cell row contains a struct array with the following fields:
wolffd@0 26 % abs_startspl and abs_stopspl
wolffd@0 27 % or
wolffd@0 28 % abs_startms and abs_stopms
wolffd@0 29 % You may also specify the channels that are to be copied in the field
wolffd@0 30 % channels
wolffd@0 31 % i.e.: files={'C:\wav\test1.wav','C:\other_wav\test2.wav'};
wolffd@0 32 % segsforfile1(1).abs_startspl=1;
wolffd@0 33 % segsforfile1(1).abs_stopspl=44100;
wolffd@0 34 % segsforfile1(2).abs_startspl=88200;
wolffd@0 35 % segsforfile1(2).abs_stopspl=200000;
wolffd@0 36 % segsforfile2(1).abs_startms=1;
wolffd@0 37 % segsforfile2(1).abs_stopms=2000;
wolffd@0 38 % segsforfile2(1).channels=[1 2]; <- use the first two channels
wolffd@0 39 % segments={segsforfile1,segsforfile2};
wolffd@0 40 % seq_wavwrite(files,'C:\combined','sequences',segments);
wolffd@0 41 %
wolffd@0 42 % If you want to copy specific files as a whole, just omit their abs_...
wolffd@0 43 % values.
wolffd@0 44 %
wolffd@0 45 % seq_wavwrite uses blockwise file processing to be able to copy large
wolffd@0 46 % amounts of data. The option 'max_chunksize' allows you to specify the
wolffd@0 47 % blocksize in samples. Keep in mind that in multichannel mode the actual
wolffd@0 48 % blocksize will be chunksize times channels.
wolffd@0 49 % i.e.: seq_wavwrite('C:\wav\','C:\combined.wav','max_chunksize',44100*60)
wolffd@0 50
wolffd@0 51 % Parse inputs:
wolffd@0 52
wolffd@0 53 [slash,leftargs]=process_options(varargin,'systemslash','\');
wolffd@0 54
wolffd@0 55 if ischar(in_files)
wolffd@0 56 data_names=dir(strcat(in_files,slash,'*.wav'));
wolffd@0 57 in_files=strcat(in_files,{data_names.name});
wolffd@0 58 end
wolffd@0 59
wolffd@0 60 [tmp_sig,tmp_Fs,tmp_nbits]=wavread(char(in_files{1}),[1 2]);
wolffd@0 61 tmp_channels=size(tmp_sig,2);
wolffd@0 62
wolffd@0 63 def_max_chunk_size = 44100*60*2;%chunksize nearly one minute at 44,1 khz sample rate
wolffd@0 64 [sequences,Fs,nbits,channels,max_chunk_size]=process_options(leftargs,'sequences',[],'Fs',...
wolffd@0 65 tmp_Fs,'nbits',tmp_nbits,'channels',tmp_channels,'max_chunksize',def_max_chunk_size);
wolffd@0 66
wolffd@0 67 if ischar(in_files) && ~isempty(sequences)
wolffd@0 68 warning('segment parameters ignored in directory-input mode')
wolffd@0 69 sequences=[];
wolffd@0 70 end
wolffd@0 71
wolffd@0 72 % Determine number of bytes in chunks
wolffd@0 73 % (not including pad bytes, if needed):
wolffd@0 74 % ----------------------------------
wolffd@0 75 % 'RIFF' 4 bytes
wolffd@0 76 % size 4 bytes
wolffd@0 77 % 'WAVE' 4 bytes
wolffd@0 78 % 'fmt ' 4 bytes
wolffd@0 79 % size 4 bytes
wolffd@0 80 % <wave-format> 14 bytes
wolffd@0 81 % <format_specific> 2 bytes (PCM)
wolffd@0 82 % 'data' 4 bytes
wolffd@0 83 % size 4 bytes
wolffd@0 84 % <wave-data> N bytes
wolffd@0 85 % ----------------------------------
wolffd@0 86
wolffd@0 87 bytes_per_sample = ceil(nbits/8);
wolffd@0 88 fmt_cksize = 16; % Don't include 'fmt ' or its size field
wolffd@0 89
wolffd@0 90 % Open file for output:
wolffd@0 91 [fid,err] = OpenWaveWrite(wavefile);
wolffd@0 92 error(err);
wolffd@0 93 try
wolffd@0 94 % Prepare basic chunk structure fields:
wolffd@0 95 ck=[]; ck.fid=fid; ck.filename = wavefile;
wolffd@0 96
wolffd@0 97 fwrite(fid,zeros(1,20),'uchar'); %skip previous chunks
wolffd@0 98 % Write <wave-format>:
wolffd@0 99 fmt.filename = wavefile;
wolffd@0 100 if nbits == 32,
wolffd@0 101 fmt.wFormatTag = 3; % Data encoding format (1=PCM, 3=Type 3 32-bit)
wolffd@0 102 else
wolffd@0 103 fmt.wFormatTag = 1;
wolffd@0 104 end
wolffd@0 105 fmt.nSamplesPerSec = Fs; % Samples per second
wolffd@0 106 fmt.nAvgBytesPerSec = channels*bytes_per_sample*Fs; % Avg transfer rate
wolffd@0 107 fmt.nBlockAlign = channels*bytes_per_sample; % Block alignment
wolffd@0 108 fmt.nBitsPerSample = nbits; % standard <PCM-format-specific> info
wolffd@0 109 fmt.nChannels = channels; % Number of channels
wolffd@0 110 error(write_wavefmt(fid,fmt));
wolffd@0 111
wolffd@0 112 fwrite(fid,zeros(1,8),'uchar'); %skip following chunks
wolffd@0 113
wolffd@0 114 % Write all audio data
wolffd@0 115 sample_sum=0;
wolffd@0 116 for filei=1:size(in_files,2)
wolffd@0 117 resamplewarn=0;
wolffd@0 118 channelwarn=0;
wolffd@0 119 if ~isempty(sequences)&& ~isempty(sequences{filei})
wolffd@0 120 numsegs=size(sequences{filei},2);
wolffd@0 121 else numsegs=1;
wolffd@0 122 end
wolffd@0 123 for seqi=1:numsegs;
wolffd@0 124 tmp_fsiz=wavread(char(in_files{filei}),'size');
wolffd@0 125 tmp_fsiz=tmp_fsiz(1);
wolffd@0 126 [y,tmp_fs,null]=wavread(char(in_files{filei}),[1 2]);%read data
wolffd@0 127 if ~isempty(sequences) && ~isempty(sequences{filei})
wolffd@0 128 if isfield(sequences{filei}(seqi),'abs_startspl')
wolffd@0 129 spl_seq=[sequences{filei}(seqi).abs_startspl sequences{filei}(seqi).abs_stopspl];
wolffd@0 130 elseif isfield(sequences{filei}(seqi),'abs_startms')
wolffd@0 131 spl_seq=floor([sequences{filei}(seqi).abs_startms sequences{filei}(seqi).abs_stopms].*tmp_fs./1000);
wolffd@0 132 else
wolffd@0 133 spl_seq=[1 tmp_fsiz];
wolffd@0 134 end
wolffd@0 135 if (spl_seq(1)< 1) || (spl_seq(2) > tmp_fsiz)
wolffd@0 136 warning('correcting segment range, not necessary critical in miliseconds-mode')
wolffd@0 137 spl_seq(1)=max(spl_seq(1),1);
wolffd@0 138 spl_seq(2)=min(spl_seq(2),tmp_fsiz);
wolffd@0 139 end
wolffd@0 140 else
wolffd@0 141 spl_seq=[1 tmp_fsiz];
wolffd@0 142 end
wolffd@0 143 win_start=spl_seq(1);
wolffd@0 144 win_stop=(min(spl_seq(2),spl_seq(1)+max_chunk_size-1));
wolffd@0 145 while win_stop <= spl_seq(2)
wolffd@0 146 [y,tmp_fs,null]=wavread(char(in_files{filei}),[win_start win_stop]);%read data
wolffd@0 147 if (size(y,2) > 1) && ~isempty(sequences) && isfield(sequences{filei}(seqi),'channels') %choose channel
wolffd@0 148 if size(y,2) >= max(sequences{filei}(seqi).channels)
wolffd@0 149 y=y(:,sequences{filei}(seqi).channels);
wolffd@0 150 else
wolffd@0 151 if ~channelwarn
wolffd@0 152 warning('ignoring errorneous channel field');
wolffd@0 153 channelwarn=1;
wolffd@0 154 end
wolffd@0 155 end
wolffd@0 156 end
wolffd@0 157 if (tmp_fs ~= Fs) %resample data if necessary
wolffd@0 158 if ~resamplewarn
wolffd@0 159 fprintf('seq_wavwrite.m: resampling from %d to %d Hz. \n',tmp_fs,Fs);
wolffd@0 160 resamplewarn=1;
wolffd@0 161 end
wolffd@0 162 y=resample(y,Fs,tmp_fs);
wolffd@0 163 end
wolffd@0 164 [samples,akt_channels] = size(y);
wolffd@0 165 if akt_channels > channels % if necessary make equivalent channelnum
wolffd@0 166 y=y(:,1:channels);
wolffd@0 167 elseif akt_channels < channels
wolffd@0 168 y=[y repmat(y(:,end),1,channels-akt_channels)];
wolffd@0 169 end
wolffd@0 170 error(write_wavedat(fid,fmt,y));
wolffd@0 171 sample_sum=sample_sum+samples;
wolffd@0 172
wolffd@0 173 if win_stop == spl_seq(2), break;
wolffd@0 174 end
wolffd@0 175 win_start=win_start+max_chunk_size;
wolffd@0 176 win_stop=(min(spl_seq(2),win_stop+max_chunk_size));
wolffd@0 177 end
wolffd@0 178 end
wolffd@0 179 end
wolffd@0 180 clear y;
wolffd@0 181
wolffd@0 182 total_samples = sample_sum * channels;
wolffd@0 183 total_bytes = total_samples * bytes_per_sample;
wolffd@0 184 data_cksize = total_bytes;
wolffd@0 185
wolffd@0 186 riff_cksize = 36+total_bytes;
wolffd@0 187
wolffd@0 188 % Determine pad bytes:
wolffd@0 189 % Determine if a pad-byte must be appended to data chunk:
wolffd@0 190 if rem(data_cksize, 2) ~= 0,
wolffd@0 191 fwrite(fid,0,'uchar');
wolffd@0 192 end
wolffd@0 193 data_pad = rem(data_cksize,2);
wolffd@0 194 riff_cksize = riff_cksize + data_pad; % + fmt_pad, always 0
wolffd@0 195
wolffd@0 196 % Write RIFF chunk:
wolffd@0 197 fseek(fid,0,'bof');
wolffd@0 198 ck.ID = 'RIFF';
wolffd@0 199 ck.Size = riff_cksize;
wolffd@0 200 error(write_ckinfo(ck));
wolffd@0 201
wolffd@0 202 % Write WAVE subchunk:
wolffd@0 203 ck.ID = 'WAVE';
wolffd@0 204 ck.Size = []; % Indicate a subchunk (no chunk size)
wolffd@0 205 error(write_ckinfo(ck));
wolffd@0 206
wolffd@0 207 % Write <fmt-ck>:
wolffd@0 208 ck.ID = 'fmt ';
wolffd@0 209 ck.Size = fmt_cksize;
wolffd@0 210 error(write_ckinfo(ck));
wolffd@0 211
wolffd@0 212 % Write <data-ck>:
wolffd@0 213 fseek(fid,36,'bof');
wolffd@0 214 ck.ID = 'data';
wolffd@0 215 ck.Size = data_cksize;
wolffd@0 216 error(write_ckinfo(ck));
wolffd@0 217 err='';
wolffd@0 218 catch
wolffd@0 219 err=lasterr;
wolffd@0 220 end
wolffd@0 221 % Close file:
wolffd@0 222 fclose(fid);
wolffd@0 223
wolffd@0 224 error(err);
wolffd@0 225 % end of wavwrite()
wolffd@0 226
wolffd@0 227
wolffd@0 228 % ------------------------------------------------------------------------
wolffd@0 229 % Private functions:
wolffd@0 230 % ------------------------------------------------------------------------
wolffd@0 231
wolffd@0 232
wolffd@0 233 % ------------------------------------------------------------------------
wolffd@0 234 function [fid,err] = OpenWaveWrite(wavefile)
wolffd@0 235 % OpenWaveWrite
wolffd@0 236 % Open WAV file for writing.
wolffd@0 237 % If filename does not contain an extension, add ".wav"
wolffd@0 238
wolffd@0 239 fid = [];
wolffd@0 240 err = '';
wolffd@0 241 if ~isstr(wavefile),
wolffd@0 242 err='Wave file name must be a string.'; return;
wolffd@0 243 end
wolffd@0 244 if isempty(findstr(wavefile,'.')),
wolffd@0 245 wavefile=[wavefile '.wav'];
wolffd@0 246 end
wolffd@0 247 % Open file, little-endian:
wolffd@0 248 [fid,err] = fopen(wavefile,'wb','l');
wolffd@0 249
wolffd@0 250 return
wolffd@0 251
wolffd@0 252
wolffd@0 253 % ------------------------------------------------------------------------
wolffd@0 254 function err = write_ckinfo(ck)
wolffd@0 255 % WRITE_CKINFO: Writes next RIFF chunk, but not the chunk data.
wolffd@0 256 % Assumes the following fields in ck:
wolffd@0 257 % .fid File ID to an open file
wolffd@0 258 % .ID 4-character string chunk identifier
wolffd@0 259 % .Size Size of chunk (empty if subchunk)
wolffd@0 260 %
wolffd@0 261 %
wolffd@0 262 % Expects an open FID pointing to first byte of chunk header,
wolffd@0 263 % and a chunk structure.
wolffd@0 264 % ck.fid, ck.ID, ck.Size, ck.Data
wolffd@0 265
wolffd@0 266 errmsg = ['Failed to write ' ck.ID ' chunk to WAVE file: ' ck.filename];
wolffd@0 267 err = '';
wolffd@0 268
wolffd@0 269 if (fwrite(ck.fid, ck.ID, 'char') ~= 4),
wolffd@0 270 err=errmsg; return;
wolffd@0 271 end
wolffd@0 272
wolffd@0 273 if ~isempty(ck.Size),
wolffd@0 274 % Write chunk size:
wolffd@0 275 if (fwrite(ck.fid, ck.Size, 'uint32') ~= 1),
wolffd@0 276 err=errmsg; return;
wolffd@0 277 end
wolffd@0 278 end
wolffd@0 279
wolffd@0 280 return
wolffd@0 281
wolffd@0 282 % ------------------------------------------------------------------------
wolffd@0 283 function err = write_wavefmt(fid, fmt)
wolffd@0 284 % WRITE_WAVEFMT: Write WAVE format chunk.
wolffd@0 285 % Assumes fid points to the wave-format subchunk.
wolffd@0 286 % Requires chunk structure to be passed, indicating
wolffd@0 287 % the length of the chunk.
wolffd@0 288
wolffd@0 289 errmsg = ['Failed to write WAVE format chunk to file' fmt.filename];
wolffd@0 290 err = '';
wolffd@0 291
wolffd@0 292 % Create <wave-format> data:
wolffd@0 293 if (fwrite(fid, fmt.wFormatTag, 'uint16') ~= 1) | ...
wolffd@0 294 (fwrite(fid, fmt.nChannels, 'uint16') ~= 1) | ...
wolffd@0 295 (fwrite(fid, fmt.nSamplesPerSec, 'uint32' ) ~= 1) | ...
wolffd@0 296 (fwrite(fid, fmt.nAvgBytesPerSec, 'uint32' ) ~= 1) | ...
wolffd@0 297 (fwrite(fid, fmt.nBlockAlign, 'uint16') ~= 1),
wolffd@0 298 err=errmsg; return;
wolffd@0 299 end
wolffd@0 300
wolffd@0 301 % Write format-specific info:
wolffd@0 302 if fmt.wFormatTag==1 | fmt.wFormatTag==3,
wolffd@0 303 % Write standard <PCM-format-specific> info:
wolffd@0 304 if (fwrite(fid, fmt.nBitsPerSample, 'uint16') ~= 1),
wolffd@0 305 err=errmsg; return;
wolffd@0 306 end
wolffd@0 307
wolffd@0 308 else
wolffd@0 309 err='Unknown data format.';
wolffd@0 310 end
wolffd@0 311
wolffd@0 312 return
wolffd@0 313
wolffd@0 314
wolffd@0 315 % -----------------------------------------------------------------------
wolffd@0 316 function y = PCM_Quantize(x, fmt)
wolffd@0 317 % PCM_Quantize:
wolffd@0 318 % Scale and quantize input data, from [-1, +1] range to
wolffd@0 319 % either an 8-, 16-, or 24-bit data range.
wolffd@0 320
wolffd@0 321 % Clip data to normalized range [-1,+1]:
wolffd@0 322 ClipMsg = ['Data clipped during write to file:' fmt.filename];
wolffd@0 323 ClipWarn = 0;
wolffd@0 324
wolffd@0 325 % Determine slope (m) and bias (b) for data scaling:
wolffd@0 326 nbits = fmt.nBitsPerSample;
wolffd@0 327 m = 2.^(nbits-1);
wolffd@0 328
wolffd@0 329 switch nbits
wolffd@0 330 case 8,
wolffd@0 331 b=128;
wolffd@0 332 case {16,24},
wolffd@0 333 b=0;
wolffd@0 334 otherwise,
wolffd@0 335 error('Invalid number of bits specified.');
wolffd@0 336 end
wolffd@0 337
wolffd@0 338 y = round(m .* x + b);
wolffd@0 339
wolffd@0 340 % Determine quantized data limits, based on the
wolffd@0 341 % presumed input data limits of [-1, +1]:
wolffd@0 342 ylim = [-1 +1];
wolffd@0 343 qlim = m * ylim + b;
wolffd@0 344 qlim(2) = qlim(2)-1;
wolffd@0 345
wolffd@0 346 % Clip data to quantizer limits:
wolffd@0 347 i = find(y < qlim(1));
wolffd@0 348 if ~isempty(i),
wolffd@0 349 warning(ClipMsg); ClipWarn=1;
wolffd@0 350 y(i) = qlim(1);
wolffd@0 351 end
wolffd@0 352
wolffd@0 353 i = find(y > qlim(2));
wolffd@0 354 if ~isempty(i),
wolffd@0 355 if ~ClipWarn, warning(ClipMsg); end
wolffd@0 356 y(i) = qlim(2);
wolffd@0 357 end
wolffd@0 358
wolffd@0 359 return
wolffd@0 360
wolffd@0 361
wolffd@0 362 % -----------------------------------------------------------------------
wolffd@0 363 function err = write_wavedat(fid,fmt,data)
wolffd@0 364 % WRITE_WAVEDAT: Write WAVE data chunk
wolffd@0 365 % Assumes fid points to the wave-data chunk
wolffd@0 366 % Requires <wave-format> structure to be passed.
wolffd@0 367
wolffd@0 368 err = '';
wolffd@0 369
wolffd@0 370 if fmt.wFormatTag==1 | fmt.wFormatTag==3,
wolffd@0 371 % PCM Format
wolffd@0 372
wolffd@0 373 % 32-bit Type 3 is normalized, so no scaling needed.
wolffd@0 374 if fmt.nBitsPerSample ~= 32,
wolffd@0 375 data = PCM_Quantize(data, fmt);
wolffd@0 376 end
wolffd@0 377
wolffd@0 378 switch fmt.nBitsPerSample
wolffd@0 379 case 8,
wolffd@0 380 dtype='uchar'; % unsigned 8-bit
wolffd@0 381 case 16,
wolffd@0 382 dtype='int16'; % signed 16-bit
wolffd@0 383 case 24,
wolffd@0 384 dtype='bit24'; % signed 24-bit
wolffd@0 385 case 32,
wolffd@0 386 dtype='float'; % normalized 32-bit floating point
wolffd@0 387 otherwise,
wolffd@0 388 err = 'Invalid number of bits specified.'; return;
wolffd@0 389 end
wolffd@0 390
wolffd@0 391 % Write data, one row at a time (one sample from each channel):
wolffd@0 392 [samples,channels] = size(data);
wolffd@0 393 total_samples = samples*channels;
wolffd@0 394
wolffd@0 395 if (fwrite(fid, reshape(data',total_samples,1), dtype) ~= total_samples),
wolffd@0 396 err = 'Failed to write PCM data samples.'; return;
wolffd@0 397 end
wolffd@0 398
wolffd@0 399
wolffd@0 400 else
wolffd@0 401 % Unknown wave-format for data.
wolffd@0 402 err = 'Unsupported data format.';
wolffd@0 403 end
wolffd@0 404
wolffd@0 405 return
wolffd@0 406
wolffd@0 407 % end of wavwrite.m