dawn@0: function i = sf_info(i) dawn@0: % i=sf_info(i) - extract useful info from file dawn@0: dawn@0: % Alain de Cheveigné, CNRS/Ircam, 2002. dawn@0: % Copyright (c) 2002 Centre National de la Recherche Scientifique. dawn@0: % dawn@0: % Permission to use, copy, modify, and distribute this software without dawn@0: % fee is hereby granted FOR RESEARCH PURPOSES only, provided that this dawn@0: % copyright notice appears in all copies and in all supporting dawn@0: % documentation, and that the software is not redistributed for any dawn@0: % fee (except for a nominal shipping charge). dawn@0: % dawn@0: % For any other uses of this software, in original or modified form, dawn@0: % including but not limited to consulting, production or distribution dawn@0: % in whole or in part, specific prior permission must be obtained from CNRS. dawn@0: % Algorithms implemented by this software may be claimed by patents owned dawn@0: % by CNRS, France Telecom, Ircam or others. dawn@0: % dawn@0: % The CNRS makes no representations about the suitability of this dawn@0: % software for any purpose. It is provided "as is" without express dawn@0: % or implied warranty. Beware of the bugs. dawn@0: dawn@0: if ~nargin ; error('sf_info: no input arguement'); end dawn@0: if ~isa(i, 'struct') dawn@0: j.fname=i; dawn@0: i=j; dawn@0: i=sf_info(i); dawn@0: return dawn@0: end dawn@0: if ~isfield(i, 'fname'); error('sf_info: no fname field'); end dawn@0: dawn@0: % guess format only if unknown dawn@0: if ~isfield(i, 'format') | isempty(i.format); dawn@0: i = sf_format(i); dawn@0: if ~strcmp(i.format, 'matrix') disp(i.format); end dawn@0: end dawn@0: dawn@0: % handle workspace matrices as if they were files dawn@0: if strcmp(i.format, 'matrix') dawn@0: [nrows, ncols] = size(i.fname); dawn@0: i.nchans=ncols; dawn@0: i.nsamples=nrows; dawn@0: i.totalsamples = i.nsamples*i.nchans; dawn@0: i.sr=[]; dawn@0: return; dawn@0: end dawn@0: dawn@0: % close file if open dawn@0: if exist('i.fd') & fopen(i.fd) dawn@0: fclose(i.fd); dawn@0: end dawn@0: dawn@0: % use standard matlab functions for AU and WAV and MACSND dawn@0: if strcmp(i.format, 'AU') dawn@0: if isempty(findstr('.', i.fname)) dawn@0: disp(['WARNING: matlab function AUREAD requires '... dawn@0: '.au or .snd suffix on file name: ' i.fname]); dawn@0: end dawn@0: sz = auread(i.fname, 'size'); dawn@0: i.nsamples=sz(1); dawn@0: i.nchans=sz(2); dawn@0: i.totalsamples = i.nsamples*i.nchans; dawn@0: [dummy, i.sr, i.samplebits] = auread(i.fname, 1); dawn@0: return; dawn@0: end dawn@0: if strcmp(i.format, 'WAV') dawn@0: if isempty(findstr('.wav', i.fname)) & isempty(findstr('.WAV', i.fname)) dawn@0: disp(['WARNING: matlab function WAVREAD requires '... dawn@0: '.wav suffix on file name: ' i.fname]); dawn@0: end dawn@0: sz = wavread(i.fname, 'size'); dawn@0: i.nsamples=sz(1); dawn@0: i.nchans=sz(2); dawn@0: i.totalsamples = i.nsamples*i.nchans; dawn@0: [dummy, i.sr, i.samplebits] = wavread(i.fname, 1); dawn@0: return; dawn@0: end dawn@0: if strcmp(i.format, 'MACSND') dawn@0: % must load the data to get info - this is stupid dawn@0: if ~isempty(findstr(':', i.fname)) dawn@0: disp(['matlab function READSND cannot handle an '... dawn@0: 'indirect path: ' i.fname]); dawn@0: end dawn@0: if 3==exist('readsnd') dawn@0: [data, i.sr] = eval('readsnd(i.fname)'); dawn@0: else dawn@0: error('cannot read MACSND on this platform'); dawn@0: end dawn@0: i.nsamples = size(data,2); dawn@0: i.nchans = size(data,1); dawn@0: i.totalsamples = i.nsamples*i.nchans; dawn@0: return dawn@0: end dawn@0: dawn@0: % reopen file dawn@0: if strcmp(i.format, 'ascii') ... dawn@0: | strcmp(i.format, 'csv') | strcmp(i.format, '|WAVE') dawn@0: [i.fd, msg] = fopen(i.fname, 'rt'); dawn@0: else dawn@0: [i.fd, msg] = fopen(i.fname, 'r', 'ieee-be.l64'); dawn@0: end dawn@0: if i.fd == -1 dawn@0: if isempty(msg) dawn@0: error(['could not open: ', i.fname]); dawn@0: else dawn@0: error(msg) dawn@0: end dawn@0: end dawn@0: fd = i.fd; dawn@0: if ~isfield(i, 'nbytes') dawn@0: if (-1 == fseek(i.fd, 0, 1)) ; error ('fseek failed'); end; dawn@0: i.nbytes = ftell(i.fd); dawn@0: end dawn@0: fseek(fd, 0, -1); % rewind dawn@0: dawn@0: dawn@0: switch i.format dawn@0: %%%%%%%%%%%%%%%%%%% AIFF %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% dawn@0: case {'AIFF','AIFC'} dawn@0: fseek(fd, 12, 0); % skip container chunk dawn@0: % skip over spurious chunks dawn@0: idx=ftell(fd); dawn@0: while 1 dawn@0: magic=char(fread(fd,4,'uchar'))'; dawn@0: if strcmp(magic,'COMM'); break; end; dawn@0: idx = idx+1; dawn@0: status = fseek(fd,idx,-1); dawn@0: if status == -1 dawn@0: error('expected COMM magic word, found eof'); dawn@0: end; dawn@0: end; dawn@0: dawn@0: %ckSize=fread(fd,1,'int32'); dawn@0: %status = fseek(fd, ckSize, 0); % skip to end of chunk dawn@0: %if status == -1 dawn@0: % error('unexpected eof'); dawn@0: %end dawn@0: dawn@0: %while (1) dawn@0: % magic = char(fread(fd, 4, 'char'))'; dawn@0: % if ~strcmp(magic, 'SSND') dawn@0: % fseek(fd, -4, 0); % skip back dawn@0: % break; dawn@0: % end dawn@0: % ckSize = fread(fd, 1, 'long'); dawn@0: % fseek(fd, ckSize, 0); % skip to end of sound chunk dawn@0: %end dawn@0: %magic = char(fread (fd, 4, 'char'))'; dawn@0: %if ~strcmp(magic, 'COMM'); error(['expected COMM, found ', magic]) ; end dawn@0: commsz = fread(fd, 1, 'int32'); dawn@0: i.nchans = fread(fd, 1, 'int16'); dawn@0: i.nsamples = fread(fd, 1, 'uint32'); dawn@0: i.totalsamples = i.nsamples*i.nchans; dawn@0: i.samplebits = fread(fd, 1, 'int16'); dawn@0: switch i.samplebits dawn@0: case 16 dawn@0: i.sample_bytes = 2; dawn@0: i.sample_type = 'int16'; dawn@0: case 32 dawn@0: i.sample_bytes = 4; dawn@0: i.sample_type = 'int32'; % or float? dawn@0: otherwise dawn@0: error(['unexpected samplebits: ' num2str(i.samplebits) ]); dawn@0: end dawn@0: % read sampling rate using Hideki Kawahara's code: dawn@0: srex1=fread(fd,1,'uint16'); dawn@0: srex2=fread(fd,1,'uint64'); dawn@0: if strcmp(char(i.format),'AIFC') dawn@0: compress=fread(fd,4,'uchar'); dawn@0: if ~strcmp(char(compress),'NONE') dawn@0: error('Compression is not supported.'); dawn@0: end; dawn@0: fseek(fd, commsz-22, 0); dawn@0: end; dawn@0: i.sr = 2^(srex1-16383)*srex2/hex2dec('8000000000000000'); dawn@0: %fseek(fd, 12, -1); % skip back to end of container chunk dawn@0: % skip over eventual common chunk dawn@0: %while(1) dawn@0: % magic = char(fread(fd, 4, 'char'))'; dawn@0: % if ~strcmp(magic, 'COMM') dawn@0: % fseek(fd, -4, 0); % skip back dawn@0: % break; dawn@0: % end dawn@0: % ckSize = fread(fd, 1, 'long'); dawn@0: % fseek(fd, ckSize, 0); % skip over chunk dawn@0: %end dawn@0: magic=char(fread(fd,4,'uchar'))'; dawn@0: while ~strcmp(char(magic),'SSND') dawn@0: [ckSize, count]=fread(fd,1,'int32'); dawn@0: if ~count; dawn@0: error('expected chunk size field, found eof'); dawn@0: return dawn@0: end dawn@0: status = fseek(fd, ckSize, 0); % skip to end of chunk dawn@0: if status == -1 dawn@0: error('expected SSND magic word, found eof'); dawn@0: return dawn@0: end; dawn@0: magic=char(fread(fd,4,'uchar'))'; dawn@0: end; dawn@0: dawn@0: %magic = char(fread(fd, 4, 'char'))'; dawn@0: %if ~strcmp(magic, 'SSND') dawn@0: % error (['expected SSND, found' magic]); dawn@0: %end dawn@0: fseek(fd, 12, 0); % skip over ckSize, offset and blocksize fields dawn@0: i.bytes_to_data = ftell(fd); dawn@0: if i.totalsamples*i.sample_bytes ~= i.nbytes-i.bytes_to_data dawn@0: disp(['WARNING: header fields sample_bytes: ' ... dawn@0: num2str(i.sample_bytes)]); dawn@0: disp (['and sample and channel count: ' num2str(i.nsamples) ... dawn@0: ', ' num2str(i.nchans)]); dawn@0: disp(['are inconsistent with offset to data: ' ... dawn@0: num2str(i.bytes_to_data) ' and file size: ' ... dawn@0: num2str(i.nbytes)]); dawn@0: disp (['(' num2str(i.totalsamples*i.sample_bytes) ... dawn@0: ' ~= ' num2str(i.nbytes-i.bytes_to_data) ')']); dawn@0: end dawn@0: dawn@0: return dawn@0: %%%%%%%%%%%%%%%%%%% NIST %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% dawn@0: case 'NIST' dawn@0: % fseek(fd, 0, -1); dawn@0: % line = fscanf(fd, '%s' , 1); dawn@0: % if ~strcmp(line, 'NIST_1A'); error(['expected NIST_1A, found ', magic]); end dawn@0: fseek(fd, 8, 0); % skip over magic string dawn@0: i.bytes_to_data = fscanf(fd, '%d', 1); dawn@0: while (1) dawn@0: key = fscanf(fd, '%s', 1); dawn@0: if strcmp(key, 'end_head'); break; end; dawn@0: % read third field according to spec in second field (type) dawn@0: % this may need refining... dawn@0: type = fscanf(fd, '%s', 1); dawn@0: if strcmp(type(1:2), '-s') dawn@0: bytes_to_read = sscanf(type(3:end), '%d', 1); dawn@0: fseek(fd, 1, 0); % skip blank dawn@0: value = char(fread(fd, bytes_to_read, 'char'))'; dawn@0: else dawn@0: value = fscanf(fd, '%f', 1); dawn@0: end dawn@0: i = setfield(i, key, value); dawn@0: end dawn@0: % give standard names to useful fields dawn@0: if isfield(i, 'channel_count'); i.nchans = i.channel_count; end dawn@0: if isfield(i, 'sample_count'); i.nsamples = i.sample_count; end dawn@0: i.totalsamples = i.nsamples*i.nchans; dawn@0: if isfield(i, 'sample_rate'); dawn@0: i.sr = i.sample_rate; dawn@0: i.xunits = 's'; dawn@0: end dawn@0: if ~isfield(i, 'sample_coding'); i.sample_coding = 'pcm'; end dawn@0: i.bytes_to_data = 1024; % needs checking dawn@0: i.sample_bytes=2; dawn@0: i.sample_type='int16'; dawn@0: return dawn@0: %%%%%%%%%%%%%%%%%%% |WAVE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% dawn@0: case '|WAV' dawn@0: line = fscanf(fd, '%s' , 1); % skip first line dawn@0: i.nsamples = fscanf(fd, '%d', 1); dawn@0: i.nchans = fscanf(fd, '%d', 1); dawn@0: i.totalsamples = i.nsamples*i.nchans; dawn@0: i.bytes_to_data = ftell(fd); dawn@0: % channel info handled during data read dawn@0: i.totalsamples = i.nsamples * i.nchans; dawn@0: return dawn@0: %%%%%%%%%%%%%%%%%%% WFF %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% dawn@0: case 'wff' dawn@0: fseek(fd, 4, 0); % skip magic number dawn@0: i.version = fread(fd, 1, 'long'); dawn@0: i.type_code = fread(fd, 1, 'long'); dawn@0: i.nchans = fread(fd, 1, 'long'); dawn@0: i.info.channel_flags = fread(fd, 1, 'long'); dawn@0: i.bytes_to_data = fread(fd, 1, 'long'); dawn@0: fseek(fd, 40, 0); dawn@0: i.gen_prog_name = fread(fd, 32, 'char'); dawn@0: i.comment = fread(fd, 32, 'char'); dawn@0: i.sample_bytes = 2; dawn@0: i.sample_type = 'int16'; dawn@0: return; dawn@0: % channel info handled later during channel read dawn@0: %%%%%%%%%%%%%%%%%%% ESPS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% dawn@0: case 'ESPS' dawn@0: % Based on Peter Kabal's afsp package. dawn@0: % This handles at least one kind of ESPS waveform file. Others? dawn@0: i.machine_code = fread(fd, 1, 'uint'); dawn@0: i.version_check_code = fread(fd, 1, 'uint'); dawn@0: i.bytes_to_data = fread(fd, 1, 'uint'); dawn@0: i.record_size = fread(fd, 1, 'uint'); dawn@0: fseek(fd, 20, -1); dawn@0: i.EDR_ESPS_flag = fread(fd, 1, 'uint'); dawn@0: i.align_pad_size = fread(fd, 1, 'uint'); dawn@0: fseek(fd, 32, -1); dawn@0: i.file_type = fread(fd, 1, 'uint16'); dawn@0: fseek(fd, 40, -1); dawn@0: i.file_creation_date_time = char(fread(fd, 26, 'char'))'; dawn@0: i.header_version = char(fread(fd, 8, 'char'))'; dawn@0: i.program_name = char(fread(fd, 16, 'char'))'; dawn@0: i.program_version = char(fread(fd, 8, 'char'))'; dawn@0: i.compile_date = char(fread(fd, 26, 'char'))'; dawn@0: i.tag = fread(fd, 1, 'uint'); dawn@0: fseek(fd, 132, -1); dawn@0: i.ndoubles = fread(fd, 1, 'uint'); dawn@0: i.nfloats = fread(fd, 1, 'uint'); dawn@0: i.nlongs = fread(fd, 1, 'uint'); dawn@0: i.nshorts = fread(fd, 1, 'uint'); dawn@0: i.nchars = fread(fd, 1, 'uint'); dawn@0: i.fixed_header_size = fread(fd, 1, 'uint'); dawn@0: i.var_header_size = fread(fd, 1, 'uint'); dawn@0: %w.dunno_what = fread(fd, 1, 'uint'); dawn@0: fseek(fd, 160, -1); dawn@0: i.user = char(fread(fd, 8, 'char'))'; dawn@0: % scan the rest of the header to find sampling rate dawn@0: a = ftell(fd); dawn@0: bytes_left_in_header = i.bytes_to_data - ftell(fd); dawn@0: hunk = char(fread(fd, bytes_left_in_header, 'uchar'))'; dawn@0: b = findstr(hunk, 'record_freq'); dawn@0: %fseek(fd, a+b-1, -1); dawn@0: %w.xxxx = char(fread(fd, 12, 'char'))'; dawn@0: %w.count = fread(fd, 1, 'uint'); dawn@0: %w.data_code = fread(fd, 1, 'ushort'); dawn@0: fseek(fd, a+b-1+12+2+4, -1); dawn@0: i.sr = fread(fd, 1, 'double'); dawn@0: i.nchans = i.ndoubles+i.nfloats+i.nlongs ... dawn@0: +i.nshorts+i.nchars; dawn@0: recordsize = i.ndoubles*8 + i.nfloats*4 + i.nlongs*4 ... dawn@0: +i.nshorts*2+i.nchars; dawn@0: i.nsamples = (i.nbytes - i.bytes_to_data) / recordsize; dawn@0: i.totalsamples = i.nsamples * i.nchans; dawn@0: i.sample_bytes = 2; % bug dawn@0: i.sample_type = 'int16'; % bug dawn@0: return; dawn@0: %%%%%%%%%%%%% PLAIN ASCII %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% dawn@0: case 'ascii' dawn@0: if isfield(i, 'bytes_to_data') dawn@0: fseek(fd, i.bytes_to_data, 0); dawn@0: else dawn@0: i.bytes_to_data = 0; dawn@0: end dawn@0: line = fgetl(fd); dawn@0: i.nchans = size(sscanf(line, '%f'), 1); dawn@0: nlines = 1; dawn@0: while (1) dawn@0: line = fgets(i.fd); dawn@0: if isa(line, 'double') & -1 == line; break; end dawn@0: nlines = nlines+1; dawn@0: end dawn@0: i.nsamples = nlines; dawn@0: i.totalsamples = i.nsamples*i.nchans; dawn@0: i.sr=1; dawn@0: return dawn@0: %%%%%%%%%%%%% COMMA-SEPARATED VALUES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% dawn@0: case 'csv'; dawn@0: if isfield(i, 'bytes_to_data') dawn@0: fseek(fd, i.bytes_to_data, 0); dawn@0: else dawn@0: i.bytes_to_data = 0; dawn@0: end dawn@0: % todo dawn@0: return dawn@0: %%%%%%%%%%%%% Binary %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% dawn@0: case {'uchar'}; % should take care of signed/unsigned dawn@0: i.sample_bytes = 1; dawn@0: i.sample_type = 'uchar'; dawn@0: if isfield(i, 'bytes_to_data') dawn@0: fseek(fd, i.bytes_to_data, 0); dawn@0: else dawn@0: i.bytes_to_data = 0; dawn@0: end dawn@0: if ~isfield(i, 'nchans') dawn@0: i.nchans = 1; dawn@0: end dawn@0: i.nsamples = (i.nbytes-i.bytes_to_data)/i.sample_bytes; dawn@0: i.totalsamples = i.nsamples * i.nchans; dawn@0: i.sr=1; dawn@0: return dawn@0: case {'short', 'int16'}; dawn@0: i.sample_bytes = 2; dawn@0: i.sample_type = 'int16'; dawn@0: if isfield(i, 'bytes_to_data') dawn@0: fseek(fd, i.bytes_to_data, 0); dawn@0: else dawn@0: i.bytes_to_data = 0; dawn@0: end dawn@0: if ~isfield(i, 'nchans') dawn@0: i.nchans = 1; dawn@0: end dawn@0: i.nsamples = (i.nbytes-i.bytes_to_data)/i.sample_bytes; dawn@0: i.totalsamples = i.nsamples * i.nchans; dawn@0: i.sr=1; dawn@0: return dawn@0: case {'long', 'int32'}; dawn@0: i.sample_bytes = 4; dawn@0: i.sample_type = 'int32'; dawn@0: if isfield(i, 'bytes_to_data') dawn@0: fseek(fd, i.bytes_to_data, 0); dawn@0: else dawn@0: i.bytes_to_data = 0; dawn@0: end dawn@0: if ~isfield(i, 'nchans') dawn@0: i.nchans = 1; dawn@0: end dawn@0: i.nsamples = (i.nbytes-i.bytes_to_data)/i.sample_bytes; dawn@0: i.totalsamples = i.nsamples * i.nchans; dawn@0: i.sr=1; dawn@0: return dawn@0: case {'float', 'float32'}; dawn@0: i.sample_bytes = 4; dawn@0: i.sample_type = 'float32'; dawn@0: if isfield(i, 'bytes_to_data') dawn@0: fseek(fd, i.bytes_to_data, 0); dawn@0: else dawn@0: i.bytes_to_data = 0; dawn@0: end dawn@0: if ~isfield(i, 'nchans') dawn@0: i.nchans = 1; dawn@0: end dawn@0: i.nsamples = (i.nbytes-i.bytes_to_data)/i.sample_bytes; dawn@0: i.totalsamples = i.nsamples * i.nchans; dawn@0: i.sr=1; dawn@0: return dawn@0: case {'double', 'float64'}; dawn@0: i.sample_bytes = 8; dawn@0: i.sample_type = 'float64'; dawn@0: if isfield(i, 'bytes_to_data') dawn@0: fseek(fd, i.bytes_to_data, 0); dawn@0: else dawn@0: i.bytes_to_data = 0; dawn@0: end dawn@0: if ~isfield(i, 'nchans') dawn@0: i.nchans = 1; dawn@0: end dawn@0: i.nsamples = (i.nbytes-i.bytes_to_data)/i.sample_bytes; dawn@0: i.totalsamples = i.nsamples * i.nchans; dawn@0: i.sr=1; dawn@0: return dawn@0: otherwise dawn@0: error(['unknown format: >' i.format '<']); dawn@0: end