NAME ==== niffsdk - faq for the NIFFSDK SYNOPSIS ======== User Guide prepared by Tim Butler and Cindy Grande <72723.1272@compuserve.com> General ------- - 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? Using the NIFFSDK ----------------- - 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? Hacking ------- - What are the coding conventions? - Why are there so many types of registration routines and callback functions? General ======= What is the Niff SDK? --------------------- 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. Who is to blame? ---------------- * Tim Butler * Cindy Grande * Mark Walsen * Steven Mounce Who do I complain to? --------------------- Tim Butler (tim@netbox.com) What is in the NIFF SDK? ------------------------ 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). What platforms does the NIFF SDK support? ----------------------------------------- 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) Where can I get the NIFF SDK? ----------------------------- 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). What is the latest version of the NIFF SDK? ------------------------------------------- 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. What features are provided by NIFFSDK? -------------------------------------- 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. How do I find my way around the NIFF SDK? ----------------------------------------- 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 How do I install the NIFF SDK? ================================ The exact procedure depends on your programming environment. Under Unix use the GNU configuration script and make: 1. Change directories to top of the distribution. 2. Run configure. If you need to customize the configuration then run "configure --help" for help. 3. make all 4. make check (optional) 5. make install 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. What are those extraneous T's at the beginning of some sentences? ----------------------------------------------------------------- I used a single (silent) T to start some sentences that don't begin with a capital letter. It's a kludge. USING THE NIFFSDK ================= How can I learn about the NIFF SDK? ----------------------------------- * 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 How do I write a NIFF file? --------------------------- 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 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; Is there an easy way to write the Chunk Length Table? ----------------------------------------------------- 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). Is there an easy way to write the String Table? =============================================== Yes. Call NIFFIOchunkStringTable() to begin a string table chunk. Then call NIFFIOStoreStbl(myStbl, n), where myStbl is an array of type NIFFIOStbl, and is the number of strings. You must first fill with pointers to all the strings you will refer to later in the NIFF file. NIFFIOStoreStbl() will store the string data, calculating the correct offset for each string and storing it in the offset element that corresponds to the pointer to the string. See MyWriteStringTable() in niffio/src/example/nif001/nif001.c for an example. How do I read a NIFF file? -------------------------- 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 , 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. How do I parse a NIFF file? --------------------------- 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. What information does the parser provide to the user callback functions? ------------------------------------------------------------------------ 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. What is the difference between Atomic and Taggable Chunks? ---------------------------------------------------------- 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. What is the difference between a Raw and Cooked Chunk? ------------------------------------------------------ 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. HACKING ======= What are the coding conventions? -------------------------------- 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 Why are there so many types of registration routines and callback functions? ---------------------------------------------------------------------------- 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.1 2009/06/08 05:33:44 gerd Exp $