Operating the Sound File Player


A task uses the sound file player by making Music library calls. This section of the chapter describes how each of those calls works in the process of sound file player operation.

Creating a Sound File Player

The first step in using the sound file player is to create a sound file player. This is a process that creates a SoundFilePlayer data structure, sets initial values for the structure's fields, and, optionally, creates the sound buffers to go with the player. To create a sound file player, use this call:

SoundFilePlayer *CreateSoundFilePlayer( int32 NumBuffers, int32 BufSize, void *Buffers[] )
The call accepts three arguments. The first, NumBuffers, is the number of buffers set up for use with the sound file player; the second is the size in bytes of a buffer. (Remember that all of a sound file player's buffers must be the same size.) The third, *Buffers[], is an array of pointers, one for each player buffer. Each pointer in the array points to the buffer's beginning location in memory. This is not the location of the sample item that uses this memory, but of the memory used to store the sampled sound data for the buffer.

When a task creates a sound file player, it can ask the Music library to create the buffers for it. To do so, it passes a NULL for the *Buffers[] argument. CreateSoundFilePlayer() then uses the NumBuffers and BufSize values to allocate the proper amount of memory and to create sample items and SoundFileBuffer data structures for each buffer. It sets the appropriate SoundFilePlayer values to refer to those data structures.

If a task wants to allocate its own memory for sound file buffers, it first allocates the memory and then sets the appropriate pointers in the *Buffers[] array. (It is important to remember that all buffers must be the same size.) When CreateSoundFilePlayer() executes, it creates sound file buffers using the allocated memory and includes the buffers in the buffer array of the SoundFilePlayer data structure. If you use this option, be sure to set NumBuffers and BufSize to values that match the buffers you point to. When you allocate memory for sound buffers, specify the memory type MEMTYPE_AUDIO.

When it executes, CreateSoundFilePlayer() creates a SoundFilePlayer data structure and sets its field values to default values. If requested, it allocates memory for sound buffers. It creates a SoundFileBuffer data structure for each buffer, then sets the appropriate buffer values in the SoundFilePlayer data structure. If successful, this call returns a pointer to the newly created SoundFilePlayer data structure. If unsuccessful, it returns a NULL.

Preloading a Sound File

Once a sound file player is created, a task specifies which AIFF file to play and asks the sound file player to preload the file using this call:

int32 LoadSoundFile( SoundFilePlayer *sfp, char *FileName )
The call accepts two arguments: *sfp, which is a pointer to the SoundFilePlayer data structure controlling playback; and *FileName, which is a pointer to a null terminated string containing the pathname to the AIFF file.

When executed, LoadSoundFile() preloads as much of the specified AIFF file's sample data as fits into the sound buffers and sends them to the sound spooler. It also determines the sampled sound instrument most appropriate to play back the sampled sound, loads that instrument, attaches it to the sound buffers, and connects the instrument's output to an output mixer. The call then sets SoundFilePlayer values to store the item number of the specified AIFF file, the item number of the sampled sound instrument. LoadSoundFile() also creates an IOReq item to read the AIFF file, and stores its item number in the SoundFilePlayer data structure.

If successful, LoadSoundFile() returns 0. If unsuccessful, it returns an error code (a negative value).

Creating a Sound File Player and Preloading a File

If you want to combine the process of creating a sound file player and preloading a sound file, you can use this convenience call:

SoundFilePlayer *OpenSoundFile( char *FileName, int32 NumBuffers, int32 BufSize )
The call accepts three arguments: *FileName, which is a pointer to a null terminated string containing the pathname to the AIFF file; NumBuffers, which is the number of buffers desired for the sound file player; and BufSize, which is the size in bytes of each sound buffer.

When it executes, StartSoundFile() instructs the sound spooler to begin playing the preloaded sound buffers. Playback starts in the first preloaded buffer, and continues sequentially through the successive buffers. The sound file player stops after playing the last filled buffer if the sound file player is not serviced (see below) before then.

Note that OpenSoundFile() does not allow you to specify existing sound buffers. If you want to create your own buffers or reuse existing buffers, use CreateSoundFilePlayer() and LoadSoundFile() instead.

Specifying an Output Mixer

Whenever a task uses LoadSoundFile() or OpenSoundFile() to specify and preload an AIFF file, the sound file player, by default, loads a directout.dsp output mixer and connects the sampled sound instrument output to the mixer. If you want to send the sampled sound output to a different mixer or to an effects instrument, you must set to true the SFP_NO_DIRECTOUT flag in the sfp_Flags field of the SoundFilePlayer data structure before calling LoadSoundFile(). (Be careful not to unset other flags in the field when you do.) When true, the flag tells the sound file player not to automatically load and connect a directout.dsp mixer. The calling task is then responsible for connecting the sound file player's sampled sound instrument to an existing mixer or effects instrument. It is also responsible for deleting the connection when the sound file player is deleted.

Starting Sound File Playback

Once a sound file player is set up and a sound file is preloaded, a task can start sound playback using this call:

int32 StartSoundFile( SoundFilePlayer *sfp, int32 Amplitude )
The call accepts the argument *sfp, a pointer to the SoundFilePlayer data structure used to control sound playback; and the argument Amplitude, a value from 0 to 0x7FFF that controls the amplitude of the sound's playback. Use the amplitude value to limit playback amplitude so that it does not overload the system amplitude. (For details, see Preparing Instruments.)

When executed, StartSoundFile() begins playing back the contents of the first sound buffer, feeding them to the SoundFilePlayer specified sampled sound instrument. The instrument feeds its output to the specified output mixer. When the first buffer finishes playback, the spooler uses the buffer's cue to send a signal to the playback calling task, and then starts playing the contents of the next sound buffer. As each buffer finishes playback, the spooler sends the buffer's signal to the calling task. When the spooler finishes playing the last buffer in the buffer array, it starts playback at the beginning of the first buffer, and the playback cycle begins again. Playback does not stop until the task calls StopSoundFile() or dies.

If successful, StartSoundFile() returns 0. If unsuccessful, it returns an error code (a negative value).

Servicing a Sound File

To keep fresh sound file data loaded in the sound buffers ahead of playback, a task must constantly service the sound file. To do so, it must use a loop. The task should ideally be a high priority task or thread so that it is not interrupted in its file service.

Within the service loop, the task checks to see what buffers it must wait for. It then enters wait state until it receives a signal from one or more of those buffers, signifying that the buffer has finished playing. The task then exits wait state, asks the sound file player to spool more of the AIFF sample data into the free buffer (or buffers), and checks again to see what buffers it must wait for.

The key call within such a loop is this:

int32 ServiceSoundFile( SoundFilePlayer *sfp, int32 SignalIn, int32 *SignalNeeded )
The call accepts three arguments: *sfp, a pointer to the SoundFilePlayer data structure used for playback; SignalIn, a signal mask containing a bit set for each buffer to which the sound file player should write; and *SignalNeeded, a pointer to a signal mask where the sound file player writes the signal bits of each buffer for which the calling task should wait. The sound file player writes 0 to SignalNeeded when it finishes spooling the sound file to the buffers.

When ServiceSoundFile() executes, it performs an input service and an output service. Its input service is to read the signal bits specified by the calling task in SignalIn, to find the corresponding sound buffers, and to read sampled sound data from disc into those buffers. Its output service is to write a signal mask to SignalNeeded that contains a signal bit set for each buffer the task should wait for. The task should use this signal mask whenever it enters wait state. If the task needs to wait for other signals as well, it can logically OR the SignalNeeded signal mask with another signal mask before entering wait state. If SignalNeeded comes back 0, the task knows that spooling is finished, and can exit its service loop.

If successful, ServiceSoundFile() returns 0. If unsuccessful, it returns an error code (a negative value).

Note: When ServiceSoundFile() executes, it calls ReadSoundFile() to help the spooler keep its place in the AIFF file and in the buffers it uses to play the file. Although ReadSoundFile() is described in the 3DO Music and Audio Programmer's Reference, your tasks should not use it directly; it can mix up the sound file player's playback.

The File Service Loop

When a task uses ServiceSoundFile() to service a file, it might use a loop such as the one defined in the following code fragment:

Example 1: Servicing a file.

int32 Result;
SoundFilePlayer *sfp;
int32 SignalIn, SignalsNeeded;
SignalIn = 0;
do {
    Result = ServiceSoundFile(sfp, SignalIn, &SignalsNeeded);
    if (SignalsNeeded) SignalIn = WaitSignal(SignalsNeeded);
} while (SignalsNeeded)

Going into the loop, the task sets to 0 SignalIn, the mask it uses to tell ServiceSoundFile() which buffers the spooler should write to. The second mask, SignalsNeeded, is used by ServiceSoundFile() to receive the signals the task should wait for.

The first statement in the loop, which calls ServiceSoundFile(), specifies no buffers to write to (SignalIn = 0), so the call does not spool any of the AIFF file to the buffers. It only reports the buffers that the task should wait for, which it does by writing the buffers' signals in the SignalsNeeded signal mask.

The second statement in the loop checks to see if there are buffers for which the task should wait. If so, it calls WaitSignal(). The task supplies WaitSignal() with the signal mask specifying the buffers to wait for. The task enters wait state until it receives a signal from any one of these buffers, that tells the task which buffer has played. (If buffers have already played before the task enters wait state, then the task immediately gets the buffers' signals when it enters wait state. This means that the task can receive more than one signal at this point.) Once the task has received one or more buffer signals, it exits wait state and proceeds to the end of the loop.

The end of the loop checks the SignalsNeeded mask to see if it is set to 0. If it is, the AIFF file has finished playing, and the loop exits.

Stopping Sound File Playback

A task can stop sound file playback at any point, but it usually does so when ServiceSoundFile() informs it that the AIFF file has finished playing. To stop playback, use the following call:

int32 StopSoundFile( SoundFilePlayer *sfp )
The call accepts a single argument: *sfp, which is a pointer to the SoundFilePlayer data structure controlling playback. When StopSoundFile() executes, it stops buffer playback so that no sample data is fed to the sampled sound instrument. As a consequence, no buffer signals are sent as each buffer finishes playback.

StopSoundFile() returns 0 if successful, or an error code (a negative value) if unsuccessful.

Rewinding a Sound File Player

To rewind a sound file player so that it is ready to play its AIFF file from the beginning once again, use this call:

int32 RewindSoundFile( SoundFilePlayer *sfp )
The call accepts a single argument: *sfp, which is a pointer to the SoundFilePlayer data structure controlling playback. When it executes, RewindSoundFile() resets all of the appropriate playback progress values in the SoundFilePlayer data structure to the beginning of the AIFF file. It also preloads the beginning of the file into the sound buffers. When playback is turned back on with a StartSoundFile() call, the AIFF sound file begins playback from its beginning.

Note: This call does not provide seamless looping.

RewindSoundFile() returns 0 if successful, or an error code (a negative value) if unsuccessful.

Cleaning Up After Playback

Once a task finishes playing a sound file and no longer needs the sound file player, it should clean up by freeing any associated items such as the playback instrument, attachment, and output mixer. It should then delete the SoundFilePlayer data structure and any associated sound buffers.

To free associated items, use this call:

int32 UnloadSoundFile( SoundFilePlayer *sfp )
It accepts a pointer to the SoundFilePlayer data structure that you want to clean up for. When it executes, the call closes the AIFF file used for spooling, deletes the IOReq item used to read the file, detaches the buffers from the sampled sound instrument by deleting the attachment items, and frees the sampled sound instrument. If the sound file player created a directout.dsp mixer for sampled sound playback, that mixer is freed as well. UnloadSoundFile() is, in essence, the reverse of the LoadSoundFile() with one exception: it does not clear the buffers of sample data, but leaves it in place.

UnloadSoundFile() returns 0 if successful, or an error code (a negative value) if unsuccessful.

Calling UnloadSoundFile() is necessary if you want to play a new AIFF file on an existing sound file player. The call cleans up the existing AIFF setup so that calling LoadSoundFile() arranges a new AIFF setup for playback.

To delete a SoundFilePlayer data structure when you're finished with it, use this call:

int32 DeleteSoundFilePlayer( SoundFilePlayer *sfp )
The call accepts a pointer to the SoundFilePlayer data structure you want to delete. When it executes, it deletes the data structure and cleans up anything else created by CreateSoundFilePlayer(). If sound buffers were created automatically for the sound player, their SoundFileBuffer data structures are deleted, and the memory used for them is freed. If the buffers were not created automatically, then DeleteSoundFilePlayer() does not delete them; the task must delete them itself.

DeleteSoundFilePlayer() returns 0 if successful, or an error code (a negative value) if unsuccessful.

If you want to use a convenience call to combine UnloadSoundFile() and DeleteSoundFilePlayer(), use this call:

int32 CloseSoundFile( SoundFilePlayer *sfp )
It accepts a pointer to the SoundFilePlayer data structure you want to delete. When executed, it calls UnloadSoundFile() to clean up any items associated with the player and then calls DeleteSoundFilePlayer() to delete the specified data structure.

CloseSoundFile() returns 0 if successful, or an error code (a negative value) if unsuccessful.

Note that CloseSoundFilePlayer() does not delete custom allocated buffers. You should not use it on a sound file player with custom buffers. If you do, you need to free those buffers yourself.

Typical Calling Sequences

A single sound file player can play back a sound file once, play back a sound file repeatedly, or play back a series of sound files. To do so, it can use the following calling sequences for playback.

Playing a Sound File Once

CreateSoundFilePlayer()
LoadSoundFile()
StartSoundFile()
loop{
    ServiceSoundFile()
}is file finished?
StopSoundFile()
UnloadSoundFile()
DeleteSoundFilePlayer()

Playing a Sound File Repeatedly

CreateSoundFilePlayer()
LoadSoundFile()
loop{
    StartSoundFile()
    loop{
        ServiceSoundFile()
    }is file finished?
    StopSoundFile()
    RewindSoundFile()
}has file played specified number of times?
UnloadSoundFile()
DeleteSoundFilePlayer()

Playing a Series of Sound Files Any Number of Times

CreateSoundFilePlayer()
loop{
    LoadSoundFile()
    loop{
        StartSoundFile()
        loop{
            ServiceSoundFile()
        }is file finished?
        StopSoundFile()
        RewindSoundFile()
    }has file played specified number of times?
    UnloadSoundFile()
}is there another file to play?
DeleteSoundFilePlayer()

Note that in this calling sequence, the outer loop checks to see if there is another file to play. If there is, it specifies the new file's pathname when it reenters the loop.