SFS Manual for Programmers

4. Standards

Any program that uses the SFS subroutine library to access speech data files gains a number of benefits over programs that access files using primitive system calls directly: simplicity of code, reliability, portability across machine architectures and data format changes. But the benefits of using a standard library of routines for speech processing programs need not end there. There are substantial benefits to the user of programs if their operation can be made consistent and coherent.

In this section we shall look at some of the programming and program standards established for SFS speech processing programs. We shall look at program documentation, at command-line switches, at operational messages and error reporting and source control.

4.1

Manual Page

The program manual page is the most important piece of program documentation. You should not write any program without also writing a manual page. Although it may be possible to run your program without reading the manual page, experience has shown that programs without manual pages are not used.

It is easy for the text of manual pages to get lost or to get out-of-date. Thus it is recommended that you include the manual page text in the source file for the main part of your program. The SFS convention is that the manual page text is prefixed by a line:

/**MAN

and ends with a line:

*/

(Note that this means that you cannot use the string '*/' in the text of the manual page, so use '*\/' instead) The text of the manual page can then be extracted by a simple utility program (sman(UCL1)) or by a call to awk(1) and then sent via nroff(1) using the manual page macros to the screen or printer.

The text of the manual page is written using the manual page macros of nroff(1), see man(5). The general layout of the manual page text is:

Title
Name
Synopsis
Description
Options
Input Items
Output Items
History
Files
Version/Author
See Also
Bugs

A prototypical layout is available in $SFSBASE/src/man.src

The Title section consists of the macro

.TH <program name> <manual section> <site> SFS

e.g.

.TH SPECTRAN UCL1 UCL SFS

SFS and SPARBASE use the following named manual sections:

SFS1  SFS utility programs
SFS3  SFS library routines
SFS5  SFS file formats
SFS8  SFS management programs
UCL1  UCL speech analysis programs
UCL3  UCL speech analysis subroutines
IC1   IC speech analysis programs
IC3   IC speech analysis subroutines
GEC1  GEC speech analysis programs
GEC3  GEC speech analysis subroutines
LDS1  Leeds speech analysis programs
LDS3  Leeds speech analysis subroutines

The Name section consists of a subheading and text

.SH NAME
<program name> - <title> 

e.g.

.SH NAME
spectran - Fourier analysis of speech waveform

The Synopsis section gives the syntax for command line options:

.SH SYNOPSIS
.B <program name>
<options> <file(s)>

e.g.

.SH SYNOPSIS
.B spectran
(-i item) (-b bandwidth|-w windowsize) file

Where switches or files are optional, enclose them in parentheses. Where switches are mutually exclusive, use the '|' to indicate OR.

The Description section consists of a sequence of paragraphs (separated by the macro '.PP') giving an overview of the operation of the program, e.g.

.SH DESCRIPTION
The program
.I spectran
converts overlapping windows of the speech signal to a vector of 
energies using the Fast Fourier Transform (FFT). The size of 
the window on the signal is specified by options. The output 
is always 128 energies from 0Hz to half the sampling frequency. 
A Hamming window and pre-emphasis is always applied.

(Note the use of the macros '.B' and '.I' in the previous two examples to give bold and underlined text). The format of the Options section consists of a header and a sequence of option descriptions:

.SH OPTIONS
.TP
.B <switch>
switch text.
.TP
.BI <switch> <value>
switch text

where the '.TP' macro introduces a temporary paragraph, and the '.B' or '.BI' macros introduce as 'hanging indents' the names (and values) of options. For example:

.SH OPTIONS
.TP
.B -I
Identify program and version.
.TP
.BI -w windowsize
Select analysis window size in milliseconds. Default 10ms.

Be sure to identify default values for optional switches that take values.

The Input Items section should describe the range of allowed input items to the program. This is done by identifying the major data type, plus any restrictions, e.g.

.SH INPUT ITEMS
.IP SP
Any speech item sampled at 10, 12.8, 16 or 20kHz.

the '.IP' macro is an alternative way of creating an indented paragraph.

The Output Items and History sections are only required for data processing programs that produce new items in the file. The first section describes the major type of item produced, the second details the fields used in the processing history of the item. For example

.SH OUTPUT ITEMS
.IP CO
128 energies from 0Hz to half sampling frequency.
.SH HISTORY
.TP
.B window=
size of selected analysis window in milliseconds.

The optional Files section may be used to name any subsidiary files that the program uses to operate. Be sure to indicate whether the pathnames are absolute, relative or based on some environment variable:

.SH FILES
.IP $SFSBASE/data/phon.dic
Default phonetic dictionary.

The Version/Author section should be kept up to date with the latest version number of the program and the last author to make amendments.

.SH VERSION/AUTHOR
.IP 1.0
Mark Huckvale
.IP 1.1
Mike Johnson

The optional See Also section can be used to list associated programs, or programs that perform similar functions:

.SH SEE ALSO
tx(UCL1), HQtx(UCL1), vtx(UCL1)

The optional Bugs section can be used to warn of temporary or permanent restrictions in operation of the program:

.SH BUGS
Maximum window size is 1024 samples.

Aside from the manual page, you should also be careful to document the code to a minimum standard. A good practice is to comment the use of each variable as it is declared (including 'parameter' variables in routines), to comment the operation of each routine as it is defined, and to comment each '#define' macro. Keep the number of variables to each routine as small as possible, and break up routines that are longer than one page of code into separate areas. Minimise the use of global variables.

