Chapter 7. Digital Media Audio File Operations
Prev |  | Next

## Chapter 7. Digital Media Audio File Operations

The Audio File (AF) Library provides a uniform programming interface for reading and writing audio disk files. It also allows applications to control the format of audio data buffers. The AF Library is implemented as a Dynamic Shared Object (DSO) which enables properly written applications to use the latest version of the library without recompiling and relinking. See “Querying the AF Library” for a further discussion.

The Audio File Library comprises routines that handle these fundamental tasks:

- Querying the AF Library

- Creating and Configuring Audio Files

- Opening, Closing, and Identifying Audio Files

- Reading and Writing Audio Track Information

This chapter covers these topics, explains basic audio file concepts, and provides programming tips for the AF Library.

## About the Audio File Library

This section explains concepts fundamental to understanding and using the Audio File Library properly.

### Audio File Library Programming Model

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:

1. Initialize an AFfilesetup, by calling afNewFileSetup().

2. Configure the AFfilesetup for your data.

3. Open an audio file for reading or writing by calling either `afOpenFile()` or `afOpenFD()`. These routines return an AFfilehandle whose data configuration matches the settings in the AFfilesetup.

   | Note: 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.

### About Audio Files

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:

Audio file format | Allows applications to identify audio file formats and format versions.
Audio tracks | Contain audio sample data, parameters that characterize the data format (such as sample rate, channel configuration, and compression type), and marker structures that store sample frame locations in the track for looping and other purposes.
Instrument configurations | Contain instrument parameters for configuring digital samples when playing back audio track data, and loop markers for repeating tracks or portions of a track.
Miscellaneous data | Include text strings (author, copyright, name, annotation, and so on) and other non-audio information (such as MIDI data and application-specific data).

The two portions of an audio file you will make most use of are audio tracks and instrument configurations.

#### Audio File Formats

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, Sample Frames, and Track Markers

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 an integer ID number and (optionally) a name string, point to locations between sample frames.

#### Audio Track Format Parameters

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.

#### Instrument Configurations and Loops

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.

#### AIFF-C and the AF Library API

Silicon Graphics has adopted AIFF-C as its default 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 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.

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.

### Virtual Data Format

The Audio File Library allows the format of audio data in an application's buffer to be independent of that being read from or written to disk audio files. The format of the audio data in the buffer is called the data's virtual format. Once the virtual data format is set with the routines described in “Getting and Setting the Virtual Audio Format,” conversion between the disk file format and the virtual format happens automatically. An application can thus specify the virtual format to be identical to the final format it wants the buffer data to be in, and ignore the original disk file format entirely.

The default virtual format of the data that is loaded by `afReadFrames()` into the application's data buffer is identical to the disk format with two important exceptions:

- the virtual byte order default is DM_AUDIO_BIG_ENDIAN

- the buffer data is always DM_AUDIO_UNCOMPRESSED

These exceptions were made to assure backwards compatibility with the first version of the AF Library. It is possible to use `afSetVirtualByteOrder()` to set the virtual byte order to DM_AUDIO_LITTLE_ENDIAN, but the virtual compression cannot be changed.

### PCM Mapping

PCM mapping enables an application to specify the numerical mapping when it converts data from an integer format to a floating point format or vice versa. For example, an application may need to read files of 8-, 16-, 24-, or 32-bit integer data, which may be signed or compressed into a buffer as floating point data. Similarly, the application may want to write floating point data in buffers to a file and have the data converted to unsigned integers in real time.

In the latter case, the application probably expects the floating point (float) data to have values within a certain range, perhaps -1.0 to 1.0 or 0.0 to 1.0. If the float-to-integer conversion is specified by only a slope value, it is impossible to achieve some mappings because the intercept would then be a fixed value. Therefore the intercept must be specifiable. That way float data which ranges from [-1.0, 1.0], for example, can be mapped to the range [0, 65535]. However, unlike the floating point range, the unsigned integer range is not symmetric. Thus, minimum and maximum clipping values (minclip and maxclip) to which the AF Library clips all PCM values, are necessary to allow the application to be more specific about how the endpoints of the mapping line up.

This model assumes there exists one PCM value which corresponds to zero volts, and a differential PCM value which produces a full-voltage value when added to or subtracted from the zero volt value. The idea of voltage is a canonical form and does not correspond to any hardware. It does not matter what the full-voltage value is. To sum up:

slope |  | the full-voltage differential PCM value
intercept |  | the zero-volt PCM value
minclip |  | the minimum permitted PCM value
maxclip |  | the maximum permitted PCM value

### Querying the AF Library

Your application can query the AF Library about features such as instrument parameters, file formats, and compression algorithms. The query functions return information about these features such as their names, descriptions, labels, default values, ID counts, and implementation status. See the reference page `afQuery(3dm)` for a complete list query parameters.

There are four query routines as shown in Table 7-2. Which routine to use depends on the data type of the parameter being asked about. For example, to retrieve a character string, use `afQueryPointer()` and cast the return value to (char *).

    void* afQueryPointer ( int querytype, int arg1, int arg2,
                           int arg3, int arg4 )

The parameters to the query functions vary with the type of query. The first parameter, querytype, is a query type such as AF_QUERYTYPE_COMPRESSION or AF_QUERYTYPE_FILEFMT. arg1 is the first sub-selector, such as AF_QUERY_TYPE or AF_QUERY_NAME. arg2 can be either an additional sub-selector, such as AF_QUERY_DEFAULT, or the target variable being returned by the query, such as the file format or the compression type. The target of the query will always be the final non-zero parameter to `afQueryPointer()` and its relatives. All subsequent parameters must be set to 0.

Example 7-1. Creating a List of Supported Compression Type IDs

    long          numCompressionTypes;
    int           *compressionIDs = NULL;
    AFfilehandle  handle     = afOpenFile ( "somefile.aifc", "r", NULL );
    int           fileformat = afGetFileFormat ( handle, NULL );

    /* Get the total number of compression types.*/
    numCompressionTypes = afQueryLong ( AF_QUERYTYPE_FILEFMT,
                                        AF_QUERY_COMPRESSION_TYPES,
                                        AFQUERY_VALUE_COUNT,
                                        fileformat, 0 );

    /* If the total is not zero, retrieve the array of IDs */
    if( numCompressionTypes > 0 ) {
        compressionIDs = (int *) afQueryPointer ( AF_QUERYTYPE_FILEFMT,
                                                AF_QUERY_COMPRESSION_TYPES,
                                                AF_QUERY_VALUES,
                                                fileformat, 0 );
        /* Use the IDs here. */
    }

    /* When finished, free the array memory. */
    if( compressionIDs != NULL )
        free ( compressionIDs );

