FSOUND_NONBLOCKING - Firelight Technologies Pty, Ltd.

# USING THE FSOUND_NONBLOCKING FLAG

Introduction

The FSOUND_NONBLOCKING flag stops certain stream or music commands blocking the caller when being executed, by performing them in a seperate thread.
Mainly meant for opening streams and music, to avoid the slowness of disk access generating huge pauses, this flag makes these commands return immediately without affecting the frame-rate at all!
The flag must be specified in FSOUND_Stream_Open or FMUSIC_LoadSongEx using the mode parameter for it to work. You cannot use SetMode type functions to make it work, it must be called at the time of opening.
This raises new issues though. You cannot just access a stream or music file that has been opened with the FSOUND_NONBLOCKING flag immediately! You have to wait for it to open!
FMOD does not 'queue' commands to non blocking streams/musics. This could lead to mis-use and overflows, or even confusion about the state of the stream behaviour at the time, so a polling method is preferred so that the user can control exactly what occurs in what order.

Polling for success

The key to working with nonblocking streams and music files is to wait for them to open before playing them. One method to do this is to poll the state of a stream using FSOUND_Stream_GetOpenState or FMUSIC_GetOpenState.
Another method is to try and play the stream or access the subsounds continuously in a game loop until it succeeds.
While a non blocking stream is opening, nearly all functions will return an error code to signify that the stream is not ready.
This means something like FSOUND_Stream_Play will return -1 while it is trying to open the file in the background.
When it returns a valid channel handle, you know it is ready and has succeeded!
An example for playing a non blocking stream is provided below. This is a typical loop that should be used when trying to play a non blocking stream. You will notice, once it succeeds, it won't try and play again because channel is not -1 anymore.
FSOUND_STREAM *stream;
int channel = -1;
stream = FSOUND_Stream_Open("mystream.fsb", FSOUND_NONBLOCKING, 0, 0);
do
{

if (channel < 0)
{

channel = FSOUND_Stream_Play(FSOUND_FREE, stream);

}
GameCode();

} while (1)
Alternatively you could use the FSOUND_Stream_GetOpenState method. This is shown below.
FSOUND_STREAM *stream;
int channel = -1;
stream = FSOUND_Stream_Open("mystream.fsb", FSOUND_NONBLOCKING, 0, 0);
do
{

if (channel < 0 && FSOUND_Stream_GetOpenState(stream) < 0)
{

channel = FSOUND_Stream_Play(FSOUND_FREE, stream);

}
GameCode();

} while (1)
It is basically the same thing and slightly redundant.

FSB format, substreams and sentences.

Setting SubStreams
FSB has multiple sounds packed within it, so you might want to use FSOUND_Stream_SetSubStream
You don't need to poll to get FSOUND_Stream_SetSubStream to work. It has been optimized so that it will immediately seek to the correct substream once the open has finished. This is a special case for this function, and no other functions allow this.
This means you can call it straight away, even after a non blocking stream open.
Here is a version of the above loop, that sets a substream.
FSOUND_STREAM *stream;
int channel = -1;
stream = FSOUND_Stream_Open("mystream.fsb", FSOUND_NONBLOCKING, 0, 0);
FSOUND_Stream_SetSubStream(stream, SOUND_4);
do
{

if (channel < 0)
{

channel = FSOUND_Stream_Play(FSOUND_FREE, stream);

}
GameCode();

} while (1)
Note that once you call FSOUND_Stream_SetSubStream, the stream is 'not ready' again. This may not seem to be relevant if you call it after FSOUND_Stream_Open, because we already knew it was not going to be ready, but if you call it when the stream has actually finished opening and is ready (for example, you may want to set the substream long after the stream has opened, ie 30 seconds later), then you will need to remember this, but the above logic always takes care of it. You just need to set channel to -1 in the above example whenever you call FSOUND_Stream_SetSubStream and it will work.
Setting SubStream Sentences
Setting a sub-stream-sentence is next , and is slightly trickier. FSOUND_Stream_SetSubStreamSentence does not succeed immediately like FSOUND_Stream_SetSubStream, and will actually fail if the open is still in progress, so you cannot call it straight after FSOUND_Stream_Open like we did in the previous example.
What you have to do is poll for the stream to be in a ready state, then execute FSOUND_Stream_SetSubStreamSentence. Alternatively you can continually call FSOUND_Stream_SetSubStreamSentence until it succeeds, a bit like we did with the call to FSOUND_Stream_Play in the previous example.
Note the following code
FSOUND_STREAM *stream;
int channel = -1;
int setsentence = 0;
stream = FSOUND_Stream_Open("mystream.fsb", FSOUND_NONBLOCKING, 0, 0);
do
{

if (!setsentence)
{

int sentence[2] = { SOUND_4, SOUND_5 };
setsentence = FSOUND_Stream_SetSubStreamSentence(stream, sentence, 2);

}
else if (channel < 0)
{

channel = FSOUND_Stream_Play(FSOUND_FREE, stream);
if (channel != -1)
{

FSOUND_Stream_SetMode(stream, FSOUND_LOOP_NORMAL); // This is just here as an example.

}

}
GameCode();

} while (1)
Remember at all times, you can use FSOUND_Stream_GetOpenState just as effectively as continuously polling the command you want to succeed. It is just slightly less code using the above method.

Things to look out for

If you suspect a file has not opened, or failed to open, poll FSOUND_Stream_GetOpenState. This will return the state of the stream, and if it has failed, it will return -3.
Note that even though the stream has failed to open, the stream handle is still valid when opening in non-blocking mode. You will need to close it.
The point of FSOUND_NONBLOCKING is that it takes no cpu time away from the caller. This means it cannot check the validity of a file, as that would constitute disk access, which could block and cause frames to drop from the application.
All disk access is done from the FMOD asynchronous loading thread, so if it fails, the user cannot get immediate feedback, they have to check the status seperately.
Don't try and close a stream before it is ready! This will cause a memory leak, as FSOUND_Stream_Close will return FALSE because it is not ready.
Check the documentation for each stream function to see if it will fail or not due to not being ready in a non blocking state.