4.2

Option decoding

To standardise the command line interface, all SFS programs decode their argument list using the routine getopt(3). This enforces consistency over how switches take arguments and how switches can be combined.

By convention, all SFS programs accept the flag '-I' which causes the program to print out its name and version number and then to exit.

When programs accept alternative data sets as input, they should also provide standard item number decoding using the '-i item' convention. The routine itspec(SFS3) is provided to take the argument of a '-i' switch and to return two variables:

int itemtype; /* datatype number of selected data set */
char* itemmatch; /* history match for selected data set */

The 'itemtype' and 'itemmatch' variables are designed to be used directly in calls to sfsitem(SFS3) and getitem(SFS3) to locate datasets.

Where illegal switches are used, the routine 'getopt()' prints a warning message automatically. In this case, and also when the program is executed with no arguments at all, the program should print a 'usage' line indicating the acceptable program switches and arguments. Thus prototypical option decoding code (available in $SFSBASE/src/opt.src) is:

#define PROGNAME "ex"
#define PROGVERS "1.0"
/* : */
main (argc,argv)
int argc;
char* argv[];
{
  extern int optind; /* option index */
  extern char* optarg; /* option argument */
  int c; /* option letter */
  int errflg=0; /* option error */

  int it; /* item data type */
  char* itype="0"; /* item sub-type, default=last */

  while ((c=getopt(argc,argv,"Ii:")) != EOF) switch (c) {
    case 'I': /* Identify */
      fprintf(stderr,"%s: Example Program V%s\n",
        PROGNAME,PROGVERS);
      exit(0);
      break;
    case 'i': /* input item selection */
      if (itspec(optarg,&it,&ty) == 0) {
        if (it==SP_TYPE)
          itype=ty;
        else
          error("unsuitable item selection: '%s'",optarg);
      }
      else
         error("illegal item selection: '%s'",optarg);
      break;
    default: /* error */
      errflg++;
      break;
  }
  if (errflg || (argc < 2))
    error("usage: %s (-I) (-i item) file",PROGNAME);

Where error(SFS3) is a standard routine that passes its arguments to fprintf() and exits with an error code, see section 4.3.

Once the program options on the command line have been processed, any remaining arguments are likely to be files. The SFS convention is that the last argument contains the destination file for any processing. In most cases the destination file is also the source file for the data to be processed, and only one filename argument is accepted. However copy(SFS1), link(SFS1) and dp(UCL1) are examples of programs that accept 2 or more files.

In most cases, the name of the SFS file typed by the user should be searched for along a path of data file directories specified by the environment variable 'SFSPATH'. The routine sfsfile(SFS3) is provided to do this. You should provide a variable of suitable length to copy the resulting filename out of the static area inside sfsfile, e.g.:

char filename[SFSMAXFILENAME];
/* : */
if (optind < argc)
  strcpy(filename,sfsfile(argv[optind]));
else
  error("no data file specified");

There are cases when it is not advisable to use sfsfile: one is when a new file is being created. The filenames of new files should be used without a data path search so that existing files are not overwritten accidentally.

4.3

Operational messages

The most common action of SFS programs when errors are encountered is to print a warning message and to stop. The routine error(SFS3) is provided to perform this function. The arguments to error() are supplied in a similar manner as for printf(), namely a format string and a number of values. error() prints the formatted text on the standard error preceded by the program name. The program name is obtained from the global variable 'progname' which should be initialised in your program:

#define PROGNAME "ex"
char *progname=PROGNAME;

The routine error also performs tidying-up operations if any temporary files have been created using sfschannel(SFS3).

When a program is operating, it is sometimes necessary to inform the operator of how much of the processing has been completed. In general it is preferable to keep operational messages to a minimum, as these are more likely to confuse users than to help. Since speech processing programs are often placed as 'background' tasks, it is also preferable that they do not bombard the user with messages that would interfere with the foreground task.

The routine ttytest(SFS3) is provided to check whether informational messages should be sent to the user. ttytest() returns 1 if the standard output channel is connected directly to the users terminal and when the task is operating in the foreground. It returns 0 otherwise. Thus the following piece of code:

/* inform user of progress */
if (ttytest()) {
  printf("\rFrame %d/%d",currframe,numframes);
  fflush(stdout);
}

will print the current progress on a single line, overwriting itself. The messages will stop if the user places the task in the background, and restart once the task is brought back to the foreground. No messages will be printed if the output of the program is re-directed in any way, or if the program runs as a batch task.

4.4

Source Control

It is very convenient when developing a program to divide the program source into many small modules and place the source of these in separate files. It is very inconvenient when copying the source of a completed program to a new site to have the source in many unrelated pieces. Wherever possible, once development work has completed, create a single source file for your program. Keep your own copy in separate modules by all means.

When a program must occupy more than one file create a subdirectory for the source that contains only the appropriate files and a 'Makefile' for the program. This subdirectory should be named '<progname>.src'. Thus the sources for the program spectran(UCL1), which are in both 'C' and FORTRAN, are stored in a directory 'spectran.src'.

A program is 'released' to the outside world by placing its source or its source directory in a commonly accessible sub-directory of '/usr/sfs'. Be sure to modify the major Makefile for the release directory to compile the new program, and to check the compilation.

Next Section


© 2000 Mark Huckvale University College London