### Summary of the Query Functions

Table 7-2 is a summary of the Audio File Library query functions. Functions that are not covered in the preceding text have a notation in the Description column. Please refer to a function's reference page if a more detailed explanation is needed. For example, the reference page for`afQuery()` is `afQuery(3dm)`.

Table 7-2. Audio File Library Query Functions

Function | Description
--- | ---
AUpvlist `afQuery` ( int querytype, int arg1, int arg2, int arg3, int arg4 ) | Retrieve an AUpvlist static parameter associated with the Audio File Library formats. Not covered in text.
double `afQueryDouble` ( int querytype, int arg1, int arg2, int arg3, int arg4 ) | Retrieve a double static parameter associated with the Audio File Library formats. Not covered in text.
long `afQueryLong` ( int querytype, int arg1, int arg2, int arg3, int arg4 ) | Retrieve a long static parameter associated with the Audio File Library formats. Not covered in text.
void* `afQueryPointer` ( int querytype, int arg1, int arg2, int arg3, int arg4 ) | Retrieve a void* static parameter associated with the Audio File Library formats.

## Creating and Configuring Audio Files

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.

### Creating an Audio File Setup

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-3 lists the AFfilesetup configuration parameters and their defaults.

Table 7-3. AFfilesetup Parameters and Defaults

Parameter | Default
--- | ---
File format | AF_FILE_AIFFC
Audio track | AF_DEFAULT_TRACK
Audio track sample format, sample width | AF_SAMPFMT_TWOSCOMP, 16-bit
Audio track channels (interleaved) | 2 (stereo)
Audio track compression | AF_COMPRESSION_NONE
Audio track markers | Four markers with IDs: 1,2,3,4
Instrument | AF_DEFAULT_INST
Instrument Parameters | (See Table 7-7)
Loops | Two loops with IDs: 1, 2; default mode is AF_LOOP_MODE_NOLOOP

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.

### Initializing the Audio File Format

You need to set the file format in an AFfilesetup structure before passing the structure to afOpenFile().

afInitFileFormat() configures the file format parameter in an AFfilesetup structure. Its function prototype is:

    void afInitFileFormat ( AFfilesetup setup, int filefmt )

where setup is the AFfilesetup structure, and filefmt is an integer constant which specifies an audio format supported by the AF Library. A new audio file that is opened by calling afOpenFile() with this AFfilesetup as an argument will then be formatted accordingly. Valid format types are shown below.

AF_FILE_AIFFC | Extended Audio Interchange File Format (AIFF-C)
AF_FILE_AIFF | Audio Interchange File Format (AIFF)
AF_FILE_NEXTSND | NeXT .snd and Sun .au
AF_FILE_WAVE | Waveform Audio File Format (RIFF)
AF_FILE_BICSF | Berkeley/IRCAM/CARL Sound
AF_FILE_MPEG1BITSTREAM | MPEG-1 audio bitstream encoded data
AF_FILE_SOUNDESIGNER2 | Digidesign Sound Designer II
AF_FILE_AVR | Audio Visual Research
AF_FILE_IFF_8SVX | Amiga IFF/8SVX
AF_FILE_VOC | Creative Labs VOC
AF_FILE_SAMPLEVISION | Sample Vision
AF_FILE_SOUNDFONT2 | Creative Labs E-mu Systems SoundFont2
AF_FILE_RAWDATA | Headerless data (for raw read-access mode only)

### Initializing Audio Track Data

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 track argument, which identifies an audio track in the AFfilesetup structure being initialized. In the current release of the AF Library, the value of track must always be AF_DEFAULT_TRACK because all supported file formats contain only one audio track.

#### Initializing the Audio Track Format

Use a DMparams structure and the function `afInitFormatParams()` to initialize a specified audio track's data format in an AFfilesetup structure.

    DMstatus afInitFormatParams ( AFfilesetup setup, int track,
                                  DMparams *params )

The parameter setup is an AFfilesetup structure previously created by a call to `afNewFileSetup()`. The track parameter is an integer identifying an audio track in setup. Finally, params is a DMparams list previously created by a call to `dmParamsCreate()`. (See “Creating and Destroying DMparams Lists” in Chapter 3 for more information on creating DmParams lists.) If successful, `afInitFormatParams()` returns DM_SUCCESS, otherwise it returns DM_FAILURE.

The `afInitFormatParams()` function initializes all the parameters associated with an audio track's data in an AFfilesetup structure. The parameters and associated values are shown below.

DM_AUDIO_FORMAT | Sample format: DM_AUDIO_DOUBLE, DM_AUDIO_UNSIGNED, DM_AUDIO_TWOSCOMP, or DM_AUDIO_FLOAT
DM_AUDIO_WIDTH | Width in bits for integer sample formats. Integer value between 1 and 32, inclusive.
DM_AUDIO_CHANNELS | Channel count. Integer value greater than or equal to 1.
DM_AUDIO_RATE | Sampling rate. A double-precision floating point positive value.
DM_AUDIO_COMPRESSION | Compression type: DM_AUDIO_GSM, DM_AUDIO_UNCOMPRESSED, DM_AUDIO_G711_ULAW, DM_AUDIO_G711_ALAW, DM_AUDIO_G722, DM_AUDIO_G726, DM_AUDIO_G728, others
DM_AUDIO_PCM_MAP_SLOPE | Slope value for PCM mapping. See `afGetVirtualPCMMapping(3dm)`.
DM_AUDIO_PCM_MAP_INTERCEPT | Intercept value for PCM mapping.
DM_AUDIO_PCM_MAP_MAXCLIP | Maximum clipping for PCM mapping.
DM_AUDIO_PCM_MAP_MINCLIP | Minimum clipping for PCM mapping.

| Note: `afInitFormatParams()` replaces afInitRate(), afInitSampleFormat(), afInitChannels(), afInitCompression(), and afInitCompressionParams().

#### Initializing AES Data

Audio Engineering Society (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.

afInitAESChannelDataTo() 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 afInitAESChannelDataTo ( AFfilesetup setup, int track,
                                  int usedata )

where setup is the AFfilesetup structure, and track is an integer that identifies an audio track in setup. The usedata parameter is a flag indicating whether AES data will be stored in the file.

| Note: afInitAESChannelDataTo() replaces afInitAESChannelData().

afSetAESChannelData() sets the values of the AES channel status bytes. Its function prototype is:

    void afSetAESChannelData ( AFfilehandle file, int track,
                               unsigned char buf[24])

where file is the AFfilehandle structure previously created by a call to afOpenFile() or afOpenFD(), track is the ID for the audio track and 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 afInitAESChannelDataTo() before creating the file), afSetAESChannelData() ignores the data and returns without error.

