The Audio File (AF) Library, libaudiofile.so, provides a uniform programming interface for reading and writing audio files. Currently, the AF Library supports the Apple Computer Inc. Audio Interchange File Format (AIFF) and the Audio Interchange File Format with Compression (AIFF-C).
The AF Library currently supports read-only and write-only file access (but not both simultaneously). Therefore, to alter an existing file, you must create a new file and copy data from the original file. Sample code that demonstrates how to copy the logical components of a file, and other concepts, is available online in /usr/people/4Dgifts/examples/dmedia/soundfile.
The Audio File Library comprises routines that handle four basic tasks:
creating and configuring new audio files
reading and writing track information to and from audio files
reading and writing instrument configurations to and from audio files
reading and writing miscellaneous data to and from audio files
In this chapter:
“Audio File Library Basics” discusses the basics of programming with the AF Library.
“Creating and Configuring Audio Files” explains how to initialize AF Library data structures.
“Opening, Closing, and Updating Audio Files” explains how to create and use audio files.
“Reading and Writing Audio Track Information” explains how to work with audio file tracks.
“Audio File Library Programming Tips” contains important programming tips for making AF Library programs format independent and multithread/multiprocessor safe.
This section explains fundamental AF Library concepts.
The AF Library has two basic data structures:
AFfilesetup, an audio file setup that stores initialization parameters used when creating a new audio file handle
AFfilehandle, an audio file handle that provides access to the audio file
The basic steps required for setting up an audio file for writing are:
Initialize an AFfilesetup, by calling AFnewfilesetup().
Configure the AFfilesetup for your data, as described in “Creating an Audio File Setup”.
Open an audio file for reading or writing, as described in “Creating an Audio File Setup” by calling either AFopenfile() or AFopenfd(). These routines return an AFfilehandle whose data configuration matches the settings in the AFfilesetup.
The AF Library provides an error handling mechanism that directs error messages to stderr. You can replace the default AF Library error handler with one of your own.
AFseterrorhandler() lets you replace the default error handler function with one of your own. Its function prototype is:
AFerrfunc AFseterrorhandler(AFerrfunc errfunc) |
where errfunc is a pointer to an alternate error handling routine of type AFerrfunc that is declared as:
void errfunc(long arg1, const char* arg2) |
This section explains basic concepts for working with audio files. It describes data structures used by the Audio File Library and in particular, the structure of AIFF-C files and the higher-level abstraction that the AF Library API uses to read and write AIFF-C (and AIFF) files.
The AF Library breaks audio files into the following four functional components:
The two portions of an audio file you will make most use of are audio tracks and instrument configurations.
Audio file format is typically indicated by header information preceding the actual data that describes the nature of the data in that file. The file format of an audio file constrains the data format of each of its tracks to one of a set of track formats supported by that file format, but you do not necessarily know which one. You must therefore set and query the track format for each of an audio file's tracks independently of its file format. It is often possible and desirable to write your application so that it queries only the data format(s) of the track(s) (instead of querying the file format) of the audio files it opens.
Audio tracks contain the recorded samples that produce sound when sent to the audio hardware. These samples are stored linearly for mono recordings and as interleaved left-right pairs (left channel in even locations, right channel in odd locations) for stereo recordings. These pairs are called sample frames (this term is also used for mono tracks, but a sample frame is the same thing as a sample when mono data is used).
Audio tracks also contain track markers, which can be set to point to arbitrary locations in the audio track. These markers, which are identified by a long integer ID number and (optionally) a name string, point to locations between sample frames.
Data format information, including sample rate, sample format, sample width, and sample compression type is stored as part of the audio track. Several kinds of compression are supported (you can also choose not to use compression). The AF Library automatically compresses samples being written to a file and decompresses samples read from a file. The ability of the AF Library to perform compression/decompression of audio data in real time is dependent on system overhead. To guarantee real-time performance, you should make use of scheduling control as described in “Using Scheduling Control to Give Audio High Priority” in Chapter 6.
Instrument configurations contain a set of parameters that define the aspects of a sampler, including detuning, key velocity, and gain. They also contain loop markers, which identify the beginning and ending points of loops that allow all or part of the audio track to be repeated. These loop markers point to previously created audio track markers, which in turn refer to locations in the audio track that comprise the beginning and ending of the loop. AIFF and AIFF-C files support two kinds of loops, sustain and release, each with a beginning and ending marker, which can be used in audio tracks and track markers.
Silicon Graphics has adopted AIFF-C as its standard digital audio file format. AIFF-C is based on Apple Computer's Audio Interchange File Format (AIFF), which conforms to the EA IFF 85 Standard for Interchange Format Files developed by Electronic Arts. Unlike the older AIFF standard, AIFF-C files can store compressed sample data as well as two's complement linear PCM sample data.
AIFF-C provides a standard file format for storing sampled sounds on magnetic media. The format can store any number of channels of sampled sound at a variety of sample rates and sample widths. The format is extensible, allowing future support of new compression types and application-specific data, while maintaining backward compatibility.
An AIFF-C file is composed of a series of different kinds of data chunks. For the most part, the AF Library API handles low-level chunk manipulation. For complete information on the types of chunks supported by AIFF-C, see the Audio Interchange File Format with Compression (AIFF-C) Specification.
Both AIFF and AIFF-C files consist of similar component structures. The chunks in an AIFF-C file are grouped together inside a special container chunk. The EA IFF 85 specification defines several types of container chunks, but the kind used by AIFF-C is of type 'FORM'.
Table 7-1 shows the mapping between the Audio File Library API functional components and the low-level AIFF-C/AIFF data chunks.
Table 7-1. Mapping of AF Library Components to AIFF-C/AIFF File Chunks
AF Library Functional Component | AIFF-C/AIFF Chunks |
|---|---|
File format information | 'FVER', 'FORM' |
Audio tracks | 'SSND', 'COMM', 'MARK', 'AESD', 'COMT'[a] |
Instrument configurations | 'INST' |
Miscellaneous data | 'AUTH', 'NAME', '(c) ', 'ANNO',' MIDI ', 'APPL' |
[a] 'COMT' chunks are not currently supported by the AF Library. | |
This section explains how to initialize an AF Library application, including how to create, configure, and free AF Library data structures for working with audio files.
The AFfilesetup structure stores initialization parameters used when creating a new audio file. When you open an audio file for reading or writing the AF Library returns another structure, an AFfilehandle, which provides access to the audio file and is used as an argument by all AF Library routines.
AFnewfilesetup() creates and initializes an AFfilesetup structure that you configure for your data, and then use to open an audio file:
AFfilesetup AFnewfilesetup(void) |
AFnewfilesetup() returns a default AFfilesetup structure.
Table 7-2 lists the AFfilesetup configuration parameters and their defaults.
Table 7-2. AFfilesetup Parameters and Defaults
Parameter | Default |
|---|---|
AF_FILE_AIFFC | |
AF_DEFAULT_TRACK | |
Audio track sample format, sample width | AF_SAMPFMT_TWOSCOMP, 16-bit |
Audio track channels (interleaved) | 2 (stereo) |
AF_COMPRESSION_NONE | |
Four markers with IDs: 1,2,3,4 | |
AF_DEFAULT_INST | |
Instrument Parameters | (See Table 7-3) |
Two loops with IDs: 1,2; default mode is AF_LOOP_MODE_NOLOOP |
Table 7-3 lists the AFfilesetup instrument parameters and their defaults.
Table 7-3. AFfilesetup Instrument Parameter Constants and Defaults
Instrument Parameter Constant | Default |
|---|---|
AF_INST_MIDI_BASENOTE | 60 |
AF_INST_MIDI_HINOTE | 127 |
AF_INST_MIDI_HIVELOCITY | 127 |
AF_INST_MIDI_LONOTE | 0 |
AF_INST_MIDI_LOVELOCITY | 1 |
AF_INST_NUMCENTS_DETUNE | 0 |
AF_INST_NUMDBS_GAIN | 0 |
AF_INST_SUSLOOPID | 1 (loop ID for sustain loop) |
AF_INST_RELLOOPID | 2 (loop ID for release loop) |
Your application should free an AFfilesetup that is no longer needed. AFfreefilesetup() deallocates an AFfilesetup structure. Its function prototype is:
void AFfreefilesetup(AFfilesetup setup) |
where setup is an AFfilesetup previously created by a call to AFnewfilesetup(). This does not affect any file previously opened using the same AFfilesetup structure.
Before using the new AFfilesetup to open an audio file, you might need to modify the default AFfilesetup in order to create the configuration you want. The sections that follow explain how to change the default AFfilesetup configuration.
You need to set the file format in an AFfilesetup structure before passing the structure to AFopenfile().
AFinitfilefmt() configures the file format parameter in an AFfilesetup structure. Its function prototype is:
void AFinitfilefmt(AFfilesetup setup, long fmt) |
where setup is the AFfilesetup structure, and fmt is an integer constant which specifies an audio format supported by the AF Library. Two valid format types are currently available:
A new audio file that is opened by calling AFopenfile() with this AFfilesetup as an argument will then be formatted accordingly.
This section explains how to change the default settings for audio track parameters in an AFfilesetup structure before passing the structure to AFopenfile().
![]() | Note: Each of the functions in this section contains a trackid argument, which identifies an audio track in the AFfilesetup structure being initialized. In the current release of the AF Library, the value of trackid must always be AF_DEFAULT_TRACK. |
The AF Library requires that you specify the sample rate for a new file before you pass the AFfilesetup structure to AFopenfile().
AFinitrate() configures the sample rate in Hz for an audio track in an AFfilesetup structure. Its function prototype is:
void AFinitrate(AFfilesetup setup, long trackid, double rate) |
where setup is the AFfilesetup structure, trackid is a long integer that identifies an audio track in setup, and rate is a positive double-precision integer that specifies the sample rate in Hz. For example, to configure setup for a CD-quality AIFF-C file, initialize the rate for AF_DEFAULT_TRACK to 44100.0.
AFinitsampfmt() initializes the sample format and width parameters for an audio track in an AFfilesetup structure. Its function prototype is:
void AFinitsampfmt(AFfilesetup setup, long trackid, long fmt,
long width)
|
where setup is the AFfilesetup structure, trackid is a long integer that identifies an audio track in setup, and fmt is a long integer constant that denotes a sample format. Currently, only one format is supported: AF_SAMPFMT_TWOSCOMP. width is a positive long integer value from 1 to 32 that specifies the width (in bits) of the sample data. See “Getting Audio Track Sample Format and Sample Width” for more details about sample format and sample width.
![]() | Note: If the audio track in an AIFF-C file is configured for compression, fmt and width should match the data format specified by the compression algorithm. See Table 7-4 for a list of compression algorithms. |
AFinitchannels() configures the number of interleaved audio channels for an audio track within an AFfilesetup structure. This information is then used by AFopenfile() when it is called with the AFfilesetup structure as an argument. Its function prototype is:
void AFinitchannels(AFfilesetup setup, long trackid,
long channels)
|
where setup is the AFfilesetup structure, trackid is a long integer that identifies an audio track in setup, and channels is a long integer representing the number of interleaved audio channels. Valid values for channels are 1 (mono) or 2 (stereo); the default value is 2.
AES channel status bytes are embedded in AES audio samples to provide additional information about that data, such as whether an emphasis has been added to a sample. For example, on early CD recordings, high frequencies were sometimes emphasized to compensate for the nature of CD players. You might want to reverse compensate for that emphasis if you are loading AES stream data directly from a CD player through the AES serial input of your workstation for playback on a different source, such as DAT. See the AES3-1985 (ANSI S4.40-1985) document for more information about AES channel status bytes.
AFinitaeschanneldata() sets a flag, which is off by default, in an AFfilesetup structure to indicate that space should be reserved for the 24 AES channel status bytes that are embedded in all AES data. Its function prototype is:
void AFinitaeschanneldata(AFfilesetup setup, long trackid) |
where setup is the AFfilesetup structure, and trackid is a long integer that identifies an audio track in setup.
AFsetaeschanneldata() sets the values of the AES channel status bytes. Its function prototype is:
void AFsetaeschanneldata(AFfilehandle file, long trackid,
unsigned char buf[24])
|
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), trackid is the ID for the audio track (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_TRACK), and buf is a 24-element array that specifies the AES channel status bytes. If no header space has been reserved in the file (by calling AFinitaeschanneldata() before creating the file), AFsetaeschanneldata() ignores the data and returns without error.
AFinitcompression() and AFinitcompressionparams() let you configure an audio track in an AFfilesetup structure to store compressed audio data. All compression encoding is handled automatically by AFwriteframes(); therefore your application program need only work with linear PCM data.
![]() | Note: AIFF files do not support compression. It is an error to try to open an AIFF file using an AFfilesetup whose compression setting is other than AF_COMPRESSION_NONE. |
AFinitcompression() lets you select from among several built-in default codec (compressor-decompressor) configurations that are preconfigured. If you use AFinitcompression() to select one of the default codecs that are built in to the Audio File Library, you don't have to worry about setting the individual compression parameters, because they are automatically set to the proper values for each default configuration.
AFinitcompressionparams() lets you chose the codec configuration and set the associated codec-specific compression parameters yourself, although it does supply the defaults listed in Table 7-4. If you use AFinitcompressionparams(), you have to create and fill in an Audio Utility Library parameter-value list (AUpvlist), as described in “Using the Audio Utility Library to Initialize Parameter Lists”.
You may also select from additional audio codecs from Aware, Inc. that provide ISO/MPEG I and Aware MultiRate I audio compression, which are built in to the Audio File Library and can be accessed under license from Aware, Inc. by using the parameters in Table 7-4. See Appendix B, “Aware Scalable Audio Compression Software,” for more information.
The function prototypes are:
void AFinitcompression( AFfilesetup setup, long track,
long compression)
long AFinitcompressionparams( AFfilesetup setup, long track,
long compression,
AUpvlist pvlist, long numitems)
|
where:
| setup | is the AFfilesetup structure that was previously created by calling AFnewfilesetup(). | |
| track | is a positive long integer that identifies an audio track in setup. Because AIFF-C files contain only one audio track per file, you should use the constant AF_DEFAULT_TRACK to access the track. | |
| compression | is a positive integer symbolic constant that indicates the type of audio compression being used. See Table 7-4 for a list of valid compression values. | |
| pvlist | is an Audio Utility Library parameter-value list (AUpvlist) structure, filled with parameters and values related to the compression scheme compression. Currently, the only compression schemes that have any parameters are those supplied by Aware, Inc. | |
| numitems | is the number of valid entries in the pvlist. |
Table 7-4 lists the valid compression values that you can set for AIFF-C files; compression must be AF_COMPRESSION_NONE for AIFF files.
Table 7-4. Settable Compression Parameter Values and Types
Parameter Value | Compression Type |
AF_COMPRESSION_NONE | No compression. |
AF_COMPRESSION_G722 | |
AF_COMPRESSION_G711_ULAW | 64 Kbps PCM encoding for 8 kHz 16-bit. |
AF_COMPRESSION_G711_ALAW | 64 Kbps PCM encoding for 8 kHz 16-bit. |
AF_COMPRESSION_AWARE_MPEG[a] | Aware implementation of ISO/MPEG I-audio, Layers I and II. |
AF_COMPRESSION_AWARE_MULTIRATEa | Aware MultiRate I lossless or near-lossless algorithm |
AF_COMPRESSION_AWARE_DEFAULT_MPEG_I[b] | Aware implementation of ISO/MPEG I-audio layer I, joint-stereo, fixed rate at 192 Kbps per channel. |
AF_COMPRESSION_AWARE_DEFAULT_MPEG_IIb | Aware implementation of ISO/MPEG I-audio layer II, joint-stereo, fixed rate at 128 Kbps per channel. |
AF_COMPRESSION_AWARE_DEFAULT_MULTIRATE b | Aware MultiRate I operating in high-resolution near-lossless (near perfect reconstruction) mode. |
AF_COMPRESSION_AWARE_DEFAULT_LOSSLESSb | Aware MultiRate I operating in lossless (perfect reconstruction) mode. |
[a] These values are intended for use with AFinitcompressionparams(). [b] These values are intended for use with AFinitcompression(). | |
Audio track marker structures store sample frame locations in the track for looping and other purposes. Markers are identified by a long integer ID number and (optionally) a name string. Markers point to a location between two samples in the audio track: position 0 is before the first sample, position 1 is between the first and second sample, and so on. You can assign positions to the markers by calling AFsetmarkpos(). By default, AFnewfilesetup() allocates space for four markers, which is sufficient to store the beginning and end points for both a sustain loop and a release loop.
AFinitmarkids() initializes a list of unique marker IDs corresponding to marker structures in a given audio track. Its function prototype is:
void AFinitmarkids(AFfilesetup setup, long trackid,
long markids[], long nmarks)
|
where setup is the AFfilesetup structure, trackid is a long integer that identifies an audio track in setup, markids is an array of unique positive long integers that will be used as handles for the marker structures in the file opened with setup, nmarks is a long integer that specifies the number of marker IDs in the markids array, that is, the total number of marker structures that will be allocated for the audio track. AIFF-C (and AIFF) files can contain up to 65535 markers in a track.
AFinitmarkname() specifies a name string for a marker structure. Marker names default to empty strings. Its function prototype is:
void AFinitmarkname(AFfilesetup setup, long trackid,
long markid, char *name)
|
where setup is the AFfilesetup structure, trackid is a long integer that identifies an audio track in setup, markid is a positive long integer that identifies a marker structure configured previously by AFinitmarkids(), name is a string that will be written into the marker structure when an audio file is created by passing setup to AFopenfile().
This section explains how to initialize the instrument parameters in an AFfilesetup structure before passing the structure to AFopenfile().
AFinitinstids() initializes a list of unique instrument IDs that are used to reference the instrument configurations in an AFfilesetup. Its function prototype is:
void AFinitinstids(AFfilesetup setup, long instids[],
long ninsts)
|
where setup is the AFfilesetup structure, instids is an array of positive long integers that are used as handles for the instrument configurations in an audio file, and ninsts is the number of entries in instids.
![]() | Note: Currently, the AF Library supports only one instrument configuration per file, which is the maximum allowed by both AIFF and AIFF-C formats; therefore, ninsts should be set to either 0 or 1 and instids contains at most one element, whose value must be AF_DEFAULT_INST. If you set ninsts to 0 (meaning that no instrument configuration will be in the audio file you plan to open), AFinitinstids() will ignore the instids argument, and instids can be made a null pointer in this case. |
AFinitloopids() initializes a list of unique instrument loop IDs that correspond to the loops supplied for a specified instrument in an audio file. Its function prototype is:
void AFinitloopids(AFfilesetup setup, long instid,
long loopids[], long nloops)
|
where setup is the AFfilesetup structure, instid is a long integer that identifies an instrument configuration in an audio track. In the current release of the AF Library, the value of instid should always be AF_DEFAULT_INST. loopids is an array of unique, positive long integers that will identify individual loops within an audio file opened using setup. nloops is a long integer that indicates the number of elements in loopids.
The values set in loopids can be used by other AF Library functions to set the start point, end point, and play mode for each loop (see “Reading and Writing Instrument Configurations”).
![]() | Note: In the current release of the AF Library, both AIFF and AIFF-C files must contain exactly 2 loops: a “sustain” loop and a “release” loop. nloops is currently ignored, since its value is always 2. |
Use these functions to initialize miscellaneous data chunks in an AFfilesetup structure, including file name, author, copyright, and annotation strings, MIDI data, and application-specific data.
AFinitmiscids() initializes a list of unique miscellaneous chunk IDs that are then used to reference various file format-dependent data chunks in an audio file. Its function prototype is:
void AFinitmiscids(AFfilesetup setup, long miscids[],
long nmisc)
|
where setup is the AFfilesetup structure, miscids is an array of unique, positive long integers used to reference the miscellaneous data chunks in an audio file opened using setup, nmisc is the number of elements in miscids, that is, the total number of miscellaneous chunks in the file configuration. The default number of miscellaneous IDs in an AFfilesetup structure is 0.
AFinitmisctype() initializes a miscellaneous data chunk with a given ID to one of a variety of supported chunk types in AIFF and AIFF-C files. Its function prototype is:
void AFinitmisctype(AFfilesetup setup, long miscid,
long type)
|
where setup is the AFfilesetup structure, miscid is a positive long integer that identifies a miscellaneous chunk in setup, and type is a long integer constant that defines the chunk type.
Table 7-5 lists the valid parameters for each chunk type.
Table 7-5. Miscellaneous Chunk Types and Parameter Values
Parameter Value | Miscellaneous Chunk Type |
|---|---|
AF_MISC_AIFF_ANNO | Annotation string |
AF_MISC_AIFF_APPL | Application-specific data |
AF_MISC_AIFF_AUTH | Author string |
AF_MISC_AIFF_COPY | Copyright string |
AF_MISC_AIFF_MIDI | MIDI data |
AF_MISC_AIFF_NAME | Name string |
A single AIFF or AIFF-C file may contain any number of ANNO, APPL, or MIDI chunks, but only one of each of the other (NAME, AUTH, and (c)) miscellaneous chunks.
AFinitmiscsize() initializes the amount of space reserved for miscellaneous chunks of data in an AFfilesetup structure. This space is then reserved (written as a zero-filled area) in the header structure of an audio file that is opened using the specified AFfilesetup structure.
Use AFwritemisc() to write the data after the file has been opened. The application program is responsible for managing the contents of the header space reserved for each chunk. Its function prototype is:
void AFinitmiscsize(AFfilesetup setup, long miscid,
long size)
|
where setup is the AFfilesetup structure, miscid is a positive long integer that identifies a miscellaneous chunk in setup, and size is a non-negative long integer that specifies the number of bytes to reserve for the chunk data identified by miscid. It is not necessary to add a trailing “zero pad byte” normally required by chunks in AIFF/AIFF-C files with odd numbers of data bytes (see the description for AFreadmisc()); the AF Library handles this transparently.
The Audio Utility Library, libaudioutil.so, provides routines for getting and setting parameters, parameter types, and parameter values contained in lists. Currently, these routines are used only when initializing and querying parameters for the built-in licensable audio compression software from Aware Inc., which is accessible from AF routines. Licenses can be verified by using the AUchecklicense() routine.
These routines use the Audio Utility Library parameter-value list (AUpvlist) data structure, which is an array of structures, each of which contains a list of parameters, parameter types, and parameter values.
Use AUpvnew() to create an empty AUpvlist with the specified number of blank structures. Its function prototype is:
AUpvlist AUpvnew(int numitems) |
where:
| numitems | is an integer number of list items to use when creating a new AUpvlist—one list item contains the parameter, parameter type, and parameter value entries. |
AUpvnew() returns an empty AUpvlist structure. If an error occurs— either because numitems is less than or equal to zero, or because of a memory allocation error—a null pointer, AU_NULL_PVLIST, is returned.
When an AUpvlist is no longer needed, you should free the memory associated with it by calling AUpvfree(). Its function prototype is:
int AUpvfree(AUpvlist pvlist) |
where pvlist is the structure for which memory should be freed.
Use the AUpvlist structure when setting and getting a parameter, its type, and its value. The “set” routines fill in the structure entries for the designated list item with the specified information; the “get” routines return the requested information in pointers corresponding to the item being queried.
Table 7-6 lists and describes the AU Library get and set routines.
Table 7-6. Audio Utility Library Set and Get Routines
Routine | Description |
|---|---|
AUpvgetmaxitems() | Returns the number of list entries allocated for pvlist when it was created by AUpvnew() |
AUgetparam() | Gets the parameter of the itemth entry in pvlist and returns it in param_ptr |
AUpvgetval() | Gets the value of the itemth entry in pvlist and returns it in val_ptr |
AUpvgetvaltype() | Gets the value type of the itemth entry in pvlist and returns it in type_ptr |
AUpvsetparam() | Sets the parameter of the itemth entry in pvlist to param |
AUpvsetval() | Sets the value of the itemth entry in pvlist to the value stored in val_ptr |
AUpvsetvaltype() | Sets the type of the value of the itemth entry in pvlist to type |
The function prototypes of the routines in Table 7-6 are:
int AUpvgetmaxitems(AUpvlist pvlist) int AUpvgetparam(AUpvlist pvlist, int item, int *param_ptr) int AUpvgetval(AUpvlist pvlist, int item, void *val_ptr) int AUpvgetvaltype(AUpvlist pvlist, int item, int *type_ptr) int AUpvsetparam(AUpvlist pvlist, int item, int param) int AUpvsetval(AUpvlist pvlist, int item, void *val_ptr) int AUpvsetvaltype(AUpvlist pvlist, int item, int type) |
| pvlist | is an Audio Utility Library parameter-value list data type created by a previous call to AUpvnew(). | |
| item | is an integer zero-based index into an AUpvlist. The index should be a non-negative value that is less than numitems-1. | |
| type | is a symbolic constant describing the type of parameter. Currently supported types are:
| |
| param | is an integer that will become the parameter or the parameter-value pair. | |
| val | is a pointer to a void type. Data is read from this pointer, interpreted according to the type associated with this entry, and stored in the AUpvlist. | |
| param_ptr | is a pointer to an integer that is filled with the value of the parameter portion of a parameter-value pair. | |
| value_ptr | is a pointer to a void type. Data representing the value portion of a parameter-value pair is copied to this address as interpreted by this entry's type. |
Use AUchecklicense() to verify whether a license for a particular audio product is available. Its function prototype is:
int AUchecklicense(int product, int *errorval,
char **message)
|
where:
| product | is a constant symbol for the product license that is being queried. Currently defined licenses are:
| |
| errorval | is a pointer to an integer describing a NetLS error, which will be set only if the return value is AU_LICENSE_ERR. | |
| message | is a pointer to a character pointer, which is changed to point to an informative string only if the return value is AU_LICENSE_ERR. The string contains the NetLS error that occurred and contact information on how to obtain support or a license. |
On successful completion, AUchecklicense() returns AU_LICENSE_OK. If product is unknown, then AU_BAD_PRODUCT is returned. If a NetLS error occurs, then AU_LICENSE_ERR is returned and *errorval and *message are set, describing the error. See Appendix B for more information about NetLS.
Example 7-1 contains a listing of a portion of code from the aifcinfo.c demo program that is provided in /usr/people/4Dgifts/examples/dmedia/soundfile. This portion of code creates an AUpvlist with 3 items, fills those items with the pertinent information, then frees the memory associated with the AUpvlist when it is no longer required.
Example 7-1. Creating, Filling, Querying and Freeing an AUpvlist
{
AUpvnew(&pvlist, 3);
AUpvsetparam(pvlist, 0, AF_AWARE_PARAM_LAYER);
AUpvsetvaltype(pvlist, 0, AU_PVTYPE_LONG);
AUpvsetparam(pvlist, 1, AF_AWARE_PARAM_BITRATE_POLICY);
AUpvsetvaltype(pvlist, 1, AU_PVTYPE_LONG);
AUpvsetparam(pvlist, 2, AF_AWARE_PARAM_BITRATE_TARGET);
AUpvsetvaltype(pvlist, 2, AU_PVTYPE_LONG);
AFgetcompressionparams(file, AF_DEFAULT_TRACK,
&track_desc->compressiontype, pvlist, 3);
AUpvgetval(pvlist, 0, &track_desc->aware_desc.layer);
AUpvgetval(pvlist, 1,
&track_desc->aware_desc.bitratepolicy);
AUpvgetval(pvlist, 2,
&track_desc->aware_desc.bitratetarget);
AUpvfree(pvlist);
}
|
Before opening a new audio file using AFopenfile(), create and configure an appropriate AFfilesetup structure (as described in “Creating and Configuring Audio Files”). Audio files can be opened either for reading or writing (but not both simultaneously). In order to change an existing file, you must copy the contents of the file to a new file, writing edits as you go. See the sample source code in /usr/people/4Dgifts/examples/dmedia/soundfile for a demonstration of this process.
AFopenfile() allocates and initializes an AFfilehandle structure for a named file. The audio track logical read/write pointer used by AFreadframes() and AFwriteframes() is initialized to point to the location of the first sample in the audio file. Its function prototype is:
AFfilehandle AFopenfile(char *name, char *mode,
AFfilesetup setup)
|
where name is a character string that names the file to be opened, and mode identifies whether the file is being opened for read or write access. Valid values for mode are:
"r" – read-only access
"w" – write-only access
setup is an AFfilesetup structure previously created using AFnewfilesetup() and configured using various AF Library initialization functions described in previous sections. setup is ignored when mode is set to "r".
AFopenfile() returns an AFfilehandle structure for the named file. If an error occurs, AFopenfile() returns the value AF_NULL_FILEHANDLE.
Another way of opening a file is to call the IRIX system function open() to open the file, and then get a handle to the file descriptor from the AF Library.
AFopenfd() returns an AFfilehandle structure for a file that has already been opened. Its function prototype is:
AFfilehandle AFopenfd(int fd, char *mode, AFfilesetup setup) |
where fd is an IRIX file descriptor previously returned by open(), mode identifies whether the file is being opened for read or write access (see AFopenfile()), and setup is an AFfilesetup structure previously created using AFnewfilesetup() and configured using various AF Library initialization functions described in previous sections. setup is ignored when mode is set
to "r".
AFopenfd() returns an AFfilehandle structure for the named file. If an error occurs, AFopenfd() returns the value AF_NULL_FILEHANDLE.
AFgetfd() returns the IRIX file descriptor associated with the audio file referred to by the given AFfilehandle structure. Its function prototype is:
int AFgetfd(AFfilehandle file) |
where file is the AFfilehandle structure previously created by a call to AFopenfile().
The file descriptor returned by AFgetfd() is intended for use in a select() loop. It is not intended to allow reading, writing, and seeking in an audio file without the knowledge of the Audio File Library. Doing so causes unpredictable results unless you save and restore the file position whenever you modify it.
The AF does not reposition the file to the correct place before reading from (using AFreadframes()) or writing to (using AFwriteframes()) it. If you modify the file position of the file descriptor given by AFgetfd(), you should save the file position and restore it to its previous position before reading or writing data to the file. Alternately, you can use one of two different file descriptors opened to the same file. The file must be re-opened in order to get a separate file descriptor (dup(2) will not work because it gives you two file descriptors that share the same file offset).
In addition, if you attempt to write to the file, no matter how the AFfilehandle was opened, the results are undefined.
AFclosefile() releases a file's resources back to the system. It also updates the headers of files opened for write access. The AFfilehandle structure deallocated by AFclosefile() should not be used by any subsequent AF Library function calls. Its function prototype is:
long AFclosefile(AFfilehandle file) |
where file is the AFfilehandle structure to be deallocated. This structure was returned by AFopenfile() when the file being closed was created.
AFclosefile() returns a negative value if an error occurs while closing a file and updating the header fields. If compression was used to write a file, a negative value indicates that some sample frames were lost due to the filter delay of the compressor. If no error occurs, the return value is 0.
AFsyncfile() updates the complete contents of an audio file opened for writing without actually closing the file. This is useful for maintaining consistent header information between writing samples to the file's audio track. Its function prototype is:
long AFsyncfile(AFfilehandle file) |
where file is the AFfilehandle structure to be updated. This structure was returned by AFopenfile() when the file being closed was created.
AFsyncfile() returns a negative value if an error occurs while trying to update file. If the update is successful, or if file was opened as read-only, AFsyncfile() returns 0.
This section describes functions that read and manipulate audio track data and parameters in an audio file. Your application should query for audio file characteristics before opening a file and reading and writing data.
This section describes functions that query the file format from either a file handle or from an IRIX file descriptor of an opened audio file.
AFgetfilefmt() returns an integer value indicating the format of the file and returns a separate version number for AIFF-C files. Its function prototype is:
long AFgetfilefmt(AFfilehandle file, long *version) |
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), and version is used to return a file format version number in the form of a non-negative long integer. AIFF files do not use version numbers, so a value of 0 is always returned as the AIFF version number.
AFgetfilefmt() returns a non-negative long integer indicating the format of the file. Currently supported values include:
AF_FILE_AIFFC (AIFF-C format)
AF_FILE_AIFF (AIFF format)
but your application should allow for the possibility of other (or unknown) file formats being returned.
AFidentifyfd() returns the file format of a given IRIX file descriptor. Its function prototype is:
long AFidentifyfd(int fd) |
where fd is an IRIX file descriptor previously returned by open().
AFidentifyfd() returns a long integer value representing the audio file format (see AFgetfilefmt() for the return values for supported formats). If AFidentifyfd() does not recognize the format, AF_FILE_UNKNOWN is returned. If the format is not one supported by the AF Library, AF_FILE_UNSUPPORTED is returned.
To determine whether a file is a sound file that can be opened by the AF, check for an unrecognizable format rather than a recognizable format. For example, rather than testing whether the file format is either AF_FILE_AIFF or AF_FILE_AIFC, use this code:
if (filefmt == AF_FILE_UNSUPPORTED ||
filefmt == AF_FIILE_UNKNOWN)
{
printf("file is not supported by the AF library!");
exit(0);
}
|
Applications that branch depending on the file format should still check for unrecognized formats:
switch (AFidentifyfd(fd))
{
case AF_FILE_AIFF: do_aiff_thing(); break;
case AF_FILE_AIFC: do_aiffc_thing(); break;
case AF_FILE_UNKNOWN:
case AF_FILE_UNSUPPORTED:
printf("this file is not supported by AF library!!");
exit(0);
default:
printf("program cannot handle this file format!");
exit(0);
}
|
![]() | Tip: Sometimes, instead of checking the file format, you should check the sampling format and other track parameters from the audio file track, as described in “Getting and Setting Audio Track Parameters”. For example, a program that simply reads 16-bit AF_SAMPFMT_TWOSCOMP audio data out of an AIFF file should be able to correctly read that type of data out of a file whose file format is not AIFF, as long as it does not also intend to read AIFF-specific chunks from the data (for example, certain MISC and INST chunks). Such a program has no need to call AFidentifyfd() or AFgetfilefmt() to get the file format. |
Most audio track parameters (except markers) must be initialized before a new audio file is opened and cannot be modified after that point, but you should query an audio file for its track parameters.
AFgetrate() returns the sample rate of an audio track in an opened audio file. Its function prototype is:
double AFgetrate(AFfilehandle file, long trackid) |
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), and trackid is the ID for the audio track (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_TRACK).
AFgetrate() returns a double-precision floating point value that describes in Hz the audio sampling rate of the audio track.
AFgetsampfmt() retrieves the sample format and sample width for an audio track in an opened audio file. Its function prototype is:
void AFgetsampfmt(AFfilehandle file, long trackid,
long *sampfmt, long *width)
|
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), trackid is the ID for the audio track (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_TRACK), sampfmt is a pointer to a long integer denoting the format of the sample data (for AIFF and AIFF-C files, this value is always AF_SAMPFMT_TWOSCOMP), and width is a pointer to a long integer that denotes the sample width in bits (for AIFF and AIFF-C files, this value is between 1 and 32).
![]() | Tip: Do not assume that AF_SAMPFMT_TWOSCOMP is the only value that can be returned by AFgetsampfmt(). Write your application so that it rejects files with sample formats it does not support. |
Sample width may or may not have meaning, depending on the value of sampfmt. For AF_SAMPFMT_TWOSCOMP data, you can use the sample width value to determine the data type used to pass samples to AFwriteframes() and from AFreadframes(): 1–8 bit samples are packed into chars, 9–16 bit samples are packed into shorts, and 17–32 bit samples are packed into longs. Data formats whose sample width is not a multiple of eight are augmented by zero-bit-padding on the right (see Figure 7-1).
There is a special case for reading 24-bit integer data. The AF automatically converts 3-byte data into 4-byte quantities in a manner that is compatible with the Audio Library (AL) by sign-extending the left-most bits of 17 to 24–bit data.
Figure 7-1 shows the data packing for twos complement integer data (AF_SAMPFMT_TWOSCOMP).
![]() | Tip: Don't assume that the maximum size of integers in files opened by the AF Library is 32 bits or that the number of bits will be a multiple of 8. Even for AIFF files, the sample width is not necessarily a multiple of 8. Generally, this can be ignored, because audio samples that do not take up an integral number of bytes are left-justified inside the next larger integral number of bytes (with the remaining bits set to 0). But you should write your application so that it does not assume the sample width is a multiple of 8, as demonstrated in Example 7-2. |
Example 7-2 checks for the audio track sample format, and then classifies integer data according to its sample width.
Example 7-2. Checking Audio Track Sample Format and Sample Width
#include <dmedia/audiofile.h>
...
AFfilehandle h = AFopenfile(....);
if (!h) return;
AFgetsampfmt(h, AF_DEFAULT_TRACK, &sampfmt, &sampwidth);
if (sampfmt != AF_SAMPFMT_TWOSCOMP)
{
printf("This program can't read audio files of this "
sample format");
exit(0);
}
/* round sampwidth up to nearest number of bytes */
int nbytes = ( (sampwidth-1) / 8 ) + 1;
switch (nbytes)
{
case 1: do_8_thing(); break;
case 2: do_16_thing(); break;
case 3: do_24_thing(); break;
case 4: do_32_thing(); break;
default:
printf("This program can't read audio files of this "
sample width %d\n", sampwidth);
exit(0);
}
|
The number of channels in an audio track is initially set by AFinitchannels() before the file is created.
AFgetchannels() returns the number of interleaved audio channels in the audio track of an opened audio file. Its function prototype is:
long AFgetchannels(AFfilehandle file, long trackid) |
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), and trackid is the ID for the audio track (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_TRACK). AFgetchannels() returns 1 if trackid is monaural, 2 if it is stereo, or any other positive integer (even for AIFF/AIFF-C files).
![]() | Tip: Your application should be able to handle audio files containing an arbitrary number of channels. For example, the application could reject a file that has more than the supported number of channels, or it could combine channels selectively or use certain channels while ignoring others. |
AFgetaeschanneldata() retrieves AES channel status information from an opened audio file. Its function prototype is:
long AFgetaeschanneldata(AFfilehandle file, long trackid,
unsigned char buf[24])
|
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), trackid is the ID for the audio track (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_TRACK), and buf is a 24-element array that receives the AES channel status bytes.
AFgetaeschanneldata() returns a 1 if there is AES channel data, or a 0 if there is no data.
![]() | Tip: There is no guarantee whether a given file format will contain AES data, so your application should call AFgetaeschanneldata() to determine whether AES channel bytes are encoded in an audio file. |
This section describes routines that let you get compression information for an audio track from an AFfilehandle structure.
When reading or writing a file (even an AIFF-C file) containing compressed data, first call AFgetsampfmt() to get the native sample format of the codec, and check that it is able to be read/written using that format. The native sample format of a codec is the sample format of the data it produces on decompression or expects on compression.
![]() | Tip: Your application should reject compressed files with native sample formats it does not support. Check for an unrecognized format rather than a defined format. The currently defined codecs all convert the compressed data to and from 16-bit AF_SAMPFMT_TWOSCOMP data, but you should not assume that a certain format is guaranteed for future codecs. For example, if you know that the file is AF_COMPRESSION_G711_ULAW, then the native format for that codec is 16-bit AF_SAMPFMT_TWOSCOMP. However, you should call AFgetsampfmt() in any case, to allow for the possibility of future codecs whose native sample format is something other than 16-bit signed integer or which have more than one native sample format (some may be configurable or may vary depending on what kind of data was originally compressed). |
AFgetcompression() and AFgetcompressionparams() return the compression type used in the audio track of an opened audio file. In addition, AFgetcompressionparams() scans a requested number of items and returns codec-specific parameters for the audio track. AFgetcompression() returns a long integer representing the compression algorithm used for the audio track's data; AFgetcompressionparams() returns this value in the compression pointer.
The function prototypes are:
long AFgetcompression(AFfilehandlefile file, long trackid)
long AFgetcompressionparams(AFfilehandle file, long trackid,
long *compression, AUpvlist pvlist, long numitems)
|
where:
| file | is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(). | |
| trackid | is the ID for the audio track (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_TRACK). | |
| compression | is a pointer to a positive long integer that will be filled in with the symbolic constant that indicates the type of audio compression being used for the specified audio track. See Table 7-7 for a list of possible return values. | |
| pvlist | is an AUpvlist structure, to be filled with parameters and values related to the compression scheme compression. Currently, the only compression schemes that have any parameters are those supplied by Aware, Inc. | |
| numitems | is the number of valid entries in the pvlist. |
Table 7-7 lists the valid return values for AIFF-C files. AIFF files always return AF_COMPRESSION_NONE.
Table 7-7. Valid Return Values for Compression Algorithms and Parameters
Parameter Value | Compression Type |
|---|---|
AF_COMPRESSION_UNKNOWN | Unrecognized compression scheme |
AF_COMPRESSION_NONE | No compression |
AF_COMPRESSION_G722 | 64 Kbps ADPCM for 16 kHz 16-bit |
AF_COMPRESSION_G711_ULAW | 64 Kbps encoding for 8 kHz 16-bit |
AF_COMPRESSION_G711_ALAW | 64 Kbps encoding for 8 kHz 16-bit |
AF_COMPRESSION_AWARE_MPEG | Aware implementation of ISO/ MPEG I-audio Layers I and II |
AF_COMPRESSION_AWARE_MULTIRATE | Aware MultiRate I lossless or near- lossless algorithm |
AF_COMPRESSION_APPLE_ACE3 | Not currently supported |
AF_COMPRESSION_APPLE_ACE8 | Not currently supported |
AF_COMPRESSION_APPLE_MAC3 | Not currently supported |
AF_COMPRESSION_APPLE_MAC6 | Not currently supported |
The Audio File Library provides built-in codec support for five compression algorithms: CCITT G.722, CCITT G.711 μ-law and A-law, and the Aware, Inc. ISO/MPEG I-audio and MultiRate I algorithms. To get more specific information about the Aware algorithms, such as MPEG I layers, see Appendix B, “Aware Scalable Audio Compression Software.”
![]() | Note: The four Apple compression algorithms listed in Table 7-7 are proprietary to Apple Computer Inc., and are not currently supported by the Audio File Library. |
AFgetcompressionname() returns a null-terminated string containing the name of the compression algorithm used for an audio track in an opened audio file. Its function prototype is:
char *AFgetcompressionname(AFfilehandle file, long trackid) |
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), and trackid is the ID for the audio track (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_TRACK).
If compression is not used, as is the case with AIFF files, AFgetcompressionname() returns a null string.
AFgetframecnt() returns the total number of sample frames in the audio track of an opened audio file. Its function prototype is:
long AFgetframecnt(AFfilehandle file, long trackid) |
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(). trackid is the ID for the audio track (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_TRACK).
AFgetframecnt() returns a long integer value that is the current total of sample frames in the track.
This section describes functions that get information about the markers in a given audio track and explains how to set the position of those markers. Markers point to positions between adjacent sample frames. For a track containing n sample frames, position 0 is before the first sample frame, and position n is after the last sample frame in the track.
AFgetmarkids() retrieves an array of marker IDs from a given audio track in an opened audio file. It returns the number of marker structures in the specified audio track. Its function prototype is:
long AFgetmarkids(AFfilehandle file, long trackid,
long markids[])
|
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), trackid is the ID for the audio track (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_TRACK), and markids is an array of long integers that receives the marker IDs for the marker structures in the audio track.
AFgetmarkids() returns a non-negative integer value specifying the number of marker structures in the given audio track.
![]() | Tip: Check for unrecognized mark return values rather than recognized values. Write your application so that it expects any number of marks and any type of mark (not just the currently defined types) and rejects files containing marks it does not support. |
Typically, you call AFgetmarkids() twice. The first time, you pass markids a null pointer and check the return value of the function. This value tells you how many locations to allocate in the markids array, which you pass back to AFgetmarkids() to obtain the list of marker IDs.
AFgetmarkname() returns the name string of a given marker within the audio track of an opened audio file. Its function prototype is:
char *AFgetmarkname(AFfilehandle file, long trackid,
long markid)
|
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), trackid is the ID for the audio track (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_TRACK), and markid is the ID of the marker whose name you want to retrieve.
AFgetmarkname() returns a null-terminated character string that is the name associated with the given markid.
AFgetmarkpos() returns the frame location of a given marker in the audio track of an opened audio file. Its function prototype is:
long AFgetmarkpos(AFfilehandle file, long trackid, long markid) |
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), trackid is the ID for the audio track (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_TRACK), and markid is the ID of the marker whose position you want to discover.
AFgetmarkpos() returns a non-negative long integer value indicating the position of the marker in the track.
AFsetmarkpos() sets the frame location of a given marker in the audio track of an audio file opened for write access. Its function prototype is:
void AFsetmarkpos(AFfilehandle file, long track, long markid,
long markpos)
|
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), trackid is the ID for the audio track (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_TRACK), markid is the ID of the marker whose position you want to move, and markpos is a non-negative long integer that describes the position to which you want to move the marker in the track.
This section describes functions that position the read pointer in a file's audio track and functions that read and write frames. You can read and seek only from a file opened for reading. Similarly, you can write frames only to a file opened for writing.
When a file is opened for read access by AFopenfile() or AFopenfd(), the logical track pointer for the audio track is initialized to point to the first frame in the track. This location can be changed by calling AFseekframe(). Before returning, AFreadframes() moves the logical track pointer so that it points to the frame following the one last copied into frames.
![]() | Caution: The logical track pointer is not the same thing as the IRIX file pointer which you position by calling the IRIX lseek(2) command. |
AFseekframe() moves the logical track pointer in the audio track of an audio file opened for read-only access to a specified frame. Its function prototype is:
long AFseekframe(AFfilehandle file, long trackid,
long offset)
|
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), trackid is the ID for the audio track (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_TRACK), and offset is the number of frames from the beginning of the track that the pointer will be moved to. This value is between 0 and the total number of frames in the track, minus 1. The total number of frames in the track can be determined by calling AFgetframecnt().
When AFseekframe() succeeds, it returns the actual offset value; otherwise, it returns a negative value.
AFreadframes() copies sample frames from an audio file opened for reading to a buffer. Its function prototype is:
long AFreadframes(AFfilehandle file, long trackid,
void *frames, long count)
|
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), trackid is the ID for the audio track (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_TRACK), frames is a pointer to a buffer into which you want to transfer copies of sample frames from file, and count is the number of sample frames you want to read from file.
AFreadframes() returns a long value indicating the number of frames successfully read from the audio track.
The data copied into frames must be interpreted according to the sample format and sample width parameter returned by AFgetsampfmt() and channel count returned by AFgetchannels(), as described in “Getting Audio Track Sample Format and Sample Width”. For AF_SAMPFMT_TWOSCOMP, AFreadframes() copies the frames to the buffer using the smallest data type (char, short, or long) that capable of holding the data. AFreadframes() automatically decompresses data encoded using any of the supported compression algorithms. (For Aware compression, an Aware license must be installed.)
![]() | Tip: Query for the sample format, sample width, and channels. Don't assume that a particular file format determines the sample format, sample width, or number of channels. Provide a mechanism for detecting and handling unsupported file configurations. |
When a file is opened for write access by AFopenfile() or AFopenfd(), the logical track pointer for the file's audio track is initialized to point to the first frame in the track. Before returning, AFwriteframes() moves the logical track pointer so that it points to the frame following the one last copied into samples.
![]() | Caution: The logical track pointer is not the same thing as the IRIX file pointer which you position by calling the IRIX lseek(2) command. |
AFwriteframes() copies frames from a buffer to an audio file opened for writing. Its function prototype is:
long AFwriteframes(const AFfilehandle file, long track,
void samples, const long count)
|
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), track is a long integer which identifies the audio track (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_TRACK), samples is a pointer to a buffer containing sample frames that you want to write to file, and count is the number of sample frames you want to write to file.
For AF_SAMPFMT_TWOSCOMP data, AFwriteframes() expects the frames to be buffered using the smallest data type (char, short, or long) capable of holding the data. AFwriteframes() automatically compresses data encoded using any of the supported compression algorithms.
AFwriteframes() returns a long value indicating the number of frames successfully written to the audio track. The return value is normally greater than or equal to 0; however, when a codec is being used and buffered data cannot be written to disk, that data is lost. In such a case, AFwriteframes() returns a negative value, indicating the number of sample frames lost.
Use the functions in this section to retrieve and manipulate instrument configuration data and parameters.
Use the functions described in this section to retrieve and set the instrument configuration parameters of an audio file. The parameters can be read from any opened audio file and written to any audio file opened as write-only.
AFgetinstids() retrieves an array of instrument IDs corresponding to the instrument chunks in a given audio file. It returns the number of instrument chunks in the file. Its function prototype is:
long AFgetinstids(AFfilehandle file, long instids[]) |
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), and instids is an array of long integer instrument IDs that reference instrument chunks within the file.
Typically, you call AFgetinstids() twice. The first time, you pass instids a null pointer and check the return value of the function. This value tells you how many locations to allocate in the instids array, which you pass back to AFgetinstids() to obtain the list of instrument IDs.
![]() | Note: The AF Library currently supports only AIFF and AIFF-C file types, so the number of instrument chunks is always either 0 or 1. If the file does contain an instrument chunk, its ID will always be AF_DEFAULT_INST for AIFF and AIFF-C files. But other instrument configurations could be returned in future releases of the AF Library. |
![]() | Tip: Write your application so that it checks for and rejects instrument configurations that you don't want to support. |
AFgetinstparamlong() retrieves a long instrument configuration parameter value from an instrument configuration in an open audio file. Its function prototype is:
long AFgetinstparamlong(AFfilehandle file, long instid,
long param)
|
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(). instid is the instrument ID for the instrument configuration chunk (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_INST). param is a symbolic constant that identifies an instrument parameter. See Table 7-3 and Table 7-8 for a list of valid parameter constants and values associated with them.
AFgetinstparamlong() returns the long integer value associated with the parameter specified in param. If instid or param is not valid, the value returned is 0.
Table 7-8 lists the instrument parameter constants and their valid values.
Table 7-8. Instrument Parameter Constants and Valid Values
Instrument Parameter Constant | Valid Values |
|---|---|
AF_INST_MIDI_BASENOTE | 0–127 |
AF_INST_NUMCENTS_DETUNE | -50 to 50 |
AF_INST_MIDI LONOTE | 0–127 |
AF_INST_MIDI_HINOTE | 0–127 |
AF_INST_MIDI_LOVELOCITY | 1–127 |
AF_INST_MIDI_HIVELOCITY | 1–127 |
AF_INST_NUMDBS_GAIN | -32768 to 32767 |
AF_INST_SUSLOOPID | Any positive long integer value |
AF_INST_RELLOOPID | Any positive long integer value |
![]() | Tip: Check for unrecognized instrument configuration and parameters rather than recognized types. Write your application so that it expects any type of instrument configuration (not just the currently defined types) and rejects files containing instruments it does not recognize. |
AFsetinstparamlong() writes a long instrument configuration parameter value to a given instrument configuration chunk in an audio file that has been opened for writing. Its function prototype is:
void AFsetinstparamlong(AFfilehandle file, long instid,
long param, long value)
|
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), instid is the instrument ID for the instrument configuration chunk (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_INST), param is a symbolic constant that identifies an instrument parameter, and value is the long integer value you want to assign to parameter named by param. See Table 7-3 and Table 7-8 for a list of valid parameter constants and values associated with them.
This section describes functions that retrieve and set the positions of instrument loops within an opened audio file. The loop information may be read from any opened audio file and written to any audio file opened as write-only. To get and set instrument loop IDs, use AFgetinstparamlong() and AFsetinstparamlong(), as described in “Reading and Writing Instrument Configurations”.
AFgetloopmode() returns the loop mode of a given loop in the instrument configuration of an opened audio file. Its function prototype is:
long AFgetloopmode(AFfilehandle file, long instid, long loopid) |
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), instid is the instrument ID for the instrument configuration chunk (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_INST), and loopid is the ID number associated with the loop whose mode you wish to read.
AFgetloopmode() returns a long integer value representing the loop mode. Current valid values for loop mode are:
AF_LOOP_MODE_NOLOOP (no loop)
AF_LOOP_MODE_FORW (forward loop)
AF_LOOP_MODE_FORWBAKW (alternating forward/backward)
AFsetloopmode() sets the loop mode of a given loop in the instrument configuration of an audio file opened as write-only. Its function prototype is:
void AFsetloopmode(AFfilehandle file, long instid,
long loopid, long mode)
|
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), instid is the instrument ID for the instrument configuration chunk (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_INST), loopid is the ID number associated with the loop whose mode you wish to write, and mode is the long integer value you wish to set for the loop mode. See AFgetloopmode() for the list of valid mode values.
AFgetloopstart() returns an audio track marker ID associated with the starting point of a given instrument loop. Its function prototype is:
long AFgetloopstart(AFfilehandle file, long instid, long loopid) |
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), instid is the instrument ID for the instrument configuration chunk (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_INST), and loopid is the ID number associated with the loop whose starting point you wish to read.
AFgetloopstart() returns a long integer value, which is a marker ID in the audio track. See “Getting and Setting Audio Track Markers” in “Reading and Writing Audio Track Information” for information on how to manipulate the position of the markers referred to by the marker IDs.
AFsetloopstart() causes an audio track marker ID to be associated with the starting point of a given instrument loop. Its function prototype is:
void AFsetloopstart(AFfilehandle file, long instid,
long loopid, long markid)
|
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), instid is the instrument ID for the instrument configuration chunk (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_INST), loopid is the ID number associated with the loop whose starting point you wish to write, and markid is the audio track marker that you wish to assign as the starting point of the given loop.
AFgetloopend() returns an audio track marker ID associated with the ending point of a given instrument loop. Its function prototype is:
long AFgetloopend(AFfilehandle file, long instid, long loopid) |
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), instid is the instrument ID for the instrument configuration chunk (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_INST), and loopid is the ID number associated with the loop whose ending point you wish to read.
AFgetloopend() returns a long integer value which is a marker ID in the audio track. See “Getting and Setting Audio Track Markers” in “Reading and Writing Audio Track Information” for information on how to manipulate the position of the markers referred to by the marker IDs.
AFsetloopend() causes an audio track marker ID to be associated with the ending point of a given instrument loop. Its function prototype is:
void AFsetloopend(AFfilehandle file, long instid,
long loopid, long markid)
|
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), instid is the instrument ID for the instrument configuration chunk (for AIFF and AIFF-C files, this value should always be AF_DEFAULT_INST), loopid is the ID number associated with the loop whose ending point you wish to write, and markid is the audio track marker that you wish to assign as the ending point of the given loop.
![]() | Tip: Loop queries can return any configuration of loops within an instrument, not just the fixed value of 2 in AIFF/AIFF-C files. Have your application check for and reject loop configurations it does not support. |
The following sections describe how to read to, write from, and get information about the miscellaneous data chunks in an audio file.
This section describes functions that get information about the number, size and type of miscellaneous data chunks in an opened audio file.
AFgetmiscids() returns the number of miscellaneous data chunks in a file and an array containing the IDs of each miscellaneous chunk. Its function prototype is:
long AFgetmiscids(AFfilehandle file, long miscids[]) |
file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(). miscids is an array of positive long integers that contains the IDs for the miscellaneous data chunks in file.
AFgetmiscids() returns a long integer value equal to the number of miscellaneous data chunks in file.
To fill the miscids array with the corresponding IDs, you first call AFgetmiscids() with a null miscids pointer, and then allocate a miscids buffer according to the return value. You can then call AFgetmiscids() again, passing the properly dimensioned miscids buffer to obtain the list of IDs.
AFgetmisctype() returns the type of a given miscellaneous chunk. Its function prototype is:
long AFgetmisctype(AFfilehandle file, long chunkid) |
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), and chunkid is a positive long integer miscellaneous chunk ID from the miscids array returned by AFgetmiscids().
AFgetmisctype() returns a long integer constant that describes the chunk type. See Table 7-5 for the list of valid chunk types and constants. If the chunk is not of any of the types listed in Table 7-5, AFgetmisctype() will return the value AF_MISC_AIFF_UNRECOGNIZED.
![]() | Tip: The set of chunk types may expand at any time. Check for unrecognized chunk types rather than recognized chunk types. Write your application so that it expects any type of MISC chunk (not just the currently defined types) and rejects miscellaneous chunks it does not recognize. |
AFgetmiscsize() returns the size of a given miscellaneous data chunk in bytes. Its function prototype is:
long AFgetmiscsize(AFfilehandle file, long chunkid) |
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), and chunkid is a positive long integer miscellaneous chunk ID from the miscids array returned by AFgetmiscids().
AFgetmiscsize() returns a long integer value that describes the size of the data in the chunk in bytes. This number does not take into account null-terminators in strings, so you will need to add one to the value returned when actually reading string data (see AFreadmisc()).
This section describes functions that read and write miscellaneous data and to position the read/write location pointer within the data portion of a miscellaneous chunk. The AFfilehandle structure maintains a logical read/write pointer for each miscellaneous data chunk in the file. Each pointer is initialized to point at the first data byte with the chunk when the AFfilehandle structure is created.
![]() | Tip: To avoid file corruption, don't copy MISC chunks from one file to another unless the content of those chunks is known. A chunk can contain references to other parts of the file that have been modified by the application, in which case attempting to copy it without properly modifying its contents would cause an error. |
AFreadmisc() reads data from a given miscellaneous chunk into a buffer, and returns the number of bytes read. Its function prototype is:
long AFreadmisc(AFfilehandle file, long chunkid,
void *buf, long nbytes)
|
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), chunkid is a positive long integer miscellaneous chunk ID from the miscids array returned by AFgetmiscids(), buf is a pointer to a buffer that will receive the data from the miscellaneous chunk, and nbytes is the number of bytes you want to read from the audio file into buf, beginning at the current position of file's logical read pointer for the data in miscid. AFreadmisc() will not read past the end of the chunk's data area. After reading the data, AFreadmisc() updates the position of the read/write pointer to point to the data byte following the last one read.
AFwritemisc() writes data from a buffer to a given miscellaneous chunk, and returns the number of bytes successfully written. Its function prototype is:
long AFwritemisc(AFfilehandle file, long chunkid,
void *buf, long nbytes)
|
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), chunkid is a positive long integer miscellaneous chunk ID from the miscids array returned by AFgetmiscids(), buf is a pointer to a buffer that contains the data you want to write to the miscellaneous chunk, and nbytes is the number of bytes you want to write to the audio file from buf, beginning at the current position of file's logical write pointer for the data in miscid. AFwritemisc() will not write past the end of the chunk's data area. After writing the data, AFreadmisc() updates the position of the read/write pointer to point to the data byte following the last one written.
It is up to the application to fill the data area of a chunk with consistent information (for example, if you don't use all the bytes you allocated in a MIDI data chunk, you need to fill the remaining bytes with no-ops).
AFseekmisc() moves the logical read/write pointer for a miscellaneous chunk to a specified offset from the beginning of the chunk's data area. Its function prototype is:
void AFseekmisc(AFfilehandle file, long chunkid, long offset) |
where file is the AFfilehandle structure previously created by a call to AFopenfile() or AFopenfd(), chunkid is a positive long integer miscellaneous chunk ID from the miscids array returned by AFgetmiscids(), offset is a non-negative long integer specifying the number of bytes past the start of the data area the read/write pointer should be moved, and offset should always be less than the size of the total data area (in bytes).
AFseekmisc() returns the new location of the logical read/write pointer, measured as the number of bytes from the beginning of the chunk data area.
This section describes important Audio File Library programming tips:
“Minimizing Data and File Format Dependence” describes how to maximize application compatibility by minimizing format dependence.
“Preventing Concurrent Access from Multiple Threads” explains how to write a multithreaded AF application in order to prevent simultaneous access to an AFfilehandle from multiple threads.
“Handling Errors in Multithreaded Applications” explains how to prevent an error handler from reporting simultaneous errors from a multithreaded application.
Currently, the Audio File Library supports the AIFF and AIFF-C file formats. As the AF Library evolves to support new file formats (beyond AIFF and AIFF-C) and new data formats (beyond 2's complement integer and compressed data formats), file-format dependent applications will require more modifications to maintain compatibility than file-format independent programs. Making your application file format independent decreases the likelihood of compatibility problems with future releases of the library and minimizes future modifications. Programming tips presented throughout this chapter call attention to methods you can use to make your application format independent.
The AF is not multithread/multiprocessor safe. Making multiple, simultaneous, uncoordinated AF calls on different AFfilehandles from different threads is possible and correct. Each AFfilehandle completely encapsulates the state (except for error handling, which is global) needed to perform operations on that AFfilehandle. In contrast, making multiple, simultaneous, uncoordinated AF calls on the same AFfilehandle from different threads is currently possible, but it is not proper programming practice.
In the following code, two threads are using one AFfilehandle:
Thread 1 | Thread2 |
|---|---|
Some amount of time | Some amount of time |
No semaphore locking | No semaphore locking |
|
|
AFseekframe(h,track,place1; | AFseekframe(h,track,place2); |
AFreadframes(h,track,...); | AFreadframes(h,track,...); |
Some amount of time | Some amount of time |
No semaphore locking | No semaphore locking |
|
|
It is possible that these calls would be executed in the following order, in which case both threads would read the wrong data.:
AFseekframe(h,track,place1); | || |
|
AFreadframes(h,track,...); | || | AFseekframe(h,track,place2) ; |
| || | AFreadframes(h,track,...); |
The only way to ensure that concurrent operations take place in the correct order is to use a process coordination facility such as semaphore locking.
Proper multithreading looks like this:
Thread 1 | Thread 2 |
|---|---|
|
|
Some amount of time | Some amount of time |
|
|
Lock Semaphore that guards h | Lock Semaphore that guards h |
AFseekframe(h,track,place1; | AFseekframe(h,track,place2); |
AFreadframes(h,track,...); | AFreadframes(h,track,...); |
Unlock Semaphore that guards h | Unlock Semaphore that guards h |
|
|
Some amount of time | Some amount of time |
|
|
IRIX guarantees that only one of the Lock Semaphore calls will succeed immediately. The thread whose lock does not succeed waits in the Lock Semaphore call (and thus does not proceed to the AFseekframe() call) until the other thread has unlocked the semaphore (after it has finished seeking and reading). When the first thread unlocks the semaphore, the thread that is waiting can now proceed.
Follow these steps to add semaphore locking to a multithreaded application:
Use usnewsema(3P) to code to create a semaphore whose value is 1.
Use uspsema(3P) to lock the semaphore.
Use usvsema(3P) to unlock the semaphore.
Example 7-3 is a code fragment that demonstrates how to create a semaphore for protecting critical regions.
Example 7-3. Creating a Semaphore
#include <ulocks.h>
AFfilehandle h; /* global file handle */
usema_t *HSema; /* global semaphore to protect h */
/* Initialize semaphore support -- do this once. */
{
usptr_t *usptr;
char *arenafile;
/* Use the fastest type (nondebugging) semaphores. */
usconfig(CONF_LOCKTYPE, US_NODEBUG);
/* Create a shared arena to hold the semaphore. */
arenafile = tmpnam(NULL);
usptr = usinit(arenafile);
/*
Create the semaphore with count 1 in that arena.
There is 1 resource (h) initially available. */
HSema = usnewsema(usptr,1);
/* No need to refer to arena again, so unlink file */
unlink(arenafile);
}
|
To use the semaphore created in Example 7-3 do this:
Thread 1 | Thread 2 |
|---|---|
|
|
Some amount of time | Some amount of time |
|
|
uspsema(HSema); /* lock */ | uspsema(HSema); /* lock */ |
AFseekframe(h,track,place1; | AFseekframe(h,track,place2); |
AFreadframes(h,track,...); | AFreadframes(h,track,...); |
usvsema(HSema); /* unlock */ | usvsema(HSema); /* unlock */ |
|
|
Some amount of time | Some amount of time |
|
|
Semaphore locking can prevent a worst-case scenario such as seeking from the second thread before the first thread has finished reading. Currently, an AF application without semaphores might not cause any problems when making simultaneous, uncoordinated AF calls on the same AFfilehandle from different threads. But this is because—by chance—the CPU scheduler timing has arranged the process timing so that both threads don't use the handle at the same time. Another time, the CPU scheduling might not be favorable, so it's best to protect the critical regions with semaphores.
In summary, you cannot make multiple, simultaneous, uncoordinated AF calls on the same AFfilehandle from different threads, even if the order of execution of those calls does not matter. Doing so is likely to cause a core dump, or at least corruption of the AFfilehandle. The application is responsible for implementing any semaphore protection that is needed; such protection is not built in to the AF calls themselves.
You cannot make multiple, simultaneous, uncoordinated AF calls from different threads that affect the library's global state—namely, the error handler function. If two threads simultaneously try to set the error handler (even if it is the same error handler), the behavior is undefined.
If you write your own error handler and then make multiple, simultaneous, uncoordinated AF calls on different file handles from different threads (and both AF calls issue an error simultaneously), then two instances of your error handler are called in a simultaneous, uncoordinated manner in both threads. If this situation is possible in your program, you should use semaphores in your error handler (in addition to the semaphores in your main program) to prevent simultaneous error reporting or handling.
Example 7-4 contains a listing of recordexample.c , in /usr/people/4Dgifts/examples/dmedia/audio/ program, which records stereo data from an audio port. If you incorporate this code in a program, use the method of rate querying shown in ratequery.c instead of the method used in recordexample.c.
Example 7-4. Recording Stereo from an Audio Port: recordexample.c
#include <stdio.h>
#include <signal.h>
#include <dmedia/audio.h>
#include <dmedia/audiofile.h>
/*
* small example program: "recordexample"
*
* record an AIFF-C file from an audio input port
* stop recording when user sends an interrupt
*
* file is configured for 16-bit stereo data at the current
* sampling rate of the audio hardware
*
* usage: "recordexample <filename>"
*/
int caught_sigint;
/*
* catch interrupt signal
*/
static void
catch_sigint()
{
caught_sigint++;
}
main(int argc, char **argv)
{
char *myname; /* name of this program */
char *portname; /* audio port name */
ALconfig portconfig; /* audio port configuration */
ALport port; /* audio port */
long portchannels; /* audio port channels */
long portrate; /* audio port sampling rate */
long portsampwidth; /* audio port sample width */
long portsampfmt; /* audio port sample format */
AFfilesetup filesetup; /* audio file setup */
AFfilehandle file; /* audio file handle */
char *filename; /* audio file name */
long filechannels; /* audio file channels */
double filerate; /* audio file sampling rate */
long filesampwidth; /* audio file sample width */
long filesampfmt; /* audio file sample format */
long pvbuf[2]; /* parameter-value buffer */
void *buf; /* sample transfer buffer */
int numframeswrit; /* number of frames written */
int done; /* flag */
int samplesperbuf; /* samples transfered per loop */
int framesperbuf; /* sample frames transfered per loop */
int samplespersec; /* samples transfered per sec */
myname = argv[0];
portname = myname;
if (argc != 2)
{
fprintf(stderr, "Usage: %s filename\n", myname);
exit(1);
}
sigset(SIGINT, catch_sigint);
filename = argv[1];
/*
* get the global IRIS Audio Processor input rate
*/
pvbuf[0] = AL_INPUT_RATE;
ALgetparams(AL_DEFAULT_DEVICE, pvbuf, 2);
portrate = pvbuf[1];
/*
* initialize the audio port and audio file configuration
*/
portchannels = AL_STEREO; /* port channels */
portsampwidth = AL_SAMPLE_16; /* port sample width */
portsampfmt = AL_SAMPFMT_TWOSCOMP; /* port sample format */
filechannels = 2; /* file channels */
filesampwidth = 16; /* file sample width */
filesampfmt = AF_SAMPFMT_TWOSCOMP; /* file sample format */
/*
* configure file sample rate to match IRIS audio processor input rate
*/
switch (portrate)
{
case AL_RATE_48000: filerate = 48000.0; break;
case AL_RATE_44100: filerate = 44100.0; break;
case AL_RATE_32000: filerate = 32000.0; break;
case AL_RATE_22050: filerate = 22050.0; break;
case AL_RATE_16000: filerate = 16000.0; break;
case AL_RATE_11025: filerate = 11025.0; break;
default:
case AL_RATE_8000: filerate = 8000.0; break;
}
/*
* compute the number of input samples equal to half a
* second and allocate a transfer buffer
*/
samplespersec = ((long)filerate) * 2; /* stereo */
samplesperbuf = samplespersec / 2; /* half second buffer */
framesperbuf = samplesperbuf / 2; /* stereo */
buf = (short *)malloc(samplesperbuf * sizeof(short));
/*
* open the audio port
*/
portconfig = ALnewconfig();
ALsetchannels(portconfig, portchannels);
ALsetwidth(portconfig, portsampwidth);
ALsetqueuesize(portconfig, samplesperbuf);
port = ALopenport(portname, "r", portconfig);
/*
* configure an audio file
*/
filesetup = AFnewfilesetup();
AFinitfilefmt(filesetup, AF_FILE_AIFFC);
AFinitchannels(filesetup, AF_DEFAULT_TRACK, filechannels);
AFinitrate(filesetup, AF_DEFAULT_TRACK, filerate);
AFinitsampfmt(filesetup, AF_DEFAULT_TRACK,
AF_SAMPFMT_TWOSCOMP, filesampwidth); /*in bits */
/*
* open the audio file
*/
file = AFopenfile(filename, "w", filesetup);
/*
* play the buffer
*/
done = 0;
caught_sigint = 0;
while (!done && !caught_sigint)
{
ALreadsamps(port, buf, samplesperbuf);
if ((numframeswrit
= AFwriteframes(file, AF_DEFAULT_TRACK,
buf, framesperbuf)) < framesperbuf)
{
done++;
}
}
AFclosefile(file); /* this is important: it updates the file header */
ALcloseport(port);
exit(0);
}
|