The following functions return a pointer which is used in a similar way to how a file pointer (FILE *) that is returned from fopen() is used with the C library file routines such as fgets() or fscanf(). To open an SSFF file for read or write access, one of the following functions must be used:
struct SSFF_Header *SSFF_Read_Open( char *File, int Num_Recs ) |
This opens a file for read access, regardless of the format of the file (as long as it is in a format the the SSFF library understands). The argument File contains the name of the file to be opened. Num_Recs is an integer which tells the routine the maximum number of records that will be read at any one time.
If the file does not exist, or is not in an understandable format, a NULL value is returned by this function. Otherwise, a valid pointer to an SSFF_Header data structure is returned. This pointer must be passed to the other SSFF routines, so that they know which file they are being asked to work on.
struct SSFF_Header *SSFF_Write_Open( char *File,
char **Var_List,
char **Col_List,
int Num_Recs,
int Format ) |
This function opens a file for write access. Num_Recs is the maximum number of records which will be written at one time. For most purposes, this will be set to one. Format controls the output format and can be one of two values: FORMAT_SSFF or FORMAT_ESPS. File is the name of the file to be opened. If the opening is successful, then a valid pointer is returned. If not, this function returns NULL.
Var_List is a NULL terminated list of strings, detailing the variables to be stored in the header of the file. Each `variable', also known as a generic, has a type and a value. Although each variable has a type, in practice the value is represented by a string.
There are three variables which must be present always. These are Machine, Record_Freq and Start_Time. No type information is needed with these variables, as they are predefined Machine is always of type CHAR, and Start_Time and Record_Freq are of type FLOAT.
Currently the possible values for Machine are SPARC, VAX or IBM-PC. To add new machines the source file machine.c must be ammended to ensure that data is converted correctly for the new machine.
Consider a valid definition of an array to be passed as a Var_List argument:
static char *Var_List[] =
{ "Machine SPARC",
"Record_Freq 100",
"Start_Time 10.45",
"Comment CHAR Created by Andrew",
"Max_Value SHORT 32768",
NULL /; |
Note that the macro THIS_MACHINE can be used in place of "Machine SPARC" in the above. This also has the advantage of being portable, as it is defined correctly for the machine that the program is being compiled on. The Record_Freq variable is in Hertz (samples per second) and Start_Time is in seconds. All variables listed after the first three obligatory ones are generics, and must be associated with a type.
Do not forget the NULL at the end of the array. This signifies the end of the list to the SSFF routines, and if omitted will probably cause the program to crash.
The Col_List argument is a NULL terminated array of strings which indicate the data fields that will be present, and also their dimension and type. Consider a definition of an array that, when passed as the argument Col_List, sets up three fields of dimension 1, 1 and 5:
static char *Col_List[] =
{ "Column Data SHORT 1",
"Column rms FLOAT 1",
"Column Labels CHAR 5",
NULL /; |
The Data field can store 1 SHORT per record, which translates to a storage of 2 bytes. rms can store a FLOAT (4 bytes) and Labels can store 5 characters. Note that if a string is to be stored, the terminator ('0') must be taken into account when working out the dimension of the character array.
Both the Col_List and the Var_List arguments must be valid for the function to work correctly.
The following functions all assume that a file has been already opened using one of the two functions above. A pointer to a SSFF_Header structure should now be available containing all the information about the storage of the generics and the records.
char *SSFF_Locate_Field( struct SSFF_Header *Hdr, char *Name,
int *Type, int *Width, int *Size ) |
This function is used to check for the presence of a data field in the header of the file identified by Hdr, and to get access to the memory which has been allocated for the storage of the field in the record(s). The Name argument is set to a string indicating which field is being requested. If the function finds the field, then it returns a pointer to the storage allocated for that element in the record. If not present, NULL is returned.
If the function is successful, the three values pointed to by the three last arguments are set to reflect the type of the field (one of the SSFF_ set in ssff.h e.g. SSFF_CHAR), the width or dimension, and the size of each individual element of that field in bytes. Any or all of these arguments can be set to NULL if the information is not required.
char *SSFF_Locate_Generic( struct SSFF_Header *Hdr,
char *Name ) |
This routine is used to check for the presence of a generic variable stored in the header Hdr. If it is not there, NULL is returned. If it is there, a pointer to the data string is returned. See the programming examples for further information on how to interpret the return data.
int SSFF_Write( struct SSFF_Header *Hdr, int Num_Recs ) |
Writes out Num_Recs records to the file denoted by the header pointer Hdr. Num_Recs must be less than or equal to the value specified in SSFF_Write_Open() and may vary from call to call. The number returned indicates how many records were actually written to the file. A number returned which is smaller than Num_Recs probably indicates that disk space has run out.
int SSFF_Read( struct SSFF_Header *Hdr, int Num_Recs ) |
Read Num_Recs records from the file into the record buffer. The value of Num_Recs may vary from call to call, but must be less than or equal to the value which was initially passed to SSFF_Read_Open().
The number returned indicates how many records were actually read. If this number is less than Num_Recs, then the end-of-file has been encountered.
int SSFF_Seek( SSFF_Header *Hdr, int Rec_Num ) |
This function provides the ability to randomly access records in a file. Rec_Num is the number of the record that the programmer wishes to access. The first data record has a record number of 0, then next is 1 and so on. The return value is TRUE if the operation is successful, and FALSE if it is not.
SSFF_Close( struct SSFF_Header *Hdr ) |
Closes the file associated with Hdr and frees all memory allocated for the record storage buffer.
The header structure pointer that is returned from either SSFF_Write_Open() or SSFF_Read_Open() can be used to access the information stored inside the header structure. The definition of struct SSFF_Header has four fields of interest. These are:
struct SSFF_Generic *Vars;
float Start_Time, Record_Freq;
struct SSFF_Column *Cols; |
Thus, the start time and the recording frequency of a file can be obtained given a pointer to the header structure. Vars provides a link to the start of the generic variable linked list. The structure used to store these variables is:
struct SSFF_Generic
{
TSSFF_Generic
*Next;
int Type; /* data is held in string form,
and converted by the user program */
/* the data */
char
Name[NAME_SIZE],
Data[DATA_SIZE];
}; |
Note that the Next field of the last generic item is NULL. This provides a way of traversing the list and knowing when the end is reached. Data contains the string representing the value of the generic. This must be interpreted if a numeric or other value is desired. For example, use the C library function atof() if a floating point value is needed.
The Cols field is a link to the start of the field list. This list contains information about the name, type and dimension of each field of the data. The structure used to store each field is:
struct SSFF_Column
{
TSSFF_Column
*Next;
/* column info */
char
Name[NAME_SIZE];
int
Type,
Size,
Width;
/* where the data is kept */
char
*Data;
#ifdef USE_ESPS
char
*ESPS_Data;
#endif
};
|
Note that the Next link is NULL for the last field in the list. If necessary, a program can traverse this list looking for information about the fields of the data file. For example, a program that prints the names of each field in the file.
Note that using the header pointer directly is to be discouraged, except for reading Record_Freq and Start_Time. Only access the fields and generics directly when the information you require is not available via the interface routines. Never modify any of the fields --- consider them read only!
The header file ssff_formats.h defines field lists for many popular signal file formats. This is where the field list definition for new standard signal types should be defined. Please consult the file for details of each format.
Several example are presented in order to answer questions about the use of the C interface. The first program opens an SSFF file to store formant parameters, and writes out some example formant values.
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ssff_formats.h>
#define NUM_RECS 1 /* number of records to write each time */
void main()
{
static char *Var_List[] =
{ THIS_MACHINE,
"Record_Freq 100",
"Start_Time 0.23",
NULL /;
struct SSFF_Header *Hdr =
SSFF_Write_Open( "test.SSFF_form", Var_List, SSFF_Formant_Cols,
NUM_RECS, FORMAT_SSFF );
float *Formants;
int loop, iloop, Width;
/* stop if file was not opened! */
assert( Hdr != NULL );
/* get the data pointer to the record buffer for the formants */
Formants = (float *) Locate_Field( Hdr, "Formant", NULL, NULL, &Width );
/* make sure that we have found the field
-- unlikely to be a problem as we have just defined the header! */
assert( Formants != NULL );
/* loop around 10 times, writing out a record each time */
for (loop = 0; loop < 10; loop++)
{
/* set the values to something */
for (iloop = 0; iloop < Width; iloop++)
Formants[iloop] = 100.0 * loop; /* dummy value */
if (SSFF_Write( Hdr, NUM_RECS ) != NUM_RECS)
break; /* out of disk space? */
/
SSFF_Close( Hdr );
} |
The next program prints out the pitch values from a pre-existing SSFF file named test.SSFF_pitch:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ssff_formats.h>
#define NUM_RECS 1 /* number of records to read each time */
void main()
{
struct SSFF_Header *Hdr = SSFF_Read_Open( "test.SSFF_pitch", NUM_RECS );
float *Pitch;
char *Max_Pitch;
/* stop if file was not opened! */
assert( Hdr != NULL );
/* print info about the sampling rate and start time */
printf("sample rate =
printf("start time =
/* look for a generic variable called Max_Pitch */
/* if there, print it out, otherwise give a message */
Max_Pitch = SSFF_Locate_Generic( Hdr, "Max_Pitch" );
if (Max_Pitch == NULL)
printf( "Max_Pitch generic not found...\n\n" );
else
printf( "Max_Pitch value =
/* get the data pointer to the record buffer for the formants */
Pitch = (float *) Locate_Field( Hdr, "Pitch", NULL, NULL, NULL );
/* if not found, try looking for an F0 column */
if (Pitch == NULL)
Pitch = (float *) Locate_Field( Hdr, "F0", NULL, NULL, NULL );
/* make sure that we have found the field
-- it won't be there if the file doesn't have pitch data in */
assert( Pitch != NULL );
/* loop around printing the data, until no more data is available */
while (SSFF_Read( Hdr, NUM_RECS ) > 0)
printf("Value =
SSFF_Close( Hdr );
} |
Several points need to be discussed:
The program which reads pitch data can read both SSFF and ESPS formats (if the ESPS libraries are available on that platform). It can also read the pitch field from any such file, even if there are other fields in the file. For example, it will read pitch data equally well from an SSFF_Synth_Cols file which stores synthesiser data and an SSFF_Pitch_Cols file which stores only F0 data.
Notice that in the reading program, if the Pitch column is not found, then the F0 column is looked for.
The SSFF_Header structure was queried directly to provide information relating to the recording frequency and the start time of the signal.
The value of the generic Max_Pitch variable, that the reading program looks for, is returned as a string and must be interpreted if a numeric result is desired.
The program that writes data uses a predefined format: SSFF_Formant_Cols. If a standard format exists for the data that needs to be stored, use it. Only create another format if absolutely necessary.