#### Initializing Audio Track Markers

Audio track marker structures store sample frame locations in the track for looping and other purposes. Markers are identified by an 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 afSetMarkPosition(). 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, int trackID, int markids[],
                         int nmarks )

where setup is the AFfilesetup structure, trackID is an integer that identifies an audio track in setup, markids is an array of unique positive integers that will be used as handles for the marker structures in the file opened with setup, nmarks is an 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.

afInitMarkName() specifies a name string for a marker structure. Marker names default to empty strings. Its function prototype is:

    void afInitMarkName ( AFfilesetup setup, int track, int markid,
                          const char *namestr )

where setup is the AFfilesetup structure, track is an integer that identifies an audio track in setup, markid is a positive integer that identifies a marker structure configured previously by afInitMarkIDs(), namestr is a constant string that will be written into the marker structure when an audio file is created by passing setup to afOpenFile().

### Initializing Instrument Data

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, int instids[], int ninsts )

where setup is the AFfilesetup structure, instids is an array of positive integers that are used as handles for the instrument configurations in an audio file, and ninsts is the number of entries in instids.

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, int inst, int loopids[],
                         int nloops )

where setup is the AFfilesetup structure, inst is an integer that identifies an instrument configuration in an audio track. In the current release of the AF Library, the value of inst should always be AF_DEFAULT_INST. loopids is an array of unique, positive integers that will identify individual loops within an audio file opened using setup. nloops is a 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.

### Initializing Miscellaneous Data

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, int miscids[], int nmisc )

where setup is the AFfilesetup structure, miscids is an array of unique, positive 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. Its function prototype is:

    void afInitMiscType ( AFfilesetup setup, int miscid, int type )

where setup is the AFfilesetup structure, miscid is a positive integer that identifies a miscellaneous chunk in setup, and type is an integer constant that defines the chunk type.

Table 7-4 lists valid parameters for each chunk type.

Table 7-4. Miscellaneous Chunk Types and Parameter Values

Parameter Value | Miscellaneous Chunk Type
--- | ---
AF_MISC_ANNO | Annotation string.
AF_MISC_APPL | Application-specific data.
AF_MISC_AUTH | Author string.
AF_MISC_COPY | Copyright string.
AF_MISC_MIDI | MIDI data.
AF_MISC_NAME | Name string.
AF_MISC_PCMMAP | PCM mapping information.
AF_MISC_NeXT | NeXT file info chunk.
AF_MISC_IRCAM_PEAKAMP | BICSF peak amplitude sfcode.
AF_MISC_COMMENT | Text comment string. The tags AF_MISC_IRCAM_COMMENT and AF_MISC_ICMT are also allowed.
AF_MISC_ICRD | Creation date string. This is usually of the form “YYYY-MM-DD”.
AF_MISC_ISFT | Software name string. Usually set to the name of the software package which created the sound.
AF_MISC_UNRECOGNIZED | Unrecognized data chunk.

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. 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, int miscid, int size )

where setup is the AFfilesetup structure, miscid is a positive integer that identifies a miscellaneous chunk in setup, and size is a non-negative 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.

### Initializing PCM Mapping

The function `afInitPCMMapping()` configures the PCM mapping for a specified audio track in an AFfilesetup structure. Its prototype is:

    void afInitPCMMapping ( AFfilesetup setup, int track,
                            double slope, double intercept,
                            double minclip, double maxclip )

The setup parameter is an AFfilesetup structure, previously created by a call to `afNewFileSetup()`, which is passed to `afOpenFile()` when a new audio file is created. The parameter track is an integer which identifies an audio track in setup, and should always have a value of AF_DEFAULT_TRACK. slope is a positive double-precision floating point value that specifies an amplitude scaling factor for the waveform associated with track. The parameter intercept is a double-precision floating point value which specifies the sample value which represents the vertical midpoint (zero crossing) of the waveform associated with track. Finally, minclip and maxclip are double-precision floating point values specifying the maximum desired negative and positive amplitudes for the waveform. Any values encountered outside the range [minclip, maxclip] are clipped to these values.

To configure setup for a floating point waveform with a total expected amplitude of 100.0 (that is, values between -100.0 and 100.0), a symmetrical clipping to match, and a 0.0 zero crossing value, the track is initialized with as follows:

    afInitPCMMapping ( setup, AF_DEFAULT_TRACK, 100.0, 0.0, -100.0, 100.0 );

PCM mapping is useful to modify frames only as they are read into or written from buffers with `afReadFrames()` or `afWriteFrames()` respectively. None of the currently supported file formats can store the PCM mapping information, even though the mapping can be applied to the frames stored in those files. Therefore, it is important to specify mapping values carefully. In general, all two's complement and floating point sample formats are expected to be symmetrical about zero. The intercept value must be 0.0, and minclip and maxclip must be negative and positive n, where n is a non-zero positive value.

### Summary of the Creation and Configuration Functions

The following table is a summary of the Audio File Library creation and configuration functions. Functions that are not covered in the preceding text have a notation in the Description column. Please refer to a function's reference page if a more detailed explanation is needed. For example, the reference page for`afInitTrackIDs()` is `afInitTrackIDs(3dm)`.

Table 7-5. Audio File Creation and Configuration Functions

