ma_bool8_isInternal;/* A marker to indicate the sound is managed entirely by the engine. This will be set to true when the sound is created internally by ma_engine_play_sound(). */
/*
We're declaring a resource manager data source object here to save us a malloc when loading a
/* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */
Fire and forget sounds are never actually removed from the group. In practice there should never be a huge number of sounds playing at the same time so we
should be able to get away with recycling sounds. What we need, however, is a way to switch out the old data source with a new one.
The first thing to do is find an available sound. We will only be doing a forward iteration here so we should be able to do this part without locking. A
sound will be available for recycling if it's marked as internal and is at the end.
We want to check if we can recycle an already-allocated inlined sound. Since this is just a
helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep
the implementation simple. Maybe this can be optimized later if there's enough demand, but
if this function is being used it probably means the caller doesn't really care too much.
What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise
we just keep iterating. If we reach the end without finding a sound to recycle we just
allocate a new one. This doesn't scale well for a massive number of sounds being played
simultaneously as we don't ever actually free the sound objects. Some kind of garbage
collection routine might be valuable for this which I'll think about.
We actually want to detach the sound from the list here. The reason is because we want the sound
to be in a consistent state at the non-recycled case to simplify the logic below.
*/
if(pSound->pPrev!=NULL){
pSound->pPrev->pNext=pSound->pNext;
}
if(pSound->pNext!=NULL){
pSound->pNext->pPrev=pSound->pPrev;
}
/* The old data source has been uninitialized so now we need to initialize the new one. */
result = ma_resource_manager_data_source_init(pEngine->pResourceManager, pFilePath, dataSourceFlags, NULL, &pSound->resourceManagerDataSource);
if (result != MA_SUCCESS) {
/* We failed to load the resource. We need to return an error. We must also put this sound back up for recycling by setting the at-end flag to true. */
c89atomic_exchange_32(&pSound->atEnd, MA_TRUE); /* <-- Put the sound back up for recycling. */
return result;
/* Now the previous sound needs to be uninitialized. */
ma_sound_uninit(&pNextSound->sound);
}else{
/* No sound available for recycling. Allocate one now. */
/* Set the data source again. It should always be set to the correct value but just set it again for completeness and consistency with the main init API. */
if(pSound!=NULL){/* Safety check for the allocation above. */
/*
At this point we should have memory allocated for the inlined sound. We just need
to initialize it like a normal sound now.
*/
dataSourceFlags|=MA_SOUND_FLAG_ASYNC;/* For inlined sounds we don't want to be sitting around waiting for stuff to load so force an async load. */
dataSourceFlags|=MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT;/* We want specific control over where the sound is attached in the graph. We'll attach it manually just before playing the sound. */
/* We need to reset the effect. */
result = ma_engine_effect_reinit(pEngine, &pSound->effect);
if (result != MA_SUCCESS) {
/* We failed to reinitialize the effect. The sound is currently in a bad state and we need to delete it and return an error. Should never happen. */
ma_sound_uninit(pSound);
return result;
}
} else {
/* There's no available sounds for recycling. We need to allocate a sound. This can be done using a stack allocator. */
pSound = (ma_sound*)ma__malloc_from_callbacks(sizeof(*pSound), &pEngine->allocationCallbacks/*, MA_ALLOCATION_TYPE_SOUND*/); /* TODO: This can certainly be optimized. */
pEngine->pInlinedSoundHead=pSound;/* <-- This is what attaches the sound to the list. */
if(pSound->pNext!=NULL){
pSound->pNext->pPrev=pSound;
}
}else{
ma_free(pSound,&pEngine->allocationCallbacks);
}
}else{
ma_free(pSound,&pEngine->allocationCallbacks);
}
}else{
result=MA_OUT_OF_MEMORY;
}
/* The sound needs to be marked as internal for our own internal memory management reasons. This is how we know whether or not the sound is available for recycling. */
pSound->_isInternal = MA_TRUE; /* This is the only place _isInternal will be modified. We therefore don't need to worry about synchronizing access to this variable. */
}
ma_mutex_unlock(&pEngine->inlinedSoundLock);
/* Finally we can start playing the sound. */
result = ma_sound_start(pSound);
result=ma_sound_start(&pSound->sound);
if(result!=MA_SUCCESS){
/* Failed to start the sound. We need to uninitialize it and return an error. */
ma_sound_uninit(pSound);
/* Failed to start the sound. We need to mark it for recycling and return an error. */