| |
Files provide long term, high capacity data storage that is essential
to all large scale computational undertakings. Since file input/output
(file I/O) is necessarily a cornerstone of all ISIP software, the ISIP
Foundation Classes
(IFC's) require a standardized, efficient, and easy to use file I/O
interface. Without such an interface, tremendous amounts of time would
be wasted rewriting code to parse files for individual
applications. ISIP's solution to this is the
Signal Object File
(Sof) class. This tutorial gives an overview of the Sof class as well as of
four other IFC's involved in file I/O.
Signal Object File (Sof) class
Sophisticated file formats are an essential part of speech research
since we often augment raw speech data with auxiliary information such
as the recording conditions, annotations, etc. Even more importantly,
as the native file I/O format for all ISIP classes Sof files provide
a standardized way for all IFC's to write themselves to, and read
themselves from, files. The standard interface makes file parsing for
new applications simple and straightforward to write.
An Sof file is nothing more than an index, which provides information
about the location of each object stored in the file, and the
corresponding object data. Sof transparently supports two basic
storage formats: text (useful for building human readable files such
as parameter files) and binary (useful for sampled data). Sof also
transparently converts binary data between different architectures by
performing the appropriate byte transformations as needed. This
prevents users from having to concern themselves with the low level
details.
Example 1:
@ Sof v1.0 @
@ Long 0 @
value = 13;
@ Long 1 @
value = 27;
@ Long 32 @
value = -2812;
|
An example of a very simple Sof file is shown to the right.
The first line is the Sof file header. For a text file, it consists
solely of the keyword Sof and a version number wrapped in the
user-specified delimiter character. Whatever delimiter character is
specified in the header will be used throughout the file.
The third line of the file is an example of an object header.
An object header has two components: a class name and an integral
tag. The class name must be a single word - no spaces are allowed. The
tag following the class name
provides an ability to have multiple instances of the same object
in a file.
Every object written to an Sof file can be uniquely addressed by
a (name, tag) pair.
The first data in the file is an instance of the
Long
Scalar
class. The data space for (Long,0) begins immediately on the
line following the object header and ends at the second newline
character. This extra line of space is not necessary, but helps to
make the file more readable. Sof itself does not deal at all with this
data space, it simply maintains the index of pointers and positions
the file pointer to assist the higher level classes to read and write
themselves from disk.
Example 2:
Holding to the signal processing model of programming, one can easily
imagine a case where every instance of some object (say speech
signals) is operated on in turn. This can be simplified for example to
a program that reads in and prints to the screen all long integers
found in that file. The core parts of such a routine are as follows:
01 // file: $isip/doc/examples/class/io/io_example_00/example.cc
02 // version: $Id: index.html,v 1.5 2006/06/30 22:40:01 ewt16 Exp $
03 //
04
05 // isip include files
06 //
07 #include <File.h>
08 #include <Sof.h>
09 #include <Long.h>
10 #include <Console.h>
11
12 // main program starts here:
13 // this program reads long integer entries from a text Sof file and prints
14 // each one found
15 //
16 int main(int argc, const char **argv) {
17
18 // declare an Sof file object
19 //
20 Sof sof1;
21
22 // open a file in read only mode:
23 // note that the Sof object determines whether the input file is text or
24 // binary automatically. in this example, it happens to be text.
25 //
26 String filename(L"./file.sof");
27 sof1.open(filename, File::READ_ONLY);
28
29 // declare a Long object used to read from the Sof file
30 //
31 Long j;
32
33 // loop through all Long objects in the file, starting with the first
34 // and ending when we have visited all objects with the given name
35 // note that the sof1 object is looking up the object based on its name
36 // which, in this case, is "Long". one could uniquely determine each Long
37 // object in the file by assigning each a different name and using that
38 // name to read in the object rather than the default name.
39 //
40 long tag = sof1.first(j.name());
41 while (tag != Sof::NO_TAG) {
42
43 // have the object read itself:
44 // this calls the Long::read method. each object in the math library
45 // and above knows how to read itself from an Sof file
46 //
47 j.read(sof1, tag, j.name());
48
49 // output the object to the console
50 //
51 String output;
52 output.assign(j);
53 output.insert(L"I found the value " , 0);
54 Console::put(output);
55
56 // go to the next object
57 //
58 tag = sof1.next(j.name(), tag);
59 }
60
61 // close the input file
62 //
63 sof1.close();
64
65 // exit gracefully
66 //
67 Integral::exit();
68 }
One subtlety of this code example is that it also works on binary
files with absolutely no changes! This demonstrates the degree
to which the details of file I/O are abstracted from the user.
The Long::read()
method branches on the mode of the file, switching between formatted
text and direct binary input.
@ Sof v1.0 @
@ Long @
value = 13;
@ Long @
value = 27;
@ Long 32 @
value = -2812;
|
In the previous example, the tag numbers were not at all useful to the
program. While tags can be very useful for grouping together and
ordering data (say parallel arrays of floating point numbers and
integers), often they are not needed. To this end, text Sof files can
be simplified even further by omitting the tag numbers. For the
purpose of an iterative program such as the print routine above, the
Sof file shown at right will behave exactly the same as the first
example. Upon reading an object header with no tag, Sof will
implicitly assign this object a unique tag. These tags are from a
special range that will not be written back out to the file. The price
of this feature is that tags less than - 2^30 (< - 1.07 billion) cannot
be specified by the user.
From a high-level programmer's perspective, we have now covered the
meat of Sof. The file is opened by passing a filename to an
Sof::open() method. The optional arguments for the overloaded
open methods specify the file access mode and a file type. The
file access mode should be File::READ_ONLY, File::WRITE_ONLY,
File::READ_PLUS, or File::WRITE_PLUS. The first two are self
explanatory, File::READ_PLUS allows for reading and writing to an
existing file, and File::WRITE_PLUS creates a new file with the option
of reading data back out. The file type is either File::TEXT or
File::BINARY, useful only for newly created files (File::WRITE_ONLY or
File::WRITE_PLUS), as existing files already have a specified
type. When a file is opened in a write mode a lock will
automatically be obtained.
Once a Sof file is open, the program needs only navigate the
(name, tag) pairs before
informing an object to read itself and calculate such tags to ask an
object to write itself. The functions first() and
last() return the first and last tags of a specified class
name in the file. Additionally, next() and prev()
allow for iteration through items of the same name, and the number of
instances can be found with number(). Objects can be deleted
from a file either one at a time, a named class at a time, or all at
once with the three delete() methods. The entire file can be
deleted from disk with delete_file(). Hooks are also available
to copy data from one Sof file to another, changing the byte mode for
binary files. Every other aspect of I/O is left to the objects.
File class
The File class abstracts file manipulations, which are operating
system specific, and provides a general interface that all IFC's use
internally to access files. The File class capabilities include, but
are not limited to:
- Reading binary or text from files or from stdin
- Writing binary or text to files or to stdout
- Creation and removal of directories
- Creation, tracking, and automatic removal of temporary files
- Automatic handling of system-specific byte-order modes
The file class handles most low level file operations for the
IFC's. It is not intended for direct interfacing with the user.
Filename class
The Filename class can possesses a few file manipulation
capabilities. Rather than building one directory at a time, as does
the File class, the Filename class can build entire paths at once. It
can also retrieve the file's extension, directory, or operating system
of origin.
AudioFile class
The AudioFile class inherits the File class, which is to say it
possesses all of the capabilities of the File class, and some added
abilities of it's own. The AudioFile class was created to handle
external (non-IFC) audio data formats. It can read and write raw,
Microsoft wav, and NIST SPHERE formats in addition to the IFC standard
Sof format. It can be modified to allow other data formats. Another
useful feature is the ability to read data from a required sample
index or sample time range from a file.
FeatureFile class
The FeatureFile class inherits the File class. The FeatureFile class is
another IFC which must work with external data formats; it is used to
manage feature data, obtained either in Sof form from files produced
by isip_transform or from other sources in raw binary or raw text
formats.
Refer to the
online manual pages
for complete descriptions of the above mentioned classes. To see detailed
examples of the usage of these classes, refer to any of the
utilities
provided in our software distribution.
|
| |
|