# v_readsfs

## PURPOSE

V_READSFS Read a .SFS format sound file [Y,FS,HD,FFX]=(FF,TY,SUB,MODE,NMAX,NSKIP,XPATH)

## SYNOPSIS

function [y,fs,hd,ffx]=v_readsfs(ff,ty,sub,mode,nmax,nskip,xpath)

## DESCRIPTION

    V_READSFS  Read a .SFS format sound file [Y,FS,HD,FFX]=(FF,TY,SUB,MODE,NMAX,NSKIP,XPATH)

     Usage:  [s,fs]=v_readsfs(filename,1); % read the last speech item in a file

     Input Parameters:

      FF gives the name of the file or alternatively
                     can be the ffx output from a previous call to v_readsfs
      TY gives the type of data item required e.g.:
                  0 Main header, 1 Speech data, 2 Laryngograph, 5 Annotation
      SUB specifies the instance of type TY in the file: 0 for first (default), -1 for last or else
          it can specify the start of the processing history field as a string (e.g. 'hqtx')
      MODE        specifies the following (*=default):

               File I/O:    'f'    Do not close file on exit
                            'd'    Look in data directory: v_voicebox('dir_data')
               Int Format:  'i'  Force integer data to be at least 16 bits
                                  (some sfs files have a header error which falsely indicates 8-bit data)
               Create item: 'c' Create item if necessary
               Errors:      'x' Ignore errors

      NMAX     maximum number of samples to read (or -1 for unlimited [default])
      NSKIP    number of samples or frames to skip from start of file
                   (or -1 to continue from previous read when FFX is given instead of a filename [default])
      XPATH    (used with 'c' option) gives the full name of the program needed to generate the data or
               the directory containing it.

     Output Parameters:

      Y        data matrix or cell matrix whose format depends on TY:
            TY=0: empty
                TY=5: cell array {nf,3} = {position length annotation}
            TY=1,2: column vector containing data
            TY=11: data array with one row per frame
      FS       sample frequency in Hz
      HD     cell matrix whose format depends on TY:
            TY=0: cell{14,1}
                  {1} row vector
                    {1}(1) = serial_date (see DATENUM() for format)
                    {1}(2) = file_number
                    {1}(3) = machine_type
                  {2} = File type (= 'UC2')
                  {3} = username of creator
                  {4} = site of creator
                  {5} = source
                  {6} = database
                  {7} = speaker name
                  {8} = session code
                  {9} = session date (as a string)
                 {10} = name of token
                 {11} = token repetition code
                 {12} = recording conditions
                 {13} = archiving details
                 {14} = general comments
            TY>0: cell{4,1}
                  {1} = (1,14) array:
                     {1}(1)  = processdate (see DATENUM() for format)
                     {1}(2)  = datatype: 1=speech, 2=lx, 3=tx cycle lengths, 4=fx freq
                                         5=annotations, 6=phonetic, 7=synthesiser, 8=words
                                         9=grey-level, 10=voicing, 11=energy, 12=formants
                                         13=energy, 14=lpc, 15=markov, 16=acoustic, 17=?,
                                         18=geometry, 19=aerodynamics, 20=articulatory
                                         21=source, 22=physiological, 23=rational filter
                                         24=poles/zeros, 25=glottal flow, 26=excitation model
                                         27=nose, 28=calibration, 29=area
                     {1}(3)  = subtype
                     {1}(4)  = floating: 1=float, 0=int, -1=structure+
                     {1}(5)  = datasize in bytes
                     {1}(6)  = framesize in units of datasize
                     {1}(7)  = numframes
                     {1}(8)  = length in bytes of data
                     {1}(9)  = frameduration=1/sample_rate
                     {1}(10) = datapresent: 0=deleted, 1=present, 2=link
                     {1}(11) = time offset
                     {1}(12) = windowsize
                     {1}(13) = overlap
                     {1}(14) = lxsync
                  {2} = processing history
                  {3} = parameter field
                  {4} = comment

      FFX     cell array containing:
                  {1} = filename
                  {2} = (1,4) = [fid byte_order item_row values_read]
                  {3} = (nitem,5) = one row per item [type subtype length position byteorder]
                                    with deleted items having a negative type and zero length
                  {4} = {nitem,3} = cell: one row per item {processing parameters comment} text strings

     The SFS (Speech Filing System) is a package mostly written by Mark Huckvale
     and is available for UNIX and PC systems from
     http://www.phon.ucl.ac.uk/resource/sfs/

## CROSS-REFERENCE INFORMATION

This function calls:

- v_readsfs V_READSFS Read a .SFS format sound file [Y,FS,HD,FFX]=(FF,TY,SUB,MODE,NMAX,NSKIP,XPATH)
- v_sprintsi V_SPRINTSI Print X with SI multiplier S=(X,D,W,U)
- v_voicebox V_VOICEBOX set global parameters for Voicebox functions Y=(FIELD,VAL)
- v_zerotrim V_ZEROTRIM Remove zero trailing rows and columns Z=(X)

This function is called by:

- v_readsfs V_READSFS Read a .SFS format sound file [Y,FS,HD,FFX]=(FF,TY,SUB,MODE,NMAX,NSKIP,XPATH)

## SOURCE CODE

    0001 function [y,fs,hd,ffx]=v_readsfs(ff,ty,sub,mode,nmax,nskip,xpath)
    0002 %V_READSFS  Read a .SFS format sound file [Y,FS,HD,FFX]=(FF,TY,SUB,MODE,NMAX,NSKIP,XPATH)
    0003 %
    0004 % Usage:  [s,fs]=v_readsfs(filename,1); % read the last speech item in a file
    0005 %
    0006 % Input Parameters:
    0007 %
    0008 %  FF gives the name of the file or alternatively
    0009 %                 can be the ffx output from a previous call to v_readsfs
    0010 %  TY gives the type of data item required e.g.:
    0011 %              0 Main header, 1 Speech data, 2 Laryngograph, 5 Annotation
    0012 %  SUB specifies the instance of type TY in the file: 0 for first (default), -1 for last or else
    0013 %      it can specify the start of the processing history field as a string (e.g. 'hqtx')
    0014 %  MODE        specifies the following (*=default):
    0015 %
    0016 %           File I/O:    'f'    Do not close file on exit
    0017 %                        'd'    Look in data directory: v_voicebox('dir_data')
    0018 %           Int Format:  'i'  Force integer data to be at least 16 bits
    0019 %                              (some sfs files have a header error which falsely indicates 8-bit data)
    0020 %           Create item: 'c' Create item if necessary
    0021 %           Errors:      'x' Ignore errors
    0022 %
    0023 %  NMAX     maximum number of samples to read (or -1 for unlimited [default])
    0024 %  NSKIP    number of samples or frames to skip from start of file
    0025 %               (or -1 to continue from previous read when FFX is given instead of a filename [default])
    0026 %  XPATH    (used with 'c' option) gives the full name of the program needed to generate the data or
    0027 %           the directory containing it.
    0028 %
    0029 % Output Parameters:
    0030 %
    0031 %  Y        data matrix or cell matrix whose format depends on TY:
    0032 %        TY=0: empty
    0033 %            TY=5: cell array {nf,3} = {position length annotation}
    0034 %        TY=1,2: column vector containing data
    0035 %        TY=11: data array with one row per frame
    0036 %  FS       sample frequency in Hz
    0037 %  HD     cell matrix whose format depends on TY:
    0038 %        TY=0: cell{14,1}
    0039 %              {1} row vector
    0040 %                {1}(1) = serial_date (see DATENUM() for format)
    0041 %                {1}(2) = file_number
    0042 %                {1}(3) = machine_type
    0043 %              {2} = File type (= 'UC2')
    0044 %              {3} = username of creator
    0045 %              {4} = site of creator
    0046 %              {5} = source
    0047 %              {6} = database
    0048 %              {7} = speaker name
    0049 %              {8} = session code
    0050 %              {9} = session date (as a string)
    0051 %             {10} = name of token
    0052 %             {11} = token repetition code
    0053 %             {12} = recording conditions
    0054 %             {13} = archiving details
    0055 %             {14} = general comments
    0056 %        TY>0: cell{4,1}
    0057 %              {1} = (1,14) array:
    0058 %                 {1}(1)  = processdate (see DATENUM() for format)
    0059 %                 {1}(2)  = datatype: 1=speech, 2=lx, 3=tx cycle lengths, 4=fx freq
    0060 %                                     5=annotations, 6=phonetic, 7=synthesiser, 8=words
    0061 %                                     9=grey-level, 10=voicing, 11=energy, 12=formants
    0062 %                                     13=energy, 14=lpc, 15=markov, 16=acoustic, 17=?,
    0063 %                                     18=geometry, 19=aerodynamics, 20=articulatory
    0064 %                                     21=source, 22=physiological, 23=rational filter
    0065 %                                     24=poles/zeros, 25=glottal flow, 26=excitation model
    0066 %                                     27=nose, 28=calibration, 29=area
    0067 %                 {1}(3)  = subtype
    0068 %                 {1}(4)  = floating: 1=float, 0=int, -1=structure+
    0069 %                 {1}(5)  = datasize in bytes
    0070 %                 {1}(6)  = framesize in units of datasize
    0071 %                 {1}(7)  = numframes
    0072 %                 {1}(8)  = length in bytes of data
    0073 %                 {1}(9)  = frameduration=1/sample_rate
    0074 %                 {1}(10) = datapresent: 0=deleted, 1=present, 2=link
    0075 %                 {1}(11) = time offset
    0076 %                 {1}(12) = windowsize
    0077 %                 {1}(13) = overlap
    0078 %                 {1}(14) = lxsync
    0079 %              {2} = processing history
    0080 %              {3} = parameter field
    0081 %              {4} = comment
    0082 %
    0083 %  FFX     cell array containing:
    0084 %              {1} = filename
    0085 %              {2} = (1,4) = [fid byte_order item_row values_read]
    0086 %              {3} = (nitem,5) = one row per item [type subtype length position byteorder]
    0087 %                                with deleted items having a negative type and zero length
    0088 %              {4} = {nitem,3} = cell: one row per item {processing parameters comment} text strings
    0089 %
    0090 %
    0091 % The SFS (Speech Filing System) is a package mostly written by Mark Huckvale
    0092 % and is available for UNIX and PC systems from
    0093 % http://www.phon.ucl.ac.uk/resource/sfs/
    0094
    0095 % Features yet to be implemented:
    0096 %
    0097 %   (1) If no output parameters are specified, header information will be printed.
    0098 %   (2) following link items
    0099 %   (3) MODE options:
    0100 %                Scaling: 's'    Auto scale to make data peak = +-1
    0101 %                         'r'    Raw unscaled data (integer values)
    0102 %                         'q'    Scaled to make 0dBm0 be unity mean square
    0103 %                         'p' *    Scaled to make +-1 equal full scale
    0104 %                Errors   'r'    Return if file is non-existant
    0105
    0106
    0107 %       Copyright (C) Mike Brookes 1998
    0108 %      Version: $Id: v_readsfs.m 10865 2018-09-21 17:22:45Z dmb $
    0109 %
    0110 %   VOICEBOX is a MATLAB toolbox for speech processing.
    0111 %   Home page: http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html
    0112 %
    0113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0114 %   This program is free software; you can redistribute it and/or modify
    0115 %   it under the terms of the GNU Lesser General Public License as published by
    0116 %   the Free Software Foundation; either version 3 of the License, or
    0117 %   (at your option) any later version.
    0118 %
    0119 %   This program is distributed in the hope that it will be useful,
    0120 %   but WITHOUT ANY WARRANTY; without even the implied warranty of
    0121 %   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    0122 %   GNU Lesser General Public License for more details.
    0123 %
    0124 %   You can obtain a copy of the GNU Lesser General Public License from
    0125 %   https://www.gnu.org/licenses/ .
    0126 %
    0127 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0128
    0129 if nargin<7
    0130     xpath=v_voicebox('sfsbin');       % path for sfs programs
    0131 end
    0132 EXESUF=v_voicebox('sfssuffix');                                              % suffix for executable O/S dependent
    0133 if nargin<4 || ~numel(mode)
    0134     mode='p';
    0135 else
    0136     mode = [mode(:).' 'p'];
    0137 end
    0138 if nargout==0
    0139     if nargin<2 || ~numel(ty) || ty<=0
    0140         [yy,fs,hd,ffx]=v_readsfs(ff,0,0,mode);
    0141         fprintf(1,'File: %s\n',ffx{1});
    0142         fprintf(1,'Database: %s, Speaker: %s, Date: %s, Token: %s\n',hd{6},hd{7},hd{9},hd{10});
    0143         lst=ffx{3};
    0144         for it=2:size(lst,1);
    0145             [yy,fs,hd,ffx]=v_readsfs(ffx,lst(it,1),lst(it,2),mode,0);
    0146             nf=hd{1}(7);
    0147             fd=hd{1}(9);
    0148             fprintf(1,'%3d.%02d %ss @ %sHz (%d frames): %s\n',lst(it,1),lst(it,2),v_sprintsi(nf*fd),v_sprintsi(1/fd),nf,ffx{4}{it,1});
    0149         end
    0150     end
    0151 else
    0152     it=[];
    0153     xfid=[];                  % xfid will be non-empty second time around
    0154     while (isempty(it))                 % may go round this loop twice
    0155         if ischar(ff)           % If ff is a string we must read file
    0156             if any(mode=='d')
    0157                 ff=fullfile(v_voicebox('dir_data'),ff);
    0158             end
    0159             fid=fopen(ff,'rb','b');
    0160             if fid == -1 error('Can''t open %s for input',ff); end
    0161
    0162             t=fread(fid,512,'uchar').';
    0163             if (t(1:3)~='UC2')
    0164                 error(sprintf('%s is not an SFS file type UC2',ff))
    0165             end
    0166             itemlist = [0 1 0 0 t(512)];
    0167             proglist={};
    0168             for i=2:200
    0169                 pos = ftell(fid);
    0170                 [t,n]=fread(fid,512,'uchar');
    0171                 if (n < 512) break; end
    0172                 mm=pow2(1,8*([0 1 2 3]+(t(512)==0)*[3 1 -1 -3]));
    0173                 itemlist(i,:)=[mm*[t(389:392) t(393:396) t(413:416)] pos t(512)];
    0174                 if itemlist(i,1)>=2^31
    0175                     itemlist(i,1)=itemlist(i,1)-2^32;
    0176                 end
    0177                 if abs(itemlist(i,1))>29
    0178                     if any(mode=='x')
    0179                         itemlist(i,:)=[];
    0180                         break;
    0181                     else
    0182                         error('%s: %d is not a valid SFS item type',ff,itemlist(i,1));
    0183                     end
    0184                 end
    0185                 proglist{i,1}=char(zerotrim(t(1:256)'));
    0186                 proglist{i,2}=char(zerotrim(t(257:384)'));
    0187                 proglist{i,3}=char(zerotrim(t(437:456)'));
    0188                 seekstat=fseek(fid,itemlist(i,3),'cof');
    0189                 if seekstat
    0190                     if any(mode=='x')
    0191                         itemlist(i,:)=[];
    0192                         break;
    0193                     else
    0194                         error('%s: Unexpected end of file',ff);
    0195                     end
    0196                 end
    0197             end
    0198             ffx={ff; [fid 0 0 0]; itemlist; proglist};
    0199         else
    0200             ffx=ff;
    0201             ff=ffx{1};
    0202             fid=ffx{2}(1);
    0203             if fid<0
    0204                 fid=fopen(ffx{1},'rb',char('b'+(ffx{2}(2)~=0)*('l'-'b')));
    0205             end
    0206         end
    0207
    0208         % now try to find the requested item
    0209
    0210         list=ffx{3};
    0211         if nargin<2 ty=0; end
    0212         if nargin<3 sub=0; end
    0213         if ty<0 ty=list(1,1); end
    0214         if ischar(sub)
    0215             lsub=length(sub);
    0216             proglist=ffx{4};
    0217
    0218             for itt=size(proglist,1):-1:2
    0219                 if list(itt,1)==ty & length(proglist{itt,1})>=lsub
    0220                     if strcmpi(sub,proglist{itt,1}(1:lsub))
    0221                         it=itt;
    0222                     end
    0223                 end
    0224             end
    0225             if (isempty(it))
    0226                 if any(mode=='c') & isempty(xfid)      % try to create item if we haven't tried before
    0227                     xfid=-1;
    0228                     if nargin>=7
    0229                         xname=xpath;
    0230                         xfid=fopen(xname);
    0231                     end
    0232                     if xfid<0
    0233                         if any('/\'==xpath(end))        % would be better to use fullfile()
    0234                             xname=[xpath sub EXESUF];
    0235                         else
    0236                             xname=[xpath '/' sub EXESUF];
    0237                         end
    0238                         xfid=fopen(xname);
    0239                     end
    0240                     if xfid<0
    0241                         error(sprintf('Cannot find executable program %s',sub));
    0242                     else
    0243                         fclose(xfid);
    0244                         fclose(fid); % close this file
    0245                         doscom=['cmd /c "' xname '" ' ffx{1}];
    0246                         %fprintf(1,'Executing: %s\n',doscom);
    0247                         if dos(doscom) % run the program
    0248                             error(sprintf('Error running DOS command: %s',doscom));
    0249                         end
    0250                         ff=ffx{1};          % force reread of header information
    0251                     end
    0252                 else
    0253                     error(sprintf('Cannot find item %d.%s in file: %s',ty,sub,ff));
    0254                 end
    0255             end
    0256         else % numeric subitem specification
    0257             if sub>0
    0258                 it = find(list(:,1)==ty & list(:,2)==sub);
    0259             elseif sub==0
    0260                 it = min(find(list(:,1)==ty));
    0261             else
    0262                 it = max(find(list(:,1)==ty));
    0263             end
    0264             if (isempty(it))
    0265                 error(sprintf('Cannot find item %d.%d in file: %s',ty,sub,ff));
    0266             end
    0267         end
    0268     end % loop up to two times while (isempty(it))
    0269     lit=list(it,:);
    0270     if ffx{2}(3)~=it
    0271         ffx{2}(3)=it;
    0272         ffx{2}(4)=0;
    0273     end
    0274
    0275     % read the selected item with the correct byte order
    0276
    0277     if lit(5)~=ffx{2}(2)
    0278         fclose(fid);
    0279         fid=fopen(ffx{1},'rb',char('b'+(lit(5)~=0)*('l'-'b')));
    0280         ffx{2}(1:2)=[fid lit(5)];
    0281         if fid == -1 error(sprintf('Can''t open %s for input',ff)); end
    0282     end
    0283     fseek(fid,lit(4),'bof');
    0284
    0285
    0286     y=[];
    0287     fs=0;
    0288     if ~lit(1)                              % read main header
    0289         mb=fread(fid,512,'uchar').';
    0290         if nargout>2
    0291             mc=[1 4; 9 28; 29 32; 37 56; 57 76; 77 96; 97 116; 117 136; 137 296; 297 304; 305 312; 313 332; 333 412];
    0292             hd=cell(14,1);
    0293             hd{1}=[pow2(1,8*([2 3 0 1]+(mb(512)==0)*[1 -1 1 -1]))*[mb(5:8); mb(33:36)].' mb(512)];
    0294             hd{1}(1)=hd{1}(1)/86400+719529;  % convert date format
    0295             for i=1:13
    0296                 hd{i+1}=char(v_zerotrim(mb(mc(i,1):mc(i,2))));
    0297             end
    0298         end
    0299     else
    0300
    0301         % read the item header
    0302
    0303         hd=cell(4,1);
    0304         hdr=zeros(1,14);
    0305
    0306         [str,n]=fread(fid,256,'uchar');
    0307         if (n<256) error(sprintf('Error reading item %d.%d in file: %s',ty,sub,file)); end
    0308         hd{2}=char(zerotrim(str'));
    0309
    0310         [str,n]=fread(fid,128,'uchar');
    0311         if (n<128) error(sprintf('Error reading item %d.%d in file: %s',ty,sub,file)); end
    0312         hd{3}=char(zerotrim(str'));
    0313
    0314         hdr(1:8)=fread(fid,8,'long');
    0315         hdr(9)=fread(fid,1,'double');
    0316         if hdr(9) fs=1/hdr(9); end
    0317         hdr(10)=fread(fid,1,'long');
    0318         hdr(11)=fread(fid,1,'double');
    0319
    0320         [str,n]=fread(fid,20,'uchar');
    0321         if (n<20) error(sprintf('Error reading item %d.%d in file: %s',ty,sub,file)); end
    0322         hd{4}=char(zerotrim(str'));
    0323
    0324         [hdr(12:14),n]=fread(fid,3,'long');
    0325         if (n<3) error(sprintf('Error reading item %d.%d in file: %s',ty,sub,file)); end
    0326         fseek(fid,44,'cof');
    0327         hd{1}=hdr;
    0328         hd{1}(1)=hd{1}(1)/86400+719529;  % convert date format
    0329
    0330         % now read the actual data
    0331
    0332         if nargin<6 nskip=ffx{2}(4);
    0333         elseif nskip<0 nskip=ffx{2}(4);
    0334         end
    0335
    0336         ksamples=hdr(7)-nskip;
    0337         if nargin>4
    0338             if nmax>=0
    0339                 ksamples=min(nmax,ksamples);
    0340             end
    0341         end
    0342
    0343         if ksamples>0
    0344             ffx{2}(4)=nskip+ksamples;
    0345             fsz=hdr(6);
    0346             if(hdr(10)==1)        % data present
    0347                 if(hdr(4)>=0)        % non-structured
    0348                     ds=hdr(5);
    0349                     if(hdr(4)>0)
    0350                         if(ds==4) fmt='float';
    0351                         elseif (ds==8) fmt='double';
    0352                         else error('error in sfs file'); end
    0353                     else
    0354                         if(ds==1 & all(mode~='i')) fmt='uchar';
    0355                         elseif(ds<=2) fmt='short'; fsz=ceil(fsz*ds/2);
    0356                         elseif(ds==4) fmt='long';
    0357                         else error('error in sfs file'); end
    0358                     end
    0359                     fseek(fid,lit(4)+512+nskip*fsz*ds,'bof');
    0360                     nd=fsz*ksamples;
    0361                     [y,n]=fread(fid,nd,fmt);
    0362                     if (n<nd) error(sprintf('Error reading item %d.%d in file: %s',ty,sub,file)); end
    0363                     y = reshape(y,fsz,ksamples)';
    0364                 else
    0365                     if (hdr(2)==5)
    0366                         y = cell(ksamples,3);
    0367                         for ifr=1:nskip
    0368                             lf=fread(fid,1,'uchar');
    0369                             fseek(fid,lf,'cof');
    0370                         end
    0371
    0372                         for ifr=1:ksamples
    0373                             lf=fread(fid,1,'uchar');
    0374                             tdat=fread(fid,2,'long');
    0375                             y(ifr,:)={tdat(1) tdat(2) char(fread(fid,lf-9,'uchar').')};
    0376                             lf=fread(fid,1,'uchar');
    0377                         end
    0378                     else
    0379                         error(sprintf('Cannot convert item %d.%d in file: %s',ty,sub,file));
    0380                     end
    0381                 end
    0382             end
    0383         end
    0384     end
    0385     if all(mode~='f') fclose(fid); ffx{2}(1)=-1; end
    0386 end
    0387
    0388
    0389
    0390
    0391
    0392