MA_APIma_resultma_sound_seek_to_pcm_frame(ma_engine*pEngine,ma_sound*pSound,ma_uint64frameIndex);/* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */
MA_APIma_resultma_sound_group_init(ma_engine*pEngine,ma_sound_group*pParentGroup,ma_sound_group*pGroup);/* Parent must be set at initialization time and cannot be changed. Not thread-safe. */
MA_APIvoidma_sound_group_uninit(ma_engine*pEngine,ma_sound_group*pGroup);/* Not thread-safe. */
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 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. */
returnresult;
}
/* 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. */
/* 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);
returnresult;
}
}else{
/* There's no available sounds for recycling. We need to allocate a sound. This can be done using a stack allocator. */
pSound=ma__malloc_from_callbacks(sizeof(*pSound),&pEngine->allocationCallbacks/*, MA_ALLOCATION_TYPE_SOUND*/);/* TODO: This can certainly be optimized. */
/* 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. */
}
/* Finally we can start playing the sound. */
result=ma_sound_start(pSound);
if(result!=MA_SUCCESS){
/* Failed to start the sound. We need to uninitialize it and return an error. */
/* Make sure the sound is stopped as soon as possible to reduce the chance that it gets locked by the mixer. We also need to stop it before detaching from the group. */
ma_sound_set_stop_delay(pEngine,pSound,0);/* <-- Ensures the sound stops immediately. */
result=ma_sound_stop(pEngine,pSound);
ma_sound_set_stop_delay(pSound,0);/* <-- Ensures the sound stops immediately. */
result=ma_sound_stop(pSound);
if(result!=MA_SUCCESS){
return;
}
/* The sound needs to removed from the group to ensure it doesn't get iterated again and cause things to break again. This is thread-safe. */
It's important that the delay be timed based on the engine's sample rate and not the rate of the sound. The reason is that no processing will be happening
by the sound before playback has actually begun and we won't have accurate frame counters due to resampling.
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 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. */
returnresult;
}
/* 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. */
/* 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(pEngine,pSound);
returnresult;
}
}else{
/* There's no available sounds for recycling. We need to allocate a sound. This can be done using a stack allocator. */
pSound=ma__malloc_from_callbacks(sizeof(*pSound),&pEngine->allocationCallbacks/*, MA_ALLOCATION_TYPE_SOUND*/);/* TODO: This can certainly be optimized. */
/* 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. */
}
/* Finally we can start playing the sound. */
result=ma_sound_start(pEngine,pSound);
if(result!=MA_SUCCESS){
/* Failed to start the sound. We need to uninitialize it and return an error. */