Decision Functions


So far, this chapter has described how to perform primarily static actions. These actions include the ability to instruct playback to always branch, continue, or stop at any given SPMarker. Any SPMarker can have precisely one static action at any time. This can be changed dynamically during playback, but in order to do this intelligently, it is important to know precisely when.

The advanced sound player provides a dynamic action facility known as decision functions. A decision function is a callback function that the advanced sound player calls during playback in order to determine what action to take at selected SPMarkers.

Before going into more detail, it is important to understand when decisions about sound playback need to be made. Sound data is read from whatever sound source it comes from (file or memory) and then enqueued to a sound spooler, which is in turn played by the Audio folio. Because of the spooling, there is an certain amount of delay between the time that sound is read and when it is actually played. All decisions about where to branch, or when to stop, take place while reading the sound. Branches do not cause the sound data in the spooler to be flushed, they merely cause the reader to read from a different location in the sound data. Therefore, all decisions have a certain amount of latency before the results are actually heard.

Note: Since the amount of latency is directly related to the size of the sound buffers you supply to the advanced sound player, you have some important design decisions to make when writing a program that uses the advanced sound player. See the Caveats at the end of this chapter.

Decision functions are called when selected SPMarkers are encountered while reading sound data. There are two kinds of decision functions:

Each SPMarker can have its own marker decision function installed, in addition to whatever static action it may have. An SPMarker's decision function is called when that SPMarker is encountered while reading. Each SPPlayer can have its own default decision function which is called for every SPMarker encountered while reading.

Just like static actions set in an SPMarker, decision functions can return an action to perform: branch to another SPMarker or stop reading. Also, decision functions have the ability to return a don't care result, which causes the advanced sound player to act as if the decision function had never been installed.

The following pseudocode shows the steps that are used in determining what action the advanced sound player takes at an SPMarker:

     if SPMarker has a decision function and that decision function specifies an action

     take action specified by decision function

     else if SPPlayer has a default decision function and that decision function specifies an action

     take action specified by decision function

     else if SPMarker has a static action (branch or stop) 

     take action specified in the SPMarker 

     else if playback has reached the end of the current sound data 

     stop 
 
     else 

     continue playback after the current SPMarker

Decision Functions and Actions

Decision functions are functions whose type can be cast to SPDecisionFunction. This is defined in <soundplayer.h> as follows:

typedef Err (*SPDecisionFunction) (SPAction *resultAction,
void *decisionData,
SPSound *sound,
const char *markerName)

Decision functions are called with a client supplied data pointer, information about the SPMarker at which the decision is to be made, and an SPAction to fill out.

SPAction is a black box data type that is used to contain the action that the decision function wishes the advanced sound player to take. The decision function can call either spSetBranchAction() or spSetStopAction() to set the SPAction to instruct the advanced sound player to branch to another SPMarker or stop reading. Alternatively, the decision function can simply not set the SPAction at all. In this case, the advanced sound player continues as if the decision function had not been installed. This can be used to permit whatever static action is set up in the SPMarker to normally take place, but to override this occasionally with some other action from the decision function.

Example Decision Function

Here are some simple modifications to the previous annotated example to do some simple branching.

Loop a Sound File a Fixed Number of Times

Substitute this step in the annotated example to play a sound file four times in a loop. This is excerpted from tsp_spoolsoundfile.c.

Set up sound to loop by default. Install a Decision Function at the end SPMarker to be called each time playback reaches the end of the sound.

spLoopSound (sound);
RemainingCount = 4; /* play 4 times */
spSetMarkerDecisionFunction (
sound, SP_MARKER_NAME_END,
(SPDecisionFunction)LoopDecisionFunction,
&RemainingCount);

The decision function, LoopDecisionFunction(), looks like this:

/*
This decision function decrements the RemainingCount variable.
Normally it does nothing. When the count is exhausted, it
instructs the advanced sound player to stop reading.
*/
Err LoopDecisionFunction (
SPAction *resultAction, int32 *remainingCountP,
SPSound *sound, const char *markerName)
{
/* decrement remaining repeat count */
(*remainingCountP)--;

/* loop back to beginning (default action) or stop if
no more repeats */
if (*remainingCountP <= 0)
return spSetStopAction (resultAction);
else
return 0;
}

Rules for Decision Functions

Besides setting an SPAction, a decision function can do almost anything to the SPPlayer that owns this marker including adding new SPSounds or SPMarkers, changing the static action for this or any other SPMarker, changing the default or marker decision functions for this or any other SPMarker, deleting this or any other SPMarker or SPSound (with the caveats listed below in mind).

A decision function must not do any of the following:

Also note that because decision functions are called on the reader's schedule, not the player's schedule, they cannot be used for notification that a particular part of the sound has actually been played.

See SPDecisionFunction in Music Library Calls for more complete information.