Non-blocking sound creation

FMOD Studio API

Firelight Technologies FMOD Studio API

Non-blocking sound creation

Introduction

FMOD_NONBLOCKING flag is used so that sounds can be loaded without affecting the framerate of the application.
Normally loading operations can take a large or significant amount of time, but with this feature, sounds can be loaded in the background without the application skipping a beat.

Creating the sound.

Simply create the sound as you normally would but add the FMOD_NONBLOCKING flag.
FMOD::Sound *sound;
result = system->createStream("../media/wave.mp3", FMOD_NONBLOCKING, 0, &sound;); // Creates a handle to a stream then commands the FMOD Async loader to open the stream in the background.
ERRCHECK(result);
Now the sound will open in the background, and you will get a handle to the sound immediately. You cannot do anything with this sound handle except call Sound::getOpenState. Any other attempts to use this sound handle will result in the function returning FMOD_ERR_NOTREADY.

Getting a callback when the sound loads.

When the sound loads or the stream opens, you can specify a callback using the nonblockcallback member of the FMOD_CREATESOUNDEXINFO structure that is called when the operation is completed.
Firstly the callback definition.
FMOD_RESULT F_CALLBACK nonblockcallback(FMOD_SOUND *sound, FMOD_RESULT result)
{
    FMOD::Sound *snd = (FMOD::Sound *)sound;

    printf("Sound loaded! (%d) %s\n", result, FMOD_ErrorString(result)); 

    return FMOD_OK;
}
And then the createSound call.
FMOD_RESULT result;
FMOD::Sound *sound;
FMOD_CREATESOUNDEXINFO exinfo;

memset(&exinfo;, 0, sizeof(FMOD_CREATESOUNDEXINFO));
exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
exinfo.nonblockcallback = nonblockcallback;

result = system->createStream("../media/wave.mp3", FMOD_NONBLOCKING, &exinfo;, &sound;);
ERRCHECK(result);

Waiting for the sound to be ready and using it.

As mentioned, you will have to call Sound::getOpenState to wait for the sound to load in the background. You could do this, or just continually try to call the function you want to call (i.e. System::playSound) until it succeeds.
Here is an example of polling the sound until it is ready, then playing it.
FMOD_RESULT result;
FMOD::Sound *sound;
result = system->createStream("../media/wave.mp3", FMOD_NONBLOCKING, 0, &sound;); // Creates a handle to a stream then commands the FMOD Async loader to open the stream in the background.
ERRCHECK(result);

do
{
	FMOD_OPENSTATE state;

	result = tmpsnd->getOpenState(&state;, 0, 0);
	ERRCHECK(result);
	
	if (state == FMOD_OPENSTATE_READY && !channel)
	{
		result = system->playSound(FMOD_CHANNEL_FREE, sound, false, &channel;);
		ERRCHECK(result);
	}

	GameCode();
} while (1)
or
do
{
	if (!channel)
	{
		result = system->playSound(FMOD_CHANNEL_FREE, sound, false, &channel;);
		if (result != FMOD_ERR_NOTREADY)
		{
			ERRCHECK(result);
		}
	}

	GameCode();
} while (1)
The second loop will simply retry playsound until it succeeds.

Creating the sound as a streamed FSB file.

An FSB file will have subsounds in it, so if you open it as a stream, you may not want FMOD seeking to the first subsound and wasting time. You can use the initialsubsound member of the FMOD_CREATESOUNDEXINFO structure to make the non-blocking open seek to the subsound of your choice.
FMOD_RESULT result;
FMOD::Sound *sound;
FMOD_CREATESOUNDEXINFO exinfo;

memset(&exinfo;, 0, sizeof(FMOD_CREATESOUNDEXINFO));
exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
exinfo.initialsubsound = 1;

result = system->createStream("../media/sounds.fsb", FMOD_NONBLOCKING, &exinfo;, &sound;);
ERRCHECK(result);
Then get the subsound you wanted with Sound::getSubSound.

Getting a subsound.

Sound::getSubSound is a free function call normally, all it does is return a pointer to the subsound, whether it be a sample or a stream. It does not execute any special code besides this.
What it would cause if it was a blocking stream though, is System::playSound stalling several milliseconds or more while it seeks and reflushes the stream buffer. Time taken can depend on the file format and media.

If the parent sound was opened using FMOD_NONBLOCKING, then it will set the subsound to be FMOD_OPENSTATE_SEEKING and it will become not ready again until the seek and stream buffer flush has completed.
When the stream is ready and System::playSound is called, then the playsound will not stall and will execute immediately because the stream has been flushed.