📄 foo1
字号:
The type-specific portion of the header is provided by a union _h_d that contains pointers to each possible type-specific structure. They are declared in a union so that additional pointers can be added in the future without affecting existing data files (since the size of the pointers themselves are the same). Programmers can tell which of these pointers to use by checking the item head->common.type. The pointer names are given in the following table: _____________________________________________ _|V_a_l_u_e__o_f____c__o__m__m__o__n__.__t__y__p__e___P_o_i_n_t_e_r__i_n____h__d_t_o__u_s_e__| |FT_SD _h_d_._s_d | |FT_SPEC _h_d_._s_p_e_c | |FT_FILT _h_d_._f_i_l_t | |FT_SCBK _h_d_._s_c_b_k | |FT_FEA _h_d_._f_e_a | _|____________________________________________| For example, the sampling frequency from the header of an (old-style) SD file is head->hd.sd->sf. 5 .1 .1 . NIST (Sphere) Headers ESPS programs can read sample data files containing the NIST Shpere header (such as found on the TIMIT CD-ROM database). This is imple- mented by having the ESPS file header access routines, detect an input file containing a NIST header and build an ESPS FEA_SD header from it. All header information in the NIST header is saved as generic header items in the ESPS header. Version 3.4 ERL 9/11/91 ETM-S-86-13:rap/jtb page 7 5 .1 .2 . Foreign Headers It is often desirable to maintain an existing file header even if a file is converted to ESPS. The non-ESPS header is called a foreign header and it can be stored in the file between the ESPS header and the first data record. Foreign headers are kept in the ESPS header in the following manner: The generic header item _f_o_r_e_i_g_n__h_d__l_e_n_g_t_h contains the size (in bytes) of the foreign header. If this item is present (and non-zero), _r_e_a_d__h_e_a_d_e_r() will read this many additional bytes of data, put it into the header, and set an additional generic header item _f_o_r_e_i_g_n__h_d__p_t_r to point to it. From that point on, the foreign header is just part of the ESPS header. With this mechanism, it is possible to write programs that use the ESPS header and record I/O functions while still having access to the foreign header. The procedure is simple: use _r_e_a_d__h_e_a_d_e_r() to read the ESPS header, and _g_e_t__g_e_n_h_d() to get the pointer to the foreign header. The conversion programs _b_t_o_s_p_s and _a_d_d_f_e_a_h_d would usually be used to create such files. See reference [7]. 5 .2 . Header Access Routines There are several ESPS library routines that support access to the ESPS data file headers. These are described in detail in Section 3 of [1]. Declarations for all these functions are found in <_e_s_p_s/_h_e_a_d_e_r._h>; the programmer need not declare them if this file is included. The _r_e_a_d__h_e_a_d_e_r routine attempts to read a file header from a stream (the fd parameter is typically one from a "fopen" call). If an I/O error occurs _r_e_a_d__h_e_a_d_e_r returns NULL. If the file does not begin with a valid ESPS header, then _r_e_a_d__h_e_a_d_e_r will determine if the file begins with a NIST header. If a NIST header is present, then a FEA_SD header is generated and filled in with the appropriate values from the NIST header (sample rate, minimum and maximum sample values, data type, etc). _R_e_a_d__h_e_a_d_e_r then returns a pointer to this FEA_SD header in the usual way. Programs which called _r_e_a_d__h_e_a_d_e_r on a NIST file will operate normally and not know that the file wasn't initially a normal ESPS file. If neither a valid ESPS header, or a NIST header in present, then _r_e_a_d__h_e_a_d_e_r will check for the _u_n_i_x environment variable DEF_HEADER. If it is defined and points to a file containg a valid ESPS header, then that header is used instead. If all of the above attempts to find a header fail, then NULL is returned. Memory for the file header is allocated from dynamic memory. After _r_e_a_d__h_e_a_d_e_r returns, the program should consult _c_o_m_m_o_n._t_y_p_e to determine the type of the header that has been returned. The _w_r_i_t_e__h_e_a_d_e_r routine attempts to write a file header onto a stream. It computes and fills in values for hsize, fixsiz, check, Version 3.4 ERL 9/11/91 ETM-S-86-13:rap/jtb page 8 date, and hdvers and ensures that must-be-zero fields contain zero, by clearing them. If an I/O error occurs, _w_r_i_t_e__h_e_a_d_e_r writes an error message and the program bombs. The _n_e_w__h_e_a_d_e_r routine allocates a new header from dynamic memory and returns a pointer to it. Which type-specific header is allocated depends on the value of the argument _t_y_p_e. If _t_y_p_e is zero, then no type-specific header is allocated, only the common part is. This case is intended for use by the header access programs. The _c_o_p_y__h_e_a_d_e_r routine accepts a pointer to a file header and returns a copy of the same type in which all items except common.comment, common.prog, common.vers, common.date, common.progdate, and common.hdvers are copied from the source header. This routine par- tially generates a header for an output file, given a header for an input file (any changed parameters must be filled in, along with the program name and version). It is important to realize that this func- tion can only be used when the output file is of the same type as the input file. The _a_d_d__s_o_u_r_c_e__f_i_l_e routine inserts a source file name and header into the next available positions in a header. The nnames and nheads fields are incremented. Strictly speaking, only pointers are copied, so the source parameters must not be altered before _w_r_i_t_e__h_e_a_d_e_r is called. The _a_d_d__c_o_m_m_e_n_t routine appends a character string onto the comment field of a header. For example, with the help of _g_e_t__c_m_d__l_i_n_e (3-ESPS), the command line is added as a comment. All ESPS programs by convention add the command line as a comment in the output header. For creating _z_f_u_n_c structures, the function _n_e_w__z_f_u_n_c is supplied. It allocates dynamic memory, builds a new zfunc structure, and returns a pointer to it. The zeros and poles are also copied to dynamic memory. To assist in storing _z_f_u_n_cs as generic header items the functions _a_d_d__g_e_n_z_f_u_n_c and _g_e_t__g_e_n_z_f_u_n_c are provided. Generic header items are added to an existing header by the _a_d_d__g_e_n_h_d_X (3-ESPS) routines ("X" stands for one of six possible data types). Other generic-related header access routines are _g_e_n_h_d__l_i_s_t, which returns a list of the defined generic header items, _g_e_n_h_d__t_y_p_e, which returns the type of a specific generic header item, and _g_e_t__g_e_n_h_d, which returns a pointer to a specific generic header item. These routines make it possible for programs to process the generic header items in ESPS files without having to know what they are in advance. 5 .2 .1 . Using the Header Access Routines The header access routines are easy to use. The important thing to remember is that, before _w_r_i_t_e__h_e_a_d_e_r is called, _a_l_l _v_a_l_u_e_s _m_u_s_t _b_e _s_e_t _i_n _t_h_e _h_e_a_d_e_r. The easiest error is to confuse pointers that might be in use pointing to several different headers, most often the input and the output file. The basic model of use is to open the Version 3.4 ERL 9/11/91 ETM-S-86-13:rap/jtb page 9 input file and call _r_e_a_d__h_e_a_d_e_r on this file. This allocates memory for the input file header, checks the header on the input file, and reads to its end. The next read on this file will return data, rather than header, values. If _r_e_a_d__h_e_a_d_e_r returns without error, then it has found a valid ESPS header. The program should now check _c_o_m_m_o_n._t_y_p_e to be sure that the file is of the type expected by the program. If it is not, then an error message should be printed and the program should exit with a non-zero exit code (standard convention is to use exit(1)). Some programs may accept several (or any) of the valid ESPS file types. These programs should still consult _c_o_m_m_o_n._t_y_p_e to determine which pointer in the union _h_d to use. The use of the wrong pointer will cause unpredictable results (most often a harmless bus error, but worse could happen). Since _r_e_a_d__h_e_a_d_e_r returns a pointer to the main header structure, a pointer must be declared before _r_e_a_d__h_e_a_d_e_r is called: struct header *h; . . . h = read_header(file); After the call to _r_e_a_d__h_e_a_d_e_r, items in the header may be accessed. Here are some examples: char *ref_file; float frequency, avg_zero; . . . if (h->common.type == FT_SD) { /*verify file type*/ fprintf(stderr, "Input file is not an SD file.0); exit(1) } ref_file = h->variable.refer; /*get name of reference file for tags*/ frequency = h->hd.sd.sf; /*sampling frequency of data*/ avg_zero = *(float *) get_genhd("zero_crossing", h); In some ESPS programs (especially newer programs), the call to _r_e_a_d__h_e_a_d_e_r and the file type checking is done within a library func- tion _e_o_p_e_n, which also opens the file. An example of this is given later. If the output file of the program is the same type as the input file then _c_o_p_y__h_e_a_d_e_r is useful for creating the header of the new output file and filling in most values from the input file. Some of these values might have to be changed before _w_r_i_t_e__h_e_a_d_e_r is called, but in many cases, the header of an output file is much like that of its input file. If the output file is of a different type than the input Version 3.4 ERL 9/11/91 ETM-S-86-13:rap/jtb page 10 file, or the programmer decides that none or very few values will be in common, then _n_e_w__h_e_a_d_e_r should be used to create the header for the new output file. A type code must be specified when _n_e_w__h_e_a_d_e_r is called. In either case, when creating an output ESPS file from source ESPS files, the source headers should be saved in the new output header. This is done by calling _a_d_d__s_o_u_r_c_e__f_i_l_e. Here is an example of reading values from the header of an input file, and creating and writing an output file header. char *speaker_name; float samp_freq, zero_crossing, *coh_thresh; long num_samples; . . . ih = read_header(in_file); /* get input header */ speaker_name = (char *) get_genhd("speaker", ih); /*generic example*/ . . . oh = new_header(FT_FEA) /* create new output header */ (void) add_source_file(oh,in_file,ih); /* save input header */ oh->variable.refer = ih->variable.refer; /*same refer file*/ /*add the command line as a comment*/ add_comment(oh, get_cmd_line(argc, argv)); (void) strcpy (oh->common.prog, progname); (void) strcpy (oh->common.vers, Version); /*add generic header items*/ (void)add_genhd_f("avg. zero crossing", &zero_crossing, 1, ih) coh_thresh = add_genhd_d("voicing threshold", (double *)NULL, 1, ih); . . . zero_crossing = . . .; *coh_thresh = . . .; (void) write_header(oh, ostrm); /*ostrm is output file stream*/ The call to _a_d_d__s_o_u_r_c_e__f_i_l_e results in the input header being stored on the new output file. This is done (within _a_d_d__s_o_u_r_c_e__f_i_l_e ) by setting a pointer (_v_a_r_i_a_b_l_e._s_r_c_h_e_a_d) to point to the old header. There is a limit to the number of headers that can be embedded in this way. It is defined as MAX_SOURCES and its current value can be found in <_e_s_p_s/_h_e_a_d_e_r._h>. The header item _v_a_r_i_a_b_l_e._n_h_e_a_d_s should be checked before calling _a_d_d__s_o_u_r_c_e__f_i_l_e if the programmer wants to avoid the chance of a run-time error because of exceeding this limit. Of course, if the header is being stored in a new header and only once in the program, then it is safe to store a header without checking. This is the case in the above example. The example above includes both "styles" of adding generic header items - the _a_d_d__g_e_n_h_d_X routines can either allocate space for the item and return a pointer to it, or accept a pointer to existing Version 3.4 ERL 9/11/91 ETM-S-86-13:rap/jtb page 11 space. Note that the actual values written out to the header are the values that exist when _w_r_i_t_e__h_e_a_d_e_r is called, _n_o_t the values that exist when the _a_d_d__g_e_n_h_d_X routines are called. Thus, in the above example, the two lines prior to the _w_r_i_t_e__h_e_a_d_e_r call determine the values written to the corresponding generic header items. There is, however, a "Hi-C" way to guarantee that the value written to the header is fixed at the time of the call to _a_d_d__g_e_n_h_d_X routine, as follows: *add_genhd_f("avg. zero crossing", NULL, 1, ih) = zero_crossing; When writing programs that exploit the generic header items in the input ESPS files, it may be the case that the presence of those items is useful but not essential. In such cases, care should be taken allow the program to run on ESPS files that do not have the required gereric header items. This can be accomplished by using _g_e_n_h_d__l_i_s_t, or by checking for a NULL return value from _g_e_t__g_e_n_h_d. 6 . Data Structures The data structures for data records in the different types of ESPS files are specified in the Section 5 manual pages that describe the file formats. The general approach is to define a C structure that represents one data record in the file. Access functions are avail- able to support reading and writing such records, so that programmers can deal with the C structure and ignore the issue of how that struc- ture is stored in the file. 6 .1 . Data Access Routines In general a function named _g_e_t_xx__r_e_c and one named _p_u_t_xx__r_e_c are provided to get and put records from/onto an ESPS file type xx. There is also n_a_l_l_o_xx__r_e_c function that allocates memory for one record. In most cases, the size of certain elements of the record, depend on values in the file header. For example, here is an example of reading a SPEC record: FILE *ifile;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -