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