Function | Description
--- | ---
AFfilesetup `afNewFileSetup` ( void ) | Create and initialize an AFfilesetup structure.
void `afFreeFileSetup` ( AFfilesetup setup ) | Deallocate an AFfilesetup structure.
void `afInitFileFormat` ( AFfilesetup setup, int filefmt ) | Initialize the audio file format type in an AFfilesetup structure.
DMstatus `afInitFormatParams` ( AFfilesetup setup, int track, DMparams *params ) | Use a DMparams structure to initialize the audio data format in an AFfilesetup structure for a specified audio track.
void `afInitRate` ( AFfilesetup setup, int track, double rate ) | Obsolete. See `afInitFormatParams()`.
void `afInitSampleFormat` ( AFfilesetup setup, int track, int sampfmt, int sampwidth ) | Obsolete. See `afInitFormatParams()`.
void `afInitChannels` ( AFfilesetup setup, int track, int channels ) | Obsolete. See `afInitFormatParams()`.
void `afInitCompression` ( AFfilesetup setup, int track, int compression ) | Obsolete. See `afInitFormatParams()`.
void `afInitCompressionParams` ( AFfilesetup setup, int track, int compression, AUpvlist pvlist, int numitems ) | Obsolete. See `afInitFormatParams()`.
void `afInitByteOrder`( AFfilesetup setup, int track, int byteorder ) | Initialize the byte order data format in an AFfilesetup structure for a specified audio track. Not covered in text.
void `afInitAESChannelDataTo` ( AFfilesetup setup, int track, int usedata ) | Set a flag in an AFfilesetup structure to reserve or remove storage space for AES channel status data in a file.
void `afInitAESChannelData` ( AFfilesetup setup, int track ) | Obsolete. See `afInitAESChannelDataTo()`.
void `afInitMarkIDs` ( AFfilesetup setup, int trackID, int markids[], int nmarks ) | Specify a list of marker IDs for a new audio file in an AFfilesetup structure.
void `afInitMarkName` ( AFfilesetup setup, int track, int markid, const char *namestr ) | Initialize the name for a specified marker in an AFfilesetup configuration structure.
void `afInitMarkComment` ( AFfilesetup setup, int track, int markid, const char *commstr ) | Initialize the comment for a specified marker in an AFfilesetup configuration structure. Not covered in text.
void `afInitInstIDs` ( AFfilesetup setup, int instids[], int ninsts ) | Specify a list of instrument parameter chunk identifiers to be stored in an AFfilesetup configuration structure.
void `afInitLoopIDs` ( AFfilesetup setup, int inst, int loopids[], int nloops ) | Initialize a list of loop IDs for a given instrument in an AFfilesetup structure.
void `afInitMiscSize` ( AFfilesetup setup, int chunkid, int size ) | Initialize the number of data bytes for a given miscellaneous chunk in an AFfilesetup structure.
void `afInitMiscIDs` ( AFfilesetup setup, int miscids[], int nmisc ) | Initialize the list of miscellaneous data chunk IDs in an AFfilesetup file configuration structure.
void `afInitMiscType` ( AFfilesetup setup, int chunkid, int type ) | Initialize the chunk type for a given miscellaneous chunk in an AFfilesetup structure.
void `afInitTrackIDs` ( AFfilesetup setup, int trackids[], int ntracks ) | Initialize the list of audio track identifiers in an AFfilesetup structure. Not covered in text.
void `afInitPCMMapping` ( AFfilesetup setup, int track, double slope, double intercept, double minclip, double maxclip ) | Configure the PCM mapping for an audio track in an AFfilesetup structure.
void `afInitDataOffset` ( AFfilesetup setup, int track, AFfileoffset offset ) | Initialize the audio data byte offset in an AFfilesetup for a specified raw-format audio track. Not covered in text.
void `afInitFrameCount` ( AFfilesetup setup, int track, AFframecount count ) | Initialize the audio frame count in an AFfilesetup for a specified raw-format audio track. Not covered in text.

## Opening, Closing, and Identifying Audio Files

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 for 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.

