wolffd@0: function seq_wavwrite(in_files,wavefile,varargin) wolffd@0: % Combines wav-data from folder or specific files in one file. wolffd@0: % wolffd@0: % seq_wavwrite(path_string,wavefile) combines all .wav files in wolffd@0: % path_string in the file wavefile. wolffd@0: % i.e.: seq_wavwrite('C:\wav\','C:\combined.wav') wolffd@0: % wolffd@0: % The file format will depend on the first file processed, the other wolffd@0: % files will be transfered to this format by resampling, dithering and wolffd@0: % channel repetition/rejection. You may use some of the extra-options 'Fs','nbits' wolffd@0: % or 'channels' to override these settings. wolffd@0: % i.e.: seq_wavwrite('C:\wav\','C:\combined.wav','Fs',44100,'nbits',16,'channels',1) wolffd@0: % will produce a mono file with 44.1 kHz samle rate and 16bits per sample wolffd@0: % wolffd@0: % seq_wavwrite(files_cellarray,wavefile) only combines the files specified wolffd@0: % in files_cellarray. wolffd@0: % i.e.: files={'C:\wav\test1.wav','C:\other_wav\test2.wav'}; wolffd@0: % seq_wavwrite(files,'C:\combined.wav'); wolffd@0: % wolffd@0: % You may want to copy only some parts of the files. wolffd@0: % Therefore use the extra-option 'sequences': wolffd@0: % seq_wavwrite(files,'C:\combined','sequences',segments); wolffd@0: % ,where segments is an cell array the same size as the files_cellarray, wolffd@0: % witch contains the position of the parts for every file. wolffd@0: % Every cell row contains a struct array with the following fields: wolffd@0: % abs_startspl and abs_stopspl wolffd@0: % or wolffd@0: % abs_startms and abs_stopms wolffd@0: % You may also specify the channels that are to be copied in the field wolffd@0: % channels wolffd@0: % i.e.: files={'C:\wav\test1.wav','C:\other_wav\test2.wav'}; wolffd@0: % segsforfile1(1).abs_startspl=1; wolffd@0: % segsforfile1(1).abs_stopspl=44100; wolffd@0: % segsforfile1(2).abs_startspl=88200; wolffd@0: % segsforfile1(2).abs_stopspl=200000; wolffd@0: % segsforfile2(1).abs_startms=1; wolffd@0: % segsforfile2(1).abs_stopms=2000; wolffd@0: % segsforfile2(1).channels=[1 2]; <- use the first two channels wolffd@0: % segments={segsforfile1,segsforfile2}; wolffd@0: % seq_wavwrite(files,'C:\combined','sequences',segments); wolffd@0: % wolffd@0: % If you want to copy specific files as a whole, just omit their abs_... wolffd@0: % values. wolffd@0: % wolffd@0: % seq_wavwrite uses blockwise file processing to be able to copy large wolffd@0: % amounts of data. The option 'max_chunksize' allows you to specify the wolffd@0: % blocksize in samples. Keep in mind that in multichannel mode the actual wolffd@0: % blocksize will be chunksize times channels. wolffd@0: % i.e.: seq_wavwrite('C:\wav\','C:\combined.wav','max_chunksize',44100*60) wolffd@0: wolffd@0: % Parse inputs: wolffd@0: wolffd@0: [slash,leftargs]=process_options(varargin,'systemslash','\'); wolffd@0: wolffd@0: if ischar(in_files) wolffd@0: data_names=dir(strcat(in_files,slash,'*.wav')); wolffd@0: in_files=strcat(in_files,{data_names.name}); wolffd@0: end wolffd@0: wolffd@0: [tmp_sig,tmp_Fs,tmp_nbits]=wavread(char(in_files{1}),[1 2]); wolffd@0: tmp_channels=size(tmp_sig,2); wolffd@0: wolffd@0: def_max_chunk_size = 44100*60*2;%chunksize nearly one minute at 44,1 khz sample rate wolffd@0: [sequences,Fs,nbits,channels,max_chunk_size]=process_options(leftargs,'sequences',[],'Fs',... wolffd@0: tmp_Fs,'nbits',tmp_nbits,'channels',tmp_channels,'max_chunksize',def_max_chunk_size); wolffd@0: wolffd@0: if ischar(in_files) && ~isempty(sequences) wolffd@0: warning('segment parameters ignored in directory-input mode') wolffd@0: sequences=[]; wolffd@0: end wolffd@0: wolffd@0: % Determine number of bytes in chunks wolffd@0: % (not including pad bytes, if needed): wolffd@0: % ---------------------------------- wolffd@0: % 'RIFF' 4 bytes wolffd@0: % size 4 bytes wolffd@0: % 'WAVE' 4 bytes wolffd@0: % 'fmt ' 4 bytes wolffd@0: % size 4 bytes wolffd@0: % 14 bytes wolffd@0: % 2 bytes (PCM) wolffd@0: % 'data' 4 bytes wolffd@0: % size 4 bytes wolffd@0: % N bytes wolffd@0: % ---------------------------------- wolffd@0: wolffd@0: bytes_per_sample = ceil(nbits/8); wolffd@0: fmt_cksize = 16; % Don't include 'fmt ' or its size field wolffd@0: wolffd@0: % Open file for output: wolffd@0: [fid,err] = OpenWaveWrite(wavefile); wolffd@0: error(err); wolffd@0: try wolffd@0: % Prepare basic chunk structure fields: wolffd@0: ck=[]; ck.fid=fid; ck.filename = wavefile; wolffd@0: wolffd@0: fwrite(fid,zeros(1,20),'uchar'); %skip previous chunks wolffd@0: % Write : wolffd@0: fmt.filename = wavefile; wolffd@0: if nbits == 32, wolffd@0: fmt.wFormatTag = 3; % Data encoding format (1=PCM, 3=Type 3 32-bit) wolffd@0: else wolffd@0: fmt.wFormatTag = 1; wolffd@0: end wolffd@0: fmt.nSamplesPerSec = Fs; % Samples per second wolffd@0: fmt.nAvgBytesPerSec = channels*bytes_per_sample*Fs; % Avg transfer rate wolffd@0: fmt.nBlockAlign = channels*bytes_per_sample; % Block alignment wolffd@0: fmt.nBitsPerSample = nbits; % standard info wolffd@0: fmt.nChannels = channels; % Number of channels wolffd@0: error(write_wavefmt(fid,fmt)); wolffd@0: wolffd@0: fwrite(fid,zeros(1,8),'uchar'); %skip following chunks wolffd@0: wolffd@0: % Write all audio data wolffd@0: sample_sum=0; wolffd@0: for filei=1:size(in_files,2) wolffd@0: resamplewarn=0; wolffd@0: channelwarn=0; wolffd@0: if ~isempty(sequences)&& ~isempty(sequences{filei}) wolffd@0: numsegs=size(sequences{filei},2); wolffd@0: else numsegs=1; wolffd@0: end wolffd@0: for seqi=1:numsegs; wolffd@0: tmp_fsiz=wavread(char(in_files{filei}),'size'); wolffd@0: tmp_fsiz=tmp_fsiz(1); wolffd@0: [y,tmp_fs,null]=wavread(char(in_files{filei}),[1 2]);%read data wolffd@0: if ~isempty(sequences) && ~isempty(sequences{filei}) wolffd@0: if isfield(sequences{filei}(seqi),'abs_startspl') wolffd@0: spl_seq=[sequences{filei}(seqi).abs_startspl sequences{filei}(seqi).abs_stopspl]; wolffd@0: elseif isfield(sequences{filei}(seqi),'abs_startms') wolffd@0: spl_seq=floor([sequences{filei}(seqi).abs_startms sequences{filei}(seqi).abs_stopms].*tmp_fs./1000); wolffd@0: else wolffd@0: spl_seq=[1 tmp_fsiz]; wolffd@0: end wolffd@0: if (spl_seq(1)< 1) || (spl_seq(2) > tmp_fsiz) wolffd@0: warning('correcting segment range, not necessary critical in miliseconds-mode') wolffd@0: spl_seq(1)=max(spl_seq(1),1); wolffd@0: spl_seq(2)=min(spl_seq(2),tmp_fsiz); wolffd@0: end wolffd@0: else wolffd@0: spl_seq=[1 tmp_fsiz]; wolffd@0: end wolffd@0: win_start=spl_seq(1); wolffd@0: win_stop=(min(spl_seq(2),spl_seq(1)+max_chunk_size-1)); wolffd@0: while win_stop <= spl_seq(2) wolffd@0: [y,tmp_fs,null]=wavread(char(in_files{filei}),[win_start win_stop]);%read data wolffd@0: if (size(y,2) > 1) && ~isempty(sequences) && isfield(sequences{filei}(seqi),'channels') %choose channel wolffd@0: if size(y,2) >= max(sequences{filei}(seqi).channels) wolffd@0: y=y(:,sequences{filei}(seqi).channels); wolffd@0: else wolffd@0: if ~channelwarn wolffd@0: warning('ignoring errorneous channel field'); wolffd@0: channelwarn=1; wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: if (tmp_fs ~= Fs) %resample data if necessary wolffd@0: if ~resamplewarn wolffd@0: fprintf('seq_wavwrite.m: resampling from %d to %d Hz. \n',tmp_fs,Fs); wolffd@0: resamplewarn=1; wolffd@0: end wolffd@0: y=resample(y,Fs,tmp_fs); wolffd@0: end wolffd@0: [samples,akt_channels] = size(y); wolffd@0: if akt_channels > channels % if necessary make equivalent channelnum wolffd@0: y=y(:,1:channels); wolffd@0: elseif akt_channels < channels wolffd@0: y=[y repmat(y(:,end),1,channels-akt_channels)]; wolffd@0: end wolffd@0: error(write_wavedat(fid,fmt,y)); wolffd@0: sample_sum=sample_sum+samples; wolffd@0: wolffd@0: if win_stop == spl_seq(2), break; wolffd@0: end wolffd@0: win_start=win_start+max_chunk_size; wolffd@0: win_stop=(min(spl_seq(2),win_stop+max_chunk_size)); wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: clear y; wolffd@0: wolffd@0: total_samples = sample_sum * channels; wolffd@0: total_bytes = total_samples * bytes_per_sample; wolffd@0: data_cksize = total_bytes; wolffd@0: wolffd@0: riff_cksize = 36+total_bytes; wolffd@0: wolffd@0: % Determine pad bytes: wolffd@0: % Determine if a pad-byte must be appended to data chunk: wolffd@0: if rem(data_cksize, 2) ~= 0, wolffd@0: fwrite(fid,0,'uchar'); wolffd@0: end wolffd@0: data_pad = rem(data_cksize,2); wolffd@0: riff_cksize = riff_cksize + data_pad; % + fmt_pad, always 0 wolffd@0: wolffd@0: % Write RIFF chunk: wolffd@0: fseek(fid,0,'bof'); wolffd@0: ck.ID = 'RIFF'; wolffd@0: ck.Size = riff_cksize; wolffd@0: error(write_ckinfo(ck)); wolffd@0: wolffd@0: % Write WAVE subchunk: wolffd@0: ck.ID = 'WAVE'; wolffd@0: ck.Size = []; % Indicate a subchunk (no chunk size) wolffd@0: error(write_ckinfo(ck)); wolffd@0: wolffd@0: % Write : wolffd@0: ck.ID = 'fmt '; wolffd@0: ck.Size = fmt_cksize; wolffd@0: error(write_ckinfo(ck)); wolffd@0: wolffd@0: % Write : wolffd@0: fseek(fid,36,'bof'); wolffd@0: ck.ID = 'data'; wolffd@0: ck.Size = data_cksize; wolffd@0: error(write_ckinfo(ck)); wolffd@0: err=''; wolffd@0: catch wolffd@0: err=lasterr; wolffd@0: end wolffd@0: % Close file: wolffd@0: fclose(fid); wolffd@0: wolffd@0: error(err); wolffd@0: % end of wavwrite() wolffd@0: wolffd@0: wolffd@0: % ------------------------------------------------------------------------ wolffd@0: % Private functions: wolffd@0: % ------------------------------------------------------------------------ wolffd@0: wolffd@0: wolffd@0: % ------------------------------------------------------------------------ wolffd@0: function [fid,err] = OpenWaveWrite(wavefile) wolffd@0: % OpenWaveWrite wolffd@0: % Open WAV file for writing. wolffd@0: % If filename does not contain an extension, add ".wav" wolffd@0: wolffd@0: fid = []; wolffd@0: err = ''; wolffd@0: if ~isstr(wavefile), wolffd@0: err='Wave file name must be a string.'; return; wolffd@0: end wolffd@0: if isempty(findstr(wavefile,'.')), wolffd@0: wavefile=[wavefile '.wav']; wolffd@0: end wolffd@0: % Open file, little-endian: wolffd@0: [fid,err] = fopen(wavefile,'wb','l'); wolffd@0: wolffd@0: return wolffd@0: wolffd@0: wolffd@0: % ------------------------------------------------------------------------ wolffd@0: function err = write_ckinfo(ck) wolffd@0: % WRITE_CKINFO: Writes next RIFF chunk, but not the chunk data. wolffd@0: % Assumes the following fields in ck: wolffd@0: % .fid File ID to an open file wolffd@0: % .ID 4-character string chunk identifier wolffd@0: % .Size Size of chunk (empty if subchunk) wolffd@0: % wolffd@0: % wolffd@0: % Expects an open FID pointing to first byte of chunk header, wolffd@0: % and a chunk structure. wolffd@0: % ck.fid, ck.ID, ck.Size, ck.Data wolffd@0: wolffd@0: errmsg = ['Failed to write ' ck.ID ' chunk to WAVE file: ' ck.filename]; wolffd@0: err = ''; wolffd@0: wolffd@0: if (fwrite(ck.fid, ck.ID, 'char') ~= 4), wolffd@0: err=errmsg; return; wolffd@0: end wolffd@0: wolffd@0: if ~isempty(ck.Size), wolffd@0: % Write chunk size: wolffd@0: if (fwrite(ck.fid, ck.Size, 'uint32') ~= 1), wolffd@0: err=errmsg; return; wolffd@0: end wolffd@0: end wolffd@0: wolffd@0: return wolffd@0: wolffd@0: % ------------------------------------------------------------------------ wolffd@0: function err = write_wavefmt(fid, fmt) wolffd@0: % WRITE_WAVEFMT: Write WAVE format chunk. wolffd@0: % Assumes fid points to the wave-format subchunk. wolffd@0: % Requires chunk structure to be passed, indicating wolffd@0: % the length of the chunk. wolffd@0: wolffd@0: errmsg = ['Failed to write WAVE format chunk to file' fmt.filename]; wolffd@0: err = ''; wolffd@0: wolffd@0: % Create data: wolffd@0: if (fwrite(fid, fmt.wFormatTag, 'uint16') ~= 1) | ... wolffd@0: (fwrite(fid, fmt.nChannels, 'uint16') ~= 1) | ... wolffd@0: (fwrite(fid, fmt.nSamplesPerSec, 'uint32' ) ~= 1) | ... wolffd@0: (fwrite(fid, fmt.nAvgBytesPerSec, 'uint32' ) ~= 1) | ... wolffd@0: (fwrite(fid, fmt.nBlockAlign, 'uint16') ~= 1), wolffd@0: err=errmsg; return; wolffd@0: end wolffd@0: wolffd@0: % Write format-specific info: wolffd@0: if fmt.wFormatTag==1 | fmt.wFormatTag==3, wolffd@0: % Write standard info: wolffd@0: if (fwrite(fid, fmt.nBitsPerSample, 'uint16') ~= 1), wolffd@0: err=errmsg; return; wolffd@0: end wolffd@0: wolffd@0: else wolffd@0: err='Unknown data format.'; wolffd@0: end wolffd@0: wolffd@0: return wolffd@0: wolffd@0: wolffd@0: % ----------------------------------------------------------------------- wolffd@0: function y = PCM_Quantize(x, fmt) wolffd@0: % PCM_Quantize: wolffd@0: % Scale and quantize input data, from [-1, +1] range to wolffd@0: % either an 8-, 16-, or 24-bit data range. wolffd@0: wolffd@0: % Clip data to normalized range [-1,+1]: wolffd@0: ClipMsg = ['Data clipped during write to file:' fmt.filename]; wolffd@0: ClipWarn = 0; wolffd@0: wolffd@0: % Determine slope (m) and bias (b) for data scaling: wolffd@0: nbits = fmt.nBitsPerSample; wolffd@0: m = 2.^(nbits-1); wolffd@0: wolffd@0: switch nbits wolffd@0: case 8, wolffd@0: b=128; wolffd@0: case {16,24}, wolffd@0: b=0; wolffd@0: otherwise, wolffd@0: error('Invalid number of bits specified.'); wolffd@0: end wolffd@0: wolffd@0: y = round(m .* x + b); wolffd@0: wolffd@0: % Determine quantized data limits, based on the wolffd@0: % presumed input data limits of [-1, +1]: wolffd@0: ylim = [-1 +1]; wolffd@0: qlim = m * ylim + b; wolffd@0: qlim(2) = qlim(2)-1; wolffd@0: wolffd@0: % Clip data to quantizer limits: wolffd@0: i = find(y < qlim(1)); wolffd@0: if ~isempty(i), wolffd@0: warning(ClipMsg); ClipWarn=1; wolffd@0: y(i) = qlim(1); wolffd@0: end wolffd@0: wolffd@0: i = find(y > qlim(2)); wolffd@0: if ~isempty(i), wolffd@0: if ~ClipWarn, warning(ClipMsg); end wolffd@0: y(i) = qlim(2); wolffd@0: end wolffd@0: wolffd@0: return wolffd@0: wolffd@0: wolffd@0: % ----------------------------------------------------------------------- wolffd@0: function err = write_wavedat(fid,fmt,data) wolffd@0: % WRITE_WAVEDAT: Write WAVE data chunk wolffd@0: % Assumes fid points to the wave-data chunk wolffd@0: % Requires structure to be passed. wolffd@0: wolffd@0: err = ''; wolffd@0: wolffd@0: if fmt.wFormatTag==1 | fmt.wFormatTag==3, wolffd@0: % PCM Format wolffd@0: wolffd@0: % 32-bit Type 3 is normalized, so no scaling needed. wolffd@0: if fmt.nBitsPerSample ~= 32, wolffd@0: data = PCM_Quantize(data, fmt); wolffd@0: end wolffd@0: wolffd@0: switch fmt.nBitsPerSample wolffd@0: case 8, wolffd@0: dtype='uchar'; % unsigned 8-bit wolffd@0: case 16, wolffd@0: dtype='int16'; % signed 16-bit wolffd@0: case 24, wolffd@0: dtype='bit24'; % signed 24-bit wolffd@0: case 32, wolffd@0: dtype='float'; % normalized 32-bit floating point wolffd@0: otherwise, wolffd@0: err = 'Invalid number of bits specified.'; return; wolffd@0: end wolffd@0: wolffd@0: % Write data, one row at a time (one sample from each channel): wolffd@0: [samples,channels] = size(data); wolffd@0: total_samples = samples*channels; wolffd@0: wolffd@0: if (fwrite(fid, reshape(data',total_samples,1), dtype) ~= total_samples), wolffd@0: err = 'Failed to write PCM data samples.'; return; wolffd@0: end wolffd@0: wolffd@0: wolffd@0: else wolffd@0: % Unknown wave-format for data. wolffd@0: err = 'Unsupported data format.'; wolffd@0: end wolffd@0: wolffd@0: return wolffd@0: wolffd@0: % end of wavwrite.m