niffsdk
niffsdk - faq for the NIFFSDK
User Guide prepared by Tim Butler and Cindy Grande <72723.1272@compuserve.com>
- What is the Niff SDK?
- Who is to blame?
- What is in the NIFF SDK?
- What platforms does the NIFF SDK support?
- Where can I get the NIFF SDK?
- What is the latest version of the NIFF SDK?
- What features are provided by NIFFSDK?
- How do I find my way around the NIFF SDK?
- How do I install the NIFF SDK?
.
- How can I learn about the NIFF SDK?
- How do I write a NIFF file?
- Is there an easy way to write the Chunk Length Table?
- Is there an easy way to write the String Table?
- How do I read a NIFF file?
- How do I parse a NIFF file?
- What information does the parser provide to the user callback functions?
- What is the difference between Atomic and Taggable Chunks?
- What is the difference between a Raw and Cooked Chunk?
.
- What are the coding conventions?
- Why are there so many types of registration routines and callback functions?
.
The NIFF SDK is a free, public domain, platform independent Software
Developer's Kit (SDK) for software developers implementing the
Notation Interchange File Format (NIFF). It is a collection of
software libraries and tools to support reading, writing, and
navigating NIFF files. NIFF documentation, sample code, and sample
NIFF files are included. The NIFF SDK makes it possible for a software
developer to add NIFF reading and writing capabilities to an existing
program without writing the housekeeping functions that would
otherwise be required.
The first release of this software has been through an initial testing
phase, but further improvements are expected. Your bug reports,
comments and suggestions are welcome - please direct them to Tim
Butler (tim@netbox.com).
The software is supplied in source code format in the C programming
language.
This guide assumes the reader is familiar with NIFF, RIFF and the C
programming language. For detailed information on the NIFF format,
and a summary of RIFF, please refer to the current NIFF specification,
at blackbox.cartah.washington.edu, in the /pub/NIFF directory.
- Tim Butler
- Cindy Grande
- Mark Walsen
- Steven Mounce
.
Tim Butler (tim@netbox.com)
The NIFFSDK is comprised of three major parts:
-
*RIFFIO
-
Libraries and tools for RIFF files.
-
*NIFFIO
-
Libraries and tools for NIFF files (a special case of RIFF)
Uses RIFFIO and NIFF.
-
*NIFF
-
``Official'' files from the NIFF Project (really only niff.h for now).
.
The NIFF SDK is written in ANSI C and uses ANSI-specific features not
provided in K+R C such as function prototypes, preprocessor
stringizing and token concatenation, and assumes minimum sizes for
data types. ANSI I/O operations and the console user interface are
used as the defaults, although the user may override these. (Future
versions may include Windows and Macintosh code for this purpose.)
The NIFF SDK has been compiled and (partially) tested under:
HP/UX C compiler
Borland C 4.5 (MS-DOS)
Think C (Mac)
The NIFF SDK is available for anonymous FTP from
URL ftp://blackbox.cartah.washington.edu
in the /pub/NIFF directory.
Compressed zip and cpt formats are provided (NIFFSDK.ZIP and NIFFSDK.CPT).
Version 1.02
Your comments, bug reports and suggestions are welcome, and will be
incorporated in future versions. Report them to Tim Butler, at the
email address above.
This version's documentation only covers the highest level user
interface, for NIFF files only. Other kinds of RIFF files are also
supported by the NIFF SDK, but are not yet documented in this User
Guide. After some practice, programmers might want to try out some of
the user-customizable features such as custom error handling and
non-ANSI I/O operations. Documentation for these features is still in
progress.
RIFFIO:
- Customizable error handling and reporting.
- Supports read, write, seek, and tell operations on RIFF files
through user-supplied callbacks.
- Reads and writes integers to RIFF files regardless of
machine byte order.
- Read, write, and navigate chunks in a RIFF file
- Operations to construct and disassemble four-character codes.
- Provides a table data structure keyed on four-character codes.
STDCRIFF (in RIFFIO):.
- Provides file I/O callbacks to RIFFIO using the Standard C library.
NIFFIO:.
- Extends RIFFIO to handle NIFF files
- Provides a chunk length table data struture and operations.
- Extends RIFFIO files to support chunk length tables.
- Supports writing strings to NIFF string tables.
- Read, write, and navigate tags in a NIFF file.
- Reads and writes NIFF primitive types, regardless of machine representation.
- Reads and writes NIFF chunk and tag structures.
- Provides a NIFF parser that scans a NIFF file and makes callbacks
to user-supplied functions based on list, chunk, and tag types.
- Keeps track of pending lists and chunks while writing a NIFF file.
.
Each component may contain the following subdirectories:
-
*include
-
source include files
-
*doc/txt
-
documentation in text format
-
*doc/{htm,man,pod}
-
documentation in other formats (derived from text)
-
*mk
-
Makefile includes
-
*src/lib
-
source code for libraries
-
*src/cmd
-
source code for tools
-
*src/example
-
example source code
-
*src/test
-
source code for testing
-
*test
-
test suite
.
The exact procedure depends on your programming environment.
Under Unix use the GNU configuration script and make:
If you aren't running Unix then you are on your own (for now)..You will have to track down all the sources tucked away in the nooks
and crannies of the distribution.
You might want to start by building the ``hello world'' application in
riffio/src/example/hello, or write a simple file using
niffio/src/example/nif001. For nif001, link in all of the libraries in the
{riffio,niffio}/src/lib directories.
I used a single (silent) T to start some sentences that don't begin with a
capital letter. It's a kludge.
- Read the online documentation
The documentation is in pretty bad shape right now but is a top
priority.
Each part of the NIFFSDK has a doc directory that contains documentation
in various formats including text, unix man pages, perl POD, and HTML.
(Some of the HTML links even work!)
Most of the documentation that exists is for reference, rather than learning.
It is extracted directly from the source.
There is no index to the documentation yet.
- Look at example source code.
There are a few examples under {riffio,niffio}/src/example
.
Look at the example program niffio/src/example/nif001.
The easiest way to write a NIFF file is to use the NIFFIOStorage
functions. They provide a layer on top of the NIFFIOFile routines to
keep track of lists and chunks that have not been finalized.
First, open a file using fopen(), and save the file pointer. Then
call NIFFIOStorageNewSTDC() to allocate and set up the storage areas
needed to keep track of the status of this file. NIFFIOStorage operations
will assume that all following I/O operations are intended for this
file (the ``current'' file), unless otherwise specified.
If more than one NIFF file is to be active at once, you must perform
the open and NIFFIOStorageNewSTDC() steps for each file. You must
change the current file by calling NIFFIOStorageSetCurrent(pstore),
where <pstore> is the NIFFIOStorage pointer returned by the
NIFFIOStorageNewSTDC() call for the desired file.
After all I/O to a file is complete, call NIFFIOStorageDelete(pstore),
using the pointer returned by NIFFIOStorageNewSTDC(). This
releases the storage used by the NIFF SDK for this file.
To write the NIFF form chunk, use NIFFIOStartNiff(). This must be
balanced with a call to NIFFIOEndNiff() when you are finished writing
to the file.
To write a NIFF list chunk, call NIFFIOStartXXX, where XXX is the name
of the list, as defined in the niff.h header file. After all chunks
have been written for this list, complete the list with a call to
NIFFIOEndXXX. For example, for a Setup Section list, use
NIFFIOStartSetupSection() and NIFFIOEndSetupSection().
To write an ordinary chunk, call NIFFIOchunkYYY(arg list), where YYY
is the name of the chunk, as defined in the niff.h header file, and
arg list is an argument list composed of the fields in the chunk's
structure. For example, to write a Part chunk,
NIFFIOchunkPart(0,0,1,2,-1,-1,-1);
supplies constant values for the fields in the following structure:
typedef struct niffPart
{
SHORT partID;
STROFFSET name;
STROFFSET abbreviation;
BYTE numberOfStaves;
SIGNEDBYTE midiChannel;
SIGNEDBYTE midiCable;
SIGNEDBYTE transpose;
} niffPart;
To add a tag to a chunk, call NIFFIOtagZZZ(arg list), where ZZZ is the
name of the tag, as defined in the niff.h header file, and arg list is
an argument list composed of the fields in the tag's structure. For
example, to write a Logical Placement tag,
NIFFIOtagLogicalPlacement(0, 2, 0);
supplies constant values for the fields in the following structure:
typedef struct niffLogicalPlacement
{
BYTE horizontal;
BYTE vertical;
BYTE proximity;
} niffLogicalPlacement;
Yes. Call NIFFIOStoreDefaultCLT(). The NIFFIOStoreDefaultCLT() function
uses a standard chunk length table that includes all valid NIFF chunks
and their lengths in the current NIFF version. If you want a custom
chunk length table, some lower level functions can be used for this
purpose (but the procedure is not yet documented).
Yes. Call NIFFIOchunkStringTable() to begin a string table chunk.
Then call NIFFIOStoreStbl(myStbl, n), where myStbl is an array of type
NIFFIOStbl, and <n> is the number of strings. You must first fill
NIFFIOStoreStbl() will store the string data,
calculating the correct offset for each string and storing it in the
MyWriteStringTable() in niffio/src/example/nif001/nif001.c for an
example.
Create a new NIFFIOFile from you own Standard C Library FILE pointer
using NIFFIOFileNewSTDC().
See the niffio/src/cmd/niffdump/niffdump.c for an example of
the next steps.
Allocate space needed by the NIFF SDK's parsing routines with the
following statement:
pparserNew = NIFFIOParserNew();
Then ``register'' each distinct type of list, chunk and tag that your
program recognizes. Registering an item means specifying a pointer to
the names of user ``callback'' functions that are to be called when the
parser encounters an item of this type. These functions are where you
would put your own program logic for interpreting the values in each
list, chunk or tag.
For lists and standard chunks, you can specify two functions - one to
be called when the list or chunk is first encountered (function1), and
one to be called after all components of the list or chunk have been
read (function2). For ``atomic'' chunks (those which are not allowed
tags) and for tags, you can specify only one function - to be called
after the chunk or tag has been read. The function names and contents
are entirely up to you - those supplied in callback.c are examples.
To register a list, use the following statement, where XXX is the name
of the list (from niff.h):
NIFFIORegisterListXXX(pparserNew, function1, function2);
To register a chunk, use the following statement, where YYY is the
name of the chunk (from niff.h):
NIFFIORegisterChunkYYY(pparserNew, function1, function2);
To register a tag, use the following statement, where ZZZ is the name
of the tag (from niff.h), and YYY is the name of the parent chunk:
NIFFIORegisterTagZZZ(pparserNew, niffckidYYY, function1);
You may specify NIFFIO_FOURCC_WILDCARD in place of <niffckidYYY>, if
you want to use the same function to process a tag no matter which
chunk is its parent.
You may also register default functions for a list, taggable chunk,
atomic chunk, or tag. These functions will be called when any
unregistered item of the appropriate type is encountered.
After initializing the parser, use the following statement to set the tracing
feature either off or on:
NIFFIOParserSetTracing(pparser, 1); /* 1 = trace on, 0 = trace off */
The tracer sends output to the console as it encounters each item in the file.
Next, start the parser with the following statement:
NIFFIOParseFile(pparser, pnf, 0, 0 );
This specifies the file to be parsed, and the parser storage area (the one
containing the pointers to the registered functions, initialized above).
The parser will proceed through the file, passing control to the user callback
functions when it encounters the corresponding items.
Each callback function is supplied with a ``context'' storage area that contains
information about the current state of the parser and the file. See the
documentation for the following structures in niffio.h:
-
*NIFFIOChunkContext
-
Parser state information provided to chunk callbacks.
-
*NIFFIOUserContext
-
User-defined parser state information provided by parent chunk callbacks.
-
*NIFFIOTagContext
-
Parser state information provided to tag callbacks.
.
The NIFF SDK parser uses the term ``atomic'' to refer to a chunk type
which will not have any tags. (That is, the Chunk Length Table entry
for this chunk type is -1). A ``taggable'' chunk is any other type of
chunk. These terms distinguish the different chunk types according to
how they are used by the parser.
The NIFF SDK parser calls a chunk ``raw'' when it has an empty fixed
portion. (The Chunk Length Table entry for this chunk type is zero.)
A ``cooked'' chunk is one which has a defined structure, and thus a
positive value in its Chunk Length Table entry.
Here are some Hungarian notation prefixes that are used throughout the
source.
str NUL-terminated string
rf RIFFIOFile
nf NIFFIOFile
offset RIFFIOOffset
fcc Four-character code
chunk RIFFIOChunk
size RIFFIOSize
tag NIFFIOTag
userctx NIFFIOUserContext
chunkctx NIFFIOChunkContext
tagctx NIFFIOTagContext
clt NIFFIOChunkLengthTable
The idea was to use C's argument and type checking as much as
possible. Each registration function takes callbacks that are
specific to chunk and tag types.
(To be continued...
...really...)
$Id: niffsdk.txt,v 1.2 1996/06/15 03:11:16 tim Exp $