### Opening an Audio File

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 ( const char *name, const 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.

### Getting an IRIX File Descriptor for an Audio File

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.

### Getting Audio File Format

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.

afGetFileFormat() returns an integer value indicating the format of the file and returns a separate version number for AIFF-C files. Its function prototype is:

    int afGetFileFormat ( AFfilehandle file, int *vers )

where file is the AFfilehandle structure previously created by a call to afOpenFile() or afOpenFD(), and vers is used to return a file format version number in the form of a non-negative integer. AIFF files do not use version numbers, so a value of 0 is always returned as the AIFF version number.

afGetFileFormat() returns a non-negative integer indicating the format of the file. For a list of these format see `afInitFileFormat()` under “Initializing the Audio File Format.”

afIdentifyFD() returns the file format of a given IRIX file descriptor. Its function prototype is:

    int afIdentifyFD ( int fd )

where fd is an IRIX file descriptor previously returned by open().

afIdentifyFD() returns a integer value representing the audio file format (see afGetFileFormat() 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 explicitly known, 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 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 `afGetFileFormat()` to get the file format.

### Closing and Updating Files

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:

    int 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:

    int 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.

### Summary of the Opening, Closing, and Identifying Functions

The following table is a summary of the Audio File Library functions for opening, closing, and identifying files. Functions that are not covered in the preceding text have a notation in the Description column. Please refer to a function's reference page if a more detailed explanation is needed. For example, the reference page for`afSaveFilePosition()` is `afSaveFilePosition(3dm)`

Table 7-6. Audio File Opening, Closing and Identifying Functions

Function | Description
--- | ---
AFfilehandle `afOpenFile` ( const char *name, const char *mode, AFfilesetup setup ) | Allocate and initialize an AFfilehandle structure for an audio file identified by name.
AFfilehandle `afOpenFD` ( int fd, const char *mode, AFfilesetup setup ) | Allocate and initialize an AFfilehandle structure for an audio file identified by a Unix file descriptor.
AFfilehandle `afOpenNamedFD` ( int fd, const char *mode, AFfilesetup setup, const char *name ) | Use instead of `afOpenFD()` for Sound Designer II files. Not covered in text.
int `afIdentifyFD` ( int fd ) | Retrieve the audio file format associated with a file descriptor.
int `afIdentifyNamedFD` ( int fd, const char* filename, int* implemented ) | Use instead of `afIdentifyFD()` for Sound Designer II files. Not covered in text.
int `afGetFD` ( AFfilehandle file ) | Get the UNIX file descriptor for the file associated with an AFfilehandle structure.
int `afGetFileFormat` ( AFfilehandle file, int *vers ) | Retrieve the audio file format associated with an open AFfilehandle.
int `afSyncFile` ( AFfilehandle file ) | Write out a snapshot of an audio file without closing the file.
void `afSaveFilePosition` ( AFfilehandle file ) | Save a logical audio sample read pointer to allow UNIX operations. Not covered in text.
void `afRestoreFilePosition` ( AFfilehandle file ) | Retrieve a logical audio sample read pointer after UNIX operations. Not covered in text.
int `afCloseFile` ( AFfilehandle file ) | Close an audio file; update the file header if the file was opened for write access.

## Reading and Writing Audio Track Information

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.

### Getting and Setting Audio Parameters

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.

#### Getting the Audio Track Format

The function `afGetFormatParams()` uses a DMparams structure to get the audio data format in an AFfilehandle for a specified audio track.

    DMstatus afGetFormatParams ( AFfilehandle file, int track,
                                 DMparams *params )

The parameter file is an AFfilehandle structure, previously created by a call to `afOpenFile()` or `afOpenFD()`. The track parameter is an integer which identifies an audio track in file, and should be set to AF_DEFAULT_TRACK. Finally, params is a DMparams list previously created by a call to `dmParamsCreate()`.

`afGetFormatParams()` retrieves all the parameters associated with an audio track's data in an AFfilehandle structure. For a table of these parameters see `afInitFormatParams()` under “Initializing the Audio File Format.”

| Note: `afGetFormatParams()` replaces the individual routines `afGetSampleFormat()`, `afGetChannels()`, `afGetRate()`, `afGetCompression()`, and `afGetCompressionParams()`.

#### Getting and Setting the Virtual Audio Format

The functions `afGetVirtualFormatParams()` and `afSetVirtualFormatParams()` use a DMparams structure to get and the virtual audio data format in an AFfilehandle for a specified audio track.

    DMstatus afGetFormatParams ( AFfilehandle file, int track,
                                 DMparams *params )
    DMstatus afSetFormatParams ( AFfilehandle file, int track,
                                 DMparams *params )

The parameter file is an AFfilehandle structure, previously created by a call to `afOpenFile()` or `afOpenFD()`. The track parameter is an integer which identifies an audio track in file, and should be set to AF_DEFAULT_TRACK. Finally, params is a DMparams list previously created by a call to `dmParamsCreate()`.

`afGetVirtualFormatParams()` retrieves and `afSetVirtualFormatParams()` sets all the parameters associated with an audio track's virtual data in an AFfilehandle structure. For a table of these parameters see the function `afInitFormatParams()` under “Initializing the Audio File Format.”

| Note: `afSetVirtualFormatParams()` replaces the routines `afSetVirtualSampleFormat()`, `afSetVirtualChannels()`, `afSetVirtualRate()`, and `afSetVirtualPCMMapping()`.

#### Getting AES Data

afGetAESChannelData() retrieves AES channel status information from an opened audio file. Its function prototype is:

    int afGetAESChannelData ( AFfilehandle file, int track,
                              unsigned char buf[24] )

where file is the AFfilehandle structure previously created by a call to afOpenFile() or afOpenFD(), track is the ID for the audio track and 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.

#### Getting Audio Track Sample Frame Count

afGetFrameCount() returns the total number of sample frames in the audio track of an opened audio file. Its function prototype is:

    AFframecount afGetFrameCount ( AFfilehandle file, int track )

where file is the AFfilehandle structure previously created by a call to afOpenFile() or afOpenFD(). track is the ID for the audio track and is always AF_DEFAULT_TRACK.

afGetFrameCount() returns an AFramecount value that is the current total of sample frames in the track.

#### Getting and Setting Audio Track Markers

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:

    int afGetMarkIDs ( AFfilehandle file, int trackID, int *markids )

where file is the AFfilehandle structure previously created by a call to afOpenFile() or afOpenFD(), trackID is the ID for the audio track and should always be AF_DEFAULT_TRACK, and markids is an array of 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, int trackID, int markid )

where file is an AFfilehandle structure previously created by a call to afOpenFile() or afOpenFD(), trackID is the ID of the audio track and is always AF_DEFAULT_TRACK. 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.

afGetMarkPosition() returns the frame location of a given marker in the audio track of an opened audio file. Its function prototype is:

    AFframecount afGetMarkPosition ( AFfilehandle file, int trackID,
                                     int markid )

where file is the AFfilehandle structure previously created by a call to afOpenFile() or afOpenFD(), trackID is the ID for the audio track and is always AF_DEFAULT_TRACK. markid is the ID of the marker whose position you want to discover.

afGetMarkPosition() returns a non-negative AFramecount value indicating the position of the marker in the track.

afSetMarkPosition() 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 afSetMarkPosition ( AFfilehandle file, int trackID, int markid,
                             AFframecount markpos )

where file is the AFfilehandle structure previously created by a call to afOpenFile() or afOpenFD(), trackID is the ID for the audio track and should always be AF_DEFAULT_TRACK, markid is the ID of the marker whose position you want to move, and markpos describes the position to which you want to move the marker in the track.

### Seeking, Reading, and Writing Audio Track Frames

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.

#### Seeking to a Position in an Audio File Track

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:

    AFframecount afSeekFrame ( const AFfilehandle file, int track,
                               AFframecount frameoffset )

where file is a AFfilehandle structure created by a call to afOpenFile() or afOpenFD(), track is the audio track ID and should always be AF_DEFAULT_TRACK, frameoffset 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 afGetFrameCount().

When afSeekFrame() succeeds, it returns the actual offset value; otherwise, it returns a negative value.

#### Reading Audio Frames from an Audio Track

afReadFrames() copies sample frames from an audio file opened for reading to a buffer. Its function prototype is:

    int afReadFrames ( const AFfilehandle file, int track,
                       void *samples, const int count )

where file is the AFfilehandle structure previously created by a call to afOpenFile() or afOpenFD(), track is the ID for the audio track and should always be AF_DEFAULT_TRACK, samples 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 an integer value indicating the number of frames successfully read from the audio track.

Data copied into frames must be interpreted using the sample format, sample width and channel count parameters returned by afGetFormatParams(). For AF_SAMPFMT_TWOSCOMP, afReadFrames() copies the frames to the buffer using the smallest data type (char, short, or int) that will hold the data, and automatically decompresses data encoded with any of the supported compression algorithms.

| 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.

#### Writing Audio Frames to an Audio Track

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:

    int afWriteFrames ( const AFfilehandle file, int track,
                        void* samples, const int count )

file is the AFfilehandle structure created by a call to afOpenFile() or afOpenFD(), track is an integer that identifies the audio track and 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 int) capable of holding the data. afWriteFrames() automatically compresses data encoded using any of the supported compression algorithms.

afWriteFrames() returns an integer 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.

### Reading and Writing Instrument Configurations

Use the functions in this section to retrieve and manipulate instrument configuration data and parameters.

#### Getting and Setting Instrument 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:

    int afGetInstIDs ( AFfilehandle file, int *instids )

where file is the AFfilehandle structure previously created by a call to afOpenFile() or afOpenFD(), and instids is a pointer to the first member of an array of 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.

| 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, int instid, int 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-7 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-7 lists the instrument parameter constants and their valid values.

Table 7-7. Instrument Parameter Constants and Valid Values

Instrument Parameter Constant | Valid Values [Default]
--- | ---
AF_INST_MIDI_BASENOTE | MIDI base note for sample: 0-127. [60]
AF_INST_NUMCENTS_DETUNE | MIDI detune in cents: varies with file format (-50 to 50 for AIFC). [0]
AF_INST_MIDI LONOTE | Lowest MIDI note for sample: 0–127. [0]
AF_INST_MIDI_HINOTE | Highest MIDI note for sample: 0–127. [127]
AF_INST_MIDI_LOVELOCITY | Lowest MIDI velocity for sample: 1-127. [1]
AF_INST_MIDI_HIVELOCITY | Highest MIDI velocity for sample: 1–127. [127]
AF_INST_NUMDBS_GAIN | Gain in dB's for sample: -32768 to 32767. [0]
AF_INST_SUSLOOPID | ID for sustain loop (AIFF / AIFF-C only). [1]
AF_INST_RELLOOPID | ID for release loop (AIFF and AIFF-C only). [2]
AF_INST_SAMP_STARTFRAME | Inst's starting frame of sample: 0 or greater.
AF_INST_SAMP_ENDFRAME | Inst's ending frame of sample: 0 or greater.
AF_INST_SAMP_MODE | Inst's sample looping mode. If present, will be one of: AF_INST_LOOP_OFF, AF_INST_LOOP_CONTINUOUS, AF_INST_LOOP_SUSTAIN.
AF_INST_TRACKID | Track ID for inst sample data: AF_DEFAULT_TRACK.
AF_INST_NAME | Name string for instrument configuration. Type AU_PVTYPE_PTR.
AF_INST_SAMP_RATE | Sample rate for sample associated with the inst. Type AU_PVTYPE_DOUBLE.

| 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, int instid, int 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-7 for a list of valid parameter constants and values associated with them.

#### Getting and Setting Loop Information

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:

    int afGetLoopMode ( AFfilehandle file, int instid, int 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 an 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, int instid, int loopid,
                         int 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 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:

    int afGetLoopStart ( AFfilehandle file, int instid, int 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 an 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, int instid, int loopid,
                          int 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:

    int afGetLoopEnd ( AFfilehandle file, int instid, int 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 an 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, int instid, int loopid,
                        int 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.

### Handling Miscellaneous Data Chunks

The following sections describe how to read to, write from, and get information about the miscellaneous data chunks in an audio file.

#### Getting Miscellaneous Data Parameters

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:

    int afGetMiscIDs ( AFfilehandle file, int miscids[] )

file is the AFfilehandle structure previously created by a call to afOpenFile() or afOpenFD(). miscids is an array of positive integers that contains the IDs for the miscellaneous data chunks in file.

afGetMiscIDs() returns a nonnegative 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:

    int afGetMiscType ( AFfilehandle file, int chunkid )

where file is the AFfilehandle structure previously created by a call to afOpenFile() or afOpenFD(), and chunkid is a positive integer miscellaneous chunk ID from the miscids array returned by afGetMiscIDs().

afGetMiscType() returns a positive symbolic constant that describes the chunk type. See Table 7-4 for the list of valid chunk types and constants. If the chunk is not recognized, afGetMiscType() will return the value AF_MISC_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:

    int afGetMiscSize ( AFfilehandle file, int chunkid )

where file is the AFfilehandle structure previously created by a call to afOpenFile() or afOpenFD(), and chunkid is a positive integer miscellaneous chunk ID from the miscids array returned by afGetMiscIDs().

afGetMiscSize() returns an nonnegative 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()).

#### Reading, Writing, and Seeking Miscellaneous Data

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:

    int afReadMisc ( const AFfilehandle file, int miscid, void *buf,
                     int nbytes )

where file is the AFfilehandle structure previously created by a call to afOpenFile() or afOpenFD(), miscid is a positive 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:

    int afWriteMisc ( AFfilehandle file, int miscid, void *buf,
                      int nbytes )

where file is the AFfilehandle structure previously created by a call to afOpenFile() or afOpenFD(), miscid is a positive 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, int chunkid, int offbytes )

where file is the AFfilehandle structure previously created by a call to afOpenFile() or afOpenFD(), chunkid is a positive integer miscellaneous chunk ID from the miscids array returned by afGetMiscIDs(), offbytes is a non-negative integer specifying the number of bytes past the start of the data area the read/write pointer should be moved, and offbytes 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.

### Handling PCM Data

This section discusses the functions that get and set PCM mapping values for tracks and buffers. PCM mapping is useful for modifying frames only as they are read into or written out of a buffer with `afReadFrames()` or `afWriteFrames()`. None of the supported file formats can store this information, even though the PCM mapping can be applied to frames stored in the files. More information about PCM mapping can be found in the “PCM Mapping” and “Initializing PCM Mapping” sections. See also the reference page `afIntro(3dm)`.

The functions `afGetPCMMapping()` and `afGetVirtualPCMMapping()` get the track and virtual PCM mapping values respectively from an AFfilehandle structure for a specified audio track.

    void afGetPCMMapping ( AFfilehandle file, int track,
                           double *slope, double *intercept,
                           double *minclip, double *maxclip )
    void afGetVirtualPCMMapping ( AFfilehandle file, int track,
                                  double *slope, double *intercept,
                                  double *minclip, double *maxclip )

The file parameter is an AFfilehandle structure previously created by a call to `afOpenFile()`. track is an integer that identifies an audio track in file. Since all supported file formats contain one audio track per file, track is the constant AF_DEFAULT_TRACK.

slope is a pointer to a double precision floating point value that specifies the amplitude scaling factor for the audio waveform associated with track. intercept is a pointer to a double precision floating point value that specifies the audio waveform's vertical midpoint (zero-crossing) value.

minclip is a pointer to a double precision floating point value that indicates the minimum audio data sample value to be returned. Any value less than this is reset to minclip. maxclip is a pointer to a double precision floating point value that indicates the maximum audio data sample value to be returned. Any value greater than this is reset to maxclip. If maxclip is less than or equal to minclip, no clipping will be done. This means all PCM values are legal, even if they are outside the full-voltage range (see “PCM Mapping”).

Using the double precision pointer arguments, `afGetPCMMapping()` returns the four values associated with the PCM (Pulse Code Modulation) mapping in the audio track of file. `afGetVirtualPCMMapping()` does the same for the virtual format of the given track. The track's virtual values were set by calls to `afSetVirtualFormatParams()` and `afSetTrackPCMMapping()`.

The function `afSetTrackPCMMapping()` modifies the current PCM mapping values associated with a given track in an AFfilehandle.

    int afSetTrackPCMMapping ( AFfilehandle file, int track,
                               double slope, double intercept,
                               double minclip, double maxclip )

The parameters are the same as for `afGetPCMMapping()`. Because none of the supported file formats can store PCM mapping information, it is important to specify the parameter values carefully. In general, all two's complement and floating point sample formats are expected to be symmetrical about zero. The intercept value should be 0.0, and minclip and maxclip should be negative and positive N, where N is some non-zero positive value.

An application specifies a PCM mapping for the virtual format and optionally for the track format. The following pseudo-code shows how the AF maps each input sample value, “in_pcm,” to an output sample value, “out_pcm.” For an AFfilehandle opened for input, “in_pcm” is the track and “out_pcm” is the buffer. For an AFfilehandle opened for output, “in_pcm” is the buffer and “out_pcm” is the track.

Example 7-2. Audio File PCM Mapping Pseudo-Code

    /* transform in_pcm to volts */
    if (in_maxclip > in_minclip) {
        if (in_pcm < in_minclip) in_pcm = in_minclip;
        if (in_pcm > in_maxclip) in_pcm = in_maxclip;
    }
    volts = (in_pcm - in_intercept) / in_slope;

    /* transform volts to out_pcm */
    out_pcm = out_intercept + out_slope * volts;
    if (out_maxclip > out_minclip) {
       if (out_pcm < out_minclip) out_pcm = out_minclip;
       if (out_pcm > out_maxclip) out_pcm = out_maxclip;
    }

### Summary of the Audio Track Reading and Writing Functions

The following table is a summary of the Audio File Library functions for reading and writing audio tracks. Functions not covered in the preceding text have a notation in the Description column. Refer to a function's reference page for a more detailed explanation. For example, the reference page for`afGetPCMMapping()` is `afGetPCMMapping(3dm)`

Table 7-8. Audio File Track Reading and Writing Functions

Function | Description
--- | ---
DMstatus `afGetFormatParams` ( AFfilehandle file, int track, DMparams *params ) | Get the audio data format in an AFfilehandle for a specified audio track via DMparams.
double `afGetRate` ( AFfilehandle file, int track ) | Obsolete. See `afGetFormatParams()`.
double `afGetVirtualRate` ( AFfilehandle file, int track ) | Get the virtual sample rate for a specified audio track from an AFfilehandle structure. Not covered in text.
void `afGetSampleFormat` ( AFfilehandle file, int track, int *sampfmt, int *sampwidth ) | Obsolete. See `afGetFormatParams()`.
void `afGetVirtualSampleFormat` ( AFfilehandle file, int track, int *sampfmt, int *sampwidth ) | Get the virtual sample format for a specified audio track from an AFfilehandle structure. Not covered in text.
int `afGetByteOrder` ( AFfilehandle file, int track ) | Get the byte order for a specified audio track from an AFfilehandle structure. Not covered in text.
int `afGetVirtualByteOrder` ( AFfilehandle file, int track ) | Get the virtual byte order for a specified audio track from an AFfilehandle structure. Not covered in text.
int `afGetChannels` ( AFfilehandle file, int track ) | Obsolete. See `afGetFormatParams()`.
int `afGetVirtualChannels` ( AFfilehandle file, int track ) | Get the number of interleaved virtual channels from an AFfilehandle structure for an audio track. Not covered in text.
int `afGetAESChannelData` ( AFfilehandle file, int track, unsigned char buf[24] ) | Get AES channel status information in an AFfilehandle structure for an audio track.
void `afSetAESChannelData` ( AFfilehandle file, int track, unsigned char buf[24] ) | Set AES channel status information in an AFfilehandle structure for an audio track. Not covered in text.
int `afGetCompression` ( AFfilehandle file, int trackid ) | Obsolete. See `afGetFormatParams()`.
int `afGetCompressionParams` ( AFfilehandle file, int trackid, int *compression, AUpvlist pvlist, int numitems ) | Obsolete. See `afGetFormatParams()`.
AFframecount `afGetFrameCount` ( AFfilehandle file, int track ) | Get the total sample frame count for a specified audio track from an AFfilehandle structure.
AFfileoffset `afGetTrackBytes` ( AFfilehandle file, int track ) | Get the total data bytes for a specified audio track from an AFfilehandle structure. Not covered in text.
int `afGetDataOffset` ( AFfilehandle file, int track ) | Get the total data offset for a specified audio track from an AFfilehandle structure. Not covered in text.
int `afGetMarkIDs` ( AFfilehandle file, int trackID, int *markids ) | Get the number and list of marker IDs for an audio track.
char* `afGetMarkName` ( AFfilehandle file, int trackID, int markid ) | Get the name string for a given marker ID in an audio track.
char* `afGetMarkComment` ( AFfilehandle file, int trackID, int markid ) | Get the comment string for a given marker ID in an audio track. Not covered in text.
AFframecount `afGetMarkPosition` ( AFfilehandle file, int trackID, int markid ) | Get the position of a marker in an audio track.
void `afSetMarkPosition` ( AFfilehandle file, int trackID, int markid, AFframecount markpos ) | Set the position of a marker in an audio track.
AFframecount `afSeekFrame` ( const AFfilehandle file, int track, AFframecount frameoffset ) | Move the logical file read pointer for a specified audio track to a desired sample frame location.
AFframecount `afTellFrame` ( const AFfilehandle file, int track ) | Retrieve current value of file read or write pointer. Not covered in text.
int `afReadFrames` ( const AFfilehandle file, int track, void *samples, const int count ) | Read sample frames from a specified audio track in an audio file.
int `afWriteFrames` ( const AFfilehandle file, int track, void *samples, const int count ) | Write audio sample frames to a specified track in an audio file.
int `afGetTrackIDs` ( AFfilehandle file, int trackids[] ) | Get the list of track descriptor IDs for the given AFfilehandle. Not covered in text.
int `afGetInstIDs` ( AFfilehandle file, int *instids ) | Get a list of instrument configurations from an AFfilehandle.
long `afGetInstParamLong` ( AFfilehandle file, int instid, int param ) | Get long parameter value for an instrument configuration in an AFfilehandle structure.
void `afGetInstParams` ( AFfilehandle file, int instid, AUpvlist pvlist, int nparams ) | Get a parameter list for an instrument configuration in an AFfilehandle structure. Not covered in text.
void `afSetInstParams` ( AFfilehandle file, int instid, AUpvlist pvlist, int nparams ) | Set a parameter list for an instrument configuration in an AFfilehandle structure. Not covered in text.
void `afSetInstParamLong` ( AFfilehandle file, int instID, int param, long value ) | Set a long parameter value for an instrument configuration in an AFfilehandle structure.
int `afGetLoopStart` ( AFfilehandle file, int instID, int loopid ) | Get the start marker from an AFfilehandle structure for a specified loop.
int `afGetLoopEnd` ( AFfilehandle file, int instID, int loopid ) | Get the end marker from an AFfilehandle structure for a specified loop.
int `afGetLoopTrack` ( AFfilehandle file, int instID, int loopid ) | Get the track from an AFfilehandle structure for a specified loop. Not covered in text.
int `afGetLoopMode` ( AFfilehandle file, int instID, int loopid ) | Get the play mode from an AFfilehandle structure for a specified loop.
int `afGetLoopIDs` ( AFfilehandle file, int instID, int loopids[] ) | Get a number and list of loop IDs for an instrument configuration. Not covered in text.
AFframecount `afGetLoopStartFrame` ( AFfilehandle file, int instID, int loopid ) | Get the start frame from an AFfilehandle structure for a specified loop. Not covered in text.
AFframecount `afGetLoopEndFrame` ( AFfilehandle file, int instID, int loopid ) | Get the end frame from an AFfilehandle structure for a specified loop. Not covered in text.
int `afGetLoopCount` ( AFfilehandle file, int instID, int loopid ) | Get the loop count in an AFfilehandle structure for a specified loop. Not covered in text.
int `afSetLoopStartFrame` ( AFfilehandle file, int instID, int loopid, AFframecount startframe ) | Set the start frame in an AFfilehandle structure for a specified loop. Not covered in text.
int `afSetLoopEndFrame` ( AFfilehandle file, int instID, int loopid, AFframecount endframe ) | Set the end frame in an AFfilehandle structure for a specified loop. Not covered in text.
int `afSetLoopCount` ( AFfilehandle file, int instID, int loopid, int count ) | Set the loop count in an AFfilehandle structure for a specified loop. Not covered in text.
void `afSetLoopStart` ( AFfilehandle file, int instID, int loopid, int markid ) | Set the start marker in an AFfilehandle structure for a specified loop.
void `afSetLoopEnd` ( AFfilehandle file, int instID, int loopid, int markid ) | Set the end marker in an AFfilehandle structure for a specified loop.
void `afSetLoopTrack` ( AFfilehandle file, int instID, int loopid, int trackID ) | Set the track in an AFfilehandle structure for a specified loop. Not covered in text.
void `afSetLoopMode` ( AFfilehandle file, int instID, int loopid, int mode ) | Set the play mode in an AFfilehandle structure for a specified loop.
int `afGetMiscIDs` ( AFfilehandle file, int miscids[] ) | Get the number and a list of miscellaneous chunk IDs for a file.
int `afGetMiscType` ( AFfilehandle file, int chunkid ) | Get the data type for a miscellaneous data chunk.
int `afGetMiscSize` ( AFfilehandle file, int chunkid ) | Get the size for a miscellaneous data chunk.
int `afReadMisc` ( const AFfilehandle file, int miscid, void *buf, int nbytes ) | Read data from a miscellaneous chunk in an audio file.
int `afWriteMisc` ( const AFfilehandle file, int miscid, void *buf, int nbytes ) | Write data to a miscellaneous chunk in an audio file.
int `afSeekMisc` ( const AFfilehandle file, int chunkid, int offbytes ) | Move logical read/write pointer for data in a miscellaneous chunk in an audio file.
int `afGetFrameSize` ( AFfilehandle file, int track, int extend3to4 ) | Get the track frame size in bytes for a specified audio track from an AFfilehandle structure. Not covered in text.
int `afGetVirtualFrameSize` ( AFfilehandle file, int track, int extend3to4 ) | Get the virtual frame size in bytes for a specified audio track from an AFfilehandle structure. Not covered in text.
int `afSetChannelMatrix` ( AFfilehandle file, int track, double* matrix ) | Set the channel mix matrix associated with a given track in an AFfilehandle. Not covered in text.
DMstatus `afGetVirtualFormatParams` ( AFfilehandle file, int track, DMparams *params ) | Get the virtual audio data format in an AFfilehandle for a specified audio track with a DMparams structure.
DMstatus `afSetVirtualFormatParams` ( AFfilehandle file, int track, DMparams *params ) | Set the virtual audio data format in an AFfilehandle for a specified audio track with a DMparams structure.
int `afSetVirtualSampleFormat` ( AFfilehandle file, int track, int sampfmt, int sampwidth ) | Obsolete. See `afSetVirtualFormatParams().`
int `afSetVirtualChannels` ( AFfilehandle file, int track, int channels ) | Obsolete. See `afSetVirtualFormatParams().`
int `afSetVirtualPCMMapping` ( AFfilehandle file, int track, double slope, double intercept, double minclip, double maxclip ) | Obsolete. See `afSetVirtualFormatParams().`
int `afSetVirtualRate` ( AFfilehandle file, int track, double rate ) | Obsolete. See `afSetVirtualFormatParams().`
int `afSetVirtualByteOrder` ( AFfilehandle file, int track, int byteorder ) | Set the virtual data format for a specified audio track. Not covered in text.
DMstatus `afSetConversionParams` ( AFfilehandle file, int track, DMparams *params ) | Set the parameters associated with format conversion for a specified audio track with a DMparams structure. Not covered in text.
DMstatus `afGetConversionParams` ( AFfilehandle file, int track, DMparams *params ) | Get the parameters associated with format conversion for a specified audio track with a DMparams structure. Not covered in text.
int `afSetTrackPCMMapping` ( AFfilehandle file, int track, double slope, double intercept, double minclip, double maxclip ) | Override the current PCM mapping values associated with a given track in an AFfilehandle.
void `afGetPCMMapping` ( AFfilehandle file, int track, double *slope, double *intercept, double *minclip, double *maxclip ) | Get the track PCM mapping values for a specified audio track from an AFfilehandle structure.
void `afGetVirtualPCMMapping` ( AFfilehandle file, int track, double *slope, double *intercept, double *minclip, double *maxclip ) | Get the virtual PCM mapping values for a specified audio track from an AFfilehandle structure.

## Audio File Library Programming Tips

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.

### Minimizing Data and File Format Dependence

As the AF Library evolves to support new file formats and new 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.

### Preventing Concurrent Access from Multiple Threads

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:

1. Use `usnewsema`(3P) to code to create a semaphore whose value is 1.

2. Use `uspsema`(3P) to lock the semaphore.

3. 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.

### Handling Errors in Multithreaded Applications

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.

### Handling Audio File Library Errors

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 )

The AF library error handler function pointer is declared as a global variable, and therefore is not safe for use in multi-threaded applications. Specifically, a core dump may result if more than one thread attempts to use the error handler simultaneously. This can be avoided by calling `afSetErrorHandler(`NULL`)` to disable the feature entirely. The current version of the AF library has a MT-safe alternative to the AFerrorhandler:

After disabling the internal error handler as shown above, an application should handle errors as in the example below:

    #include <dmedia/dmedia.h>

    AFfilehandle handle;

    handle = afOpenFile ( “a_filename”, “r”, NULL ); /* attempt to open */
    if(handle == NULL) {
        char detail[DM_MAX_ERROR_DETAIL]; /* storage for error detail */
        int errorNumber;                  /* error token storage */
        char *msg;                        /* short error message */

        msg = dmGetError ( &errorNumber, detail );
        if(msg != NULL)                   /* error was reported */
            fprintf ( stderr, “%s [error number %d]\\n”,
                      detail, errorNumber );
        exit(1); /* or whatever */
    }

The application must add `-ldmedia` to its link list if it calls `dmGetError()`.

Prev | Table of Contents | Next
Chapter 6. Digital Media Data Conversion |  | Appendix A. Digital Media Conversion Libraries