Commit 0c410b0f authored by David Reid's avatar David Reid

Remove the pEngine parameter from all ma_sound_*() APIs.

parent fd1ca852
......@@ -9,7 +9,6 @@
typedef struct
{
ma_async_notification_callbacks cb;
ma_engine* pEngine;
ma_sound* pSound;
} sound_loaded_notification;
......@@ -22,8 +21,8 @@ void on_sound_loaded(ma_async_notification* pNotification)
This will be fired when the sound has finished loading. We should be able to retrieve the length of the sound at this point. Here we'll just set
the fade out time.
*/
ma_sound_get_length_in_pcm_frames(pLoadedNotification->pEngine, pLoadedNotification->pSound, &lengthInPCMFrames);
ma_sound_set_fade_point_in_frames(pLoadedNotification->pEngine, pLoadedNotification->pSound, 1, 1, 0, lengthInPCMFrames - 192000, lengthInPCMFrames);
ma_sound_get_length_in_pcm_frames(pLoadedNotification->pSound, &lengthInPCMFrames);
ma_sound_set_fade_point_in_frames(pLoadedNotification->pSound, 1, 1, 0, lengthInPCMFrames - 192000, lengthInPCMFrames);
}
int main(int argc, char** argv)
......@@ -47,8 +46,7 @@ int main(int argc, char** argv)
#if 1
loadNotification.pEngine = &engine;
loadNotification.pSound = &sound;
loadNotification.pSound = &sound;
result = ma_sound_init_from_file(&engine, argv[1], MA_DATA_SOURCE_FLAG_DECODE | MA_DATA_SOURCE_FLAG_ASYNC | MA_DATA_SOURCE_FLAG_STREAM, &loadNotification, NULL, &sound);
if (result != MA_SUCCESS) {
......@@ -63,19 +61,19 @@ int main(int argc, char** argv)
//ma_sound_group_set_pitch(&engine, NULL, 1.0f);
//ma_sound_group_set_start_delay(&engine, NULL, 2000);
/*ma_sound_set_volume(&engine, &sound, 0.25f);*/
//ma_sound_set_pitch(&engine, &sound, 2.0f);
ma_sound_set_pan(&engine, &sound, 0.0f);
ma_sound_set_looping(&engine, &sound, MA_TRUE);
//ma_sound_seek_to_pcm_frame(&engine, &sound, 6000000);
//ma_sound_set_start_delay(&engine, &sound, 1110);
ma_sound_set_fade_point_in_milliseconds(&engine, &sound, 0, 0, 1, 0, 2000);
ma_sound_set_stop_delay(&engine, &sound, 1000);
ma_sound_start(&engine, &sound);
/*ma_sound_set_volume(&sound, 0.25f);*/
//ma_sound_set_pitch(&sound, 2.0f);
ma_sound_set_pan(&sound, 0.0f);
ma_sound_set_looping(&sound, MA_TRUE);
//ma_sound_seek_to_pcm_frame(&sound, 6000000);
//ma_sound_set_start_delay(&sound, 1110);
ma_sound_set_fade_point_in_milliseconds(&sound, 0, 0, 1, 0, 2000);
ma_sound_set_stop_delay(&sound, 1000);
ma_sound_start(&sound);
ma_sleep(2000);
printf("Stopping...\n");
ma_sound_stop(&engine, &sound);
ma_sound_stop(&sound);
//ma_sound_group_stop(&engine, NULL);
#endif
......@@ -102,7 +100,7 @@ int main(int argc, char** argv)
}
//ma_sound_group_set_pitch(&engine, NULL, pitch);
ma_sound_set_pitch(&engine, &sound, pitch);
ma_sound_set_pitch(&sound, pitch);
printf("Pitch: %f\n", pitch);
ma_sleep(1);
......@@ -112,7 +110,7 @@ int main(int argc, char** argv)
printf("Press Enter to quit...");
getchar();
ma_sound_uninit(&engine, &sound);
ma_sound_uninit(&sound);
ma_engine_uninit(&engine);
return 0;
......
......@@ -883,6 +883,7 @@ typedef struct
struct ma_sound
{
ma_engine* pEngine; /* A pointer to the object that owns this sound. */
ma_data_source* pDataSource;
ma_sound_group* pGroup; /* The group the sound is attached to. */
ma_sound* pPrevSoundInGroup;
......@@ -905,6 +906,7 @@ struct ma_sound
struct ma_sound_group
{
ma_engine* pEngine; /* A pointer to the engine that owns this sound group. */
ma_sound_group* pParent;
ma_sound_group* pFirstChild;
ma_sound_group* pPrevSibling;
......@@ -970,32 +972,34 @@ MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB);
MA_API ma_result ma_engine_listener_set_position(ma_engine* pEngine, ma_vec3 position);
MA_API ma_result ma_engine_listener_set_rotation(ma_engine* pEngine, ma_quat rotation);
MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */
#ifndef MA_NO_RESOURCE_MANAGER
MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_async_notification* pNotification, ma_sound_group* pGroup, ma_sound* pSound);
#endif
MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound);
MA_API void ma_sound_uninit(ma_engine* pEngine, ma_sound* pSound);
MA_API ma_result ma_sound_start(ma_engine* pEngine, ma_sound* pSound);
MA_API ma_result ma_sound_stop(ma_engine* pEngine, ma_sound* pSound);
MA_API ma_result ma_sound_set_volume(ma_engine* pEngine, ma_sound* pSound, float volume);
MA_API ma_result ma_sound_set_gain_db(ma_engine* pEngine, ma_sound* pSound, float gainDB);
MA_API ma_result ma_sound_set_effect(ma_engine* pEngine, ma_sound* pSound, ma_effect* pEffect);
MA_API ma_result ma_sound_set_pan(ma_engine* pEngine, ma_sound* pSound, float pan);
MA_API ma_result ma_sound_set_pitch(ma_engine* pEngine, ma_sound* pSound, float pitch);
MA_API ma_result ma_sound_set_position(ma_engine* pEngine, ma_sound* pSound, ma_vec3 position);
MA_API ma_result ma_sound_set_rotation(ma_engine* pEngine, ma_sound* pSound, ma_quat rotation);
MA_API ma_result ma_sound_set_looping(ma_engine* pEngine, ma_sound* pSound, ma_bool32 isLooping);
MA_API ma_result ma_sound_set_fade_point_in_frames(ma_engine* pEngine, ma_sound* pSound, ma_uint32 fadePointIndex, float volumeBeg, float volumeEnd, ma_uint64 timeInFramesBeg, ma_uint64 timeInFramesEnd);
MA_API ma_result ma_sound_set_fade_point_in_milliseconds(ma_engine* pEngine, ma_sound* pSound, ma_uint32 fadePointIndex, float volumeBeg, float volumeEnd, ma_uint64 timeInMillisecondsBeg, ma_uint64 timeInMillisecondsEnd);
MA_API ma_result ma_sound_set_start_delay(ma_engine* pEngine, ma_sound* pSound, ma_uint64 delayInMilliseconds);
MA_API ma_result ma_sound_set_stop_delay(ma_engine* pEngine, ma_sound* pSound, ma_uint64 delayInMilliseconds);
MA_API ma_bool32 ma_sound_at_end(ma_engine* pEngine, const ma_sound* pSound);
MA_API ma_result ma_sound_get_time_in_frames(ma_engine* pEngine, const ma_sound* pSound, ma_uint64* pTimeInFrames);
MA_API ma_result ma_sound_seek_to_pcm_frame(ma_engine* pEngine, ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */
MA_API ma_result ma_sound_get_data_format(ma_engine* pEngine, ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_engine* pEngine, ma_sound* pSound, ma_uint64* pCursor);
MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_engine* pEngine, ma_sound* pSound, ma_uint64* pLength);
MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */
MA_API void ma_sound_uninit(ma_sound* pSound);
MA_API ma_result ma_sound_start(ma_sound* pSound);
MA_API ma_result ma_sound_stop(ma_sound* pSound);
MA_API ma_result ma_sound_set_volume(ma_sound* pSound, float volume);
MA_API ma_result ma_sound_set_gain_db(ma_sound* pSound, float gainDB);
MA_API ma_result ma_sound_set_effect(ma_sound* pSound, ma_effect* pEffect);
MA_API ma_result ma_sound_set_pan(ma_sound* pSound, float pan);
MA_API ma_result ma_sound_set_pitch(ma_sound* pSound, float pitch);
MA_API ma_result ma_sound_set_position(ma_sound* pSound, ma_vec3 position);
MA_API ma_result ma_sound_set_rotation(ma_sound* pSound, ma_quat rotation);
MA_API ma_result ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping);
MA_API ma_result ma_sound_set_fade_point_in_frames(ma_sound* pSound, ma_uint32 fadePointIndex, float volumeBeg, float volumeEnd, ma_uint64 timeInFramesBeg, ma_uint64 timeInFramesEnd);
MA_API ma_result ma_sound_set_fade_point_in_milliseconds(ma_sound* pSound, ma_uint32 fadePointIndex, float volumeBeg, float volumeEnd, ma_uint64 timeInMillisecondsBeg, ma_uint64 timeInMillisecondsEnd);
MA_API ma_result ma_sound_set_start_delay(ma_sound* pSound, ma_uint64 delayInMilliseconds);
MA_API ma_result ma_sound_set_stop_delay(ma_sound* pSound, ma_uint64 delayInMilliseconds);
MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound);
MA_API ma_result ma_sound_get_time_in_frames(const ma_sound* pSound, ma_uint64* pTimeInFrames);
MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */
MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor);
MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength);
MA_API ma_result ma_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_API void ma_sound_group_uninit(ma_engine* pEngine, ma_sound_group* pGroup); /* Not thread-safe. */
......@@ -5244,7 +5248,7 @@ static ma_result ma_engine_effect_set_time(ma_engine_effect* pEffect, ma_uint64
}
static MA_INLINE ma_result ma_sound_stop_internal(ma_engine* pEngine, ma_sound* pSound);
static MA_INLINE ma_result ma_sound_stop_internal(ma_sound* pSound);
static MA_INLINE ma_result ma_sound_group_stop_internal(ma_engine* pEngine, ma_sound_group* pGroup);
MA_API ma_engine_config ma_engine_config_init_default()
......@@ -5322,7 +5326,7 @@ static void ma_engine_mix_sound(ma_engine* pEngine, ma_sound_group* pGroup, ma_s
/* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */
if (result == MA_AT_END) {
ma_sound_stop_internal(pEngine, pSound);
ma_sound_stop_internal(pSound);
c89atomic_exchange_32(&pSound->atEnd, MA_TRUE); /* This will be set to false in ma_sound_start(). */
}
......@@ -5342,7 +5346,7 @@ static void ma_engine_mix_sound(ma_engine* pEngine, ma_sound_group* pGroup, ma_s
/* Stop the sound if the delay has been reached. */
if (pSound->stopDelayInEngineFramesRemaining == 0) {
ma_sound_stop_internal(pEngine, pSound);
ma_sound_stop_internal(pSound);
}
}
}
......@@ -5751,12 +5755,114 @@ MA_API ma_result ma_engine_listener_set_rotation(ma_engine* pEngine, ma_quat rot
}
static ma_result ma_sound_detach(ma_engine* pEngine, ma_sound* pSound)
MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup)
{
ma_result result;
ma_sound* pSound = NULL;
ma_sound* pNextSound = NULL;
ma_uint32 dataSourceFlags = 0;
if (pEngine == NULL || pFilePath == NULL) {
return MA_INVALID_ARGS;
}
if (pGroup == NULL) {
pGroup = &pEngine->masterSoundGroup;
}
dataSourceFlags |= MA_DATA_SOURCE_FLAG_ASYNC;
/*
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.
*/
for (pNextSound = pGroup->pFirstSoundInGroup; pNextSound != NULL; pNextSound = pNextSound->pNextSoundInGroup) {
if (pNextSound->_isInternal) {
/*
We need to check that atEnd flag to determine if this sound is available. The problem is that another thread might be wanting to acquire this
sound at the same time. We want to avoid as much locking as possible, so we'll do this as a compare and swap.
*/
if (c89atomic_compare_and_swap_32(&pNextSound->atEnd, MA_TRUE, MA_FALSE) == MA_TRUE) {
/* We got it. */
pSound = pNextSound;
break;
} else {
/* The sound is not available for recycling. Move on to the next one. */
}
}
}
if (pSound != NULL) {
/*
An existing sound is being recycled. There's no need to allocate memory or re-insert into the group (it's already there). All we need to do is replace
the data source. The at-end flag has already been unset, and it will marked as playing at the end of this function.
*/
/* The at-end flag should have been set to false when we acquired the sound for recycling. */
MA_ASSERT(pSound->atEnd == MA_FALSE);
/* We're just going to reuse the same data source as before so we need to make sure we uninitialize the old one first. */
if (pSound->pDataSource != NULL) { /* <-- Safety. Should never happen. */
MA_ASSERT(pSound->ownsDataSource == MA_TRUE);
ma_resource_manager_data_source_uninit(&pSound->resourceManagerDataSource);
}
/* 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;
}
/* 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. */
pSound->pDataSource = &pSound->resourceManagerDataSource;
/* 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__malloc_from_callbacks(sizeof(*pSound), &pEngine->allocationCallbacks/*, MA_ALLOCATION_TYPE_SOUND*/); /* TODO: This can certainly be optimized. */
if (pSound == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_sound_init_from_file(pEngine, pFilePath, dataSourceFlags, NULL, pGroup, pSound);
if (result != MA_SUCCESS) {
ma__free_from_callbacks(pEngine, &pEngine->allocationCallbacks);
return result;
}
/* 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. */
ma_sound_uninit(pSound);
return result;
}
return MA_SUCCESS;
}
static ma_result ma_sound_detach(ma_sound* pSound)
{
ma_sound_group* pGroup;
MA_ASSERT(pEngine != NULL);
MA_ASSERT(pSound != NULL);
MA_ASSERT(pSound != NULL);
pGroup = pSound->pGroup;
MA_ASSERT(pGroup != NULL);
......@@ -5805,11 +5911,10 @@ static ma_result ma_sound_detach(ma_engine* pEngine, ma_sound* pSound)
return MA_SUCCESS;
}
static ma_result ma_sound_attach(ma_engine* pEngine, ma_sound* pSound, ma_sound_group* pGroup)
static ma_result ma_sound_attach(ma_sound* pSound, ma_sound_group* pGroup)
{
MA_ASSERT(pEngine != NULL);
MA_ASSERT(pSound != NULL);
MA_ASSERT(pGroup != NULL);
MA_ASSERT(pSound != NULL);
MA_ASSERT(pGroup != NULL);
MA_ASSERT(pSound->pGroup == NULL);
/* This should only ever be called when the sound is first initialized which means we should never be in a playing state. */
......@@ -5870,7 +5975,7 @@ static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, ma_
}
/* By default the sound needs to be added to the master group. */
result = ma_sound_attach(pEngine, pSound, pGroup);
result = ma_sound_attach(pSound, pGroup);
if (result != MA_SUCCESS) {
return result; /* Should never happen. Failed to attach the sound to the group. */
}
......@@ -5900,6 +6005,8 @@ MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePa
return MA_INVALID_ARGS;
}
pSound->pEngine = pEngine;
/* We need to user the resource manager to load the data source. */
result = ma_resource_manager_data_source_init(pEngine->pResourceManager, pFilePath, flags, NULL, &pSound->resourceManagerDataSource);
if (result != MA_SUCCESS) {
......@@ -5928,27 +6035,28 @@ MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_sour
}
MA_ZERO_OBJECT(pSound);
pSound->pEngine = pEngine;
return ma_sound_init_from_data_source_internal(pEngine, pDataSource, flags, pGroup, pSound);
}
MA_API void ma_sound_uninit(ma_engine* pEngine, ma_sound* pSound)
MA_API void ma_sound_uninit(ma_sound* pSound)
{
ma_result result;
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return;
}
/* 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. */
result = ma_sound_detach(pEngine, pSound);
result = ma_sound_detach(pSound);
if (result != MA_SUCCESS) {
return;
}
......@@ -5974,9 +6082,9 @@ MA_API void ma_sound_uninit(ma_engine* pEngine, ma_sound* pSound)
#endif
}
MA_API ma_result ma_sound_start(ma_engine* pEngine, ma_sound* pSound)
MA_API ma_result ma_sound_start(ma_sound* pSound)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
......@@ -5999,19 +6107,18 @@ MA_API ma_result ma_sound_start(ma_engine* pEngine, ma_sound* pSound)
return MA_SUCCESS;
}
static MA_INLINE ma_result ma_sound_stop_internal(ma_engine* pEngine, ma_sound* pSound)
static MA_INLINE ma_result ma_sound_stop_internal(ma_sound* pSound)
{
MA_ASSERT(pEngine != NULL);
MA_ASSERT(pSound != NULL);
MA_ASSERT(pSound != NULL);
c89atomic_exchange_32(&pSound->isPlaying, MA_FALSE);
return MA_SUCCESS;
}
MA_API ma_result ma_sound_stop(ma_engine* pEngine, ma_sound* pSound)
MA_API ma_result ma_sound_stop(ma_sound* pSound)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
......@@ -6019,15 +6126,15 @@ MA_API ma_result ma_sound_stop(ma_engine* pEngine, ma_sound* pSound)
/* Stop immediately if we don't have a delay. */
if (pSound->stopDelayInEngineFrames == 0) {
ma_sound_stop_internal(pEngine, pSound);
ma_sound_stop_internal(pSound);
}
return MA_SUCCESS;
}
MA_API ma_result ma_sound_set_volume(ma_engine* pEngine, ma_sound* pSound, float volume)
MA_API ma_result ma_sound_set_volume(ma_sound* pSound, float volume)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
......@@ -6036,18 +6143,18 @@ MA_API ma_result ma_sound_set_volume(ma_engine* pEngine, ma_sound* pSound, float
return MA_SUCCESS;
}
MA_API ma_result ma_sound_set_gain_db(ma_engine* pEngine, ma_sound* pSound, float gainDB)
MA_API ma_result ma_sound_set_gain_db(ma_sound* pSound, float gainDB)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
return ma_sound_set_volume(pEngine, pSound, ma_gain_db_to_factor(gainDB));
return ma_sound_set_volume(pSound, ma_gain_db_to_factor(gainDB));
}
MA_API ma_result ma_sound_set_effect(ma_engine* pEngine, ma_sound* pSound, ma_effect* pEffect)
MA_API ma_result ma_sound_set_effect(ma_sound* pSound, ma_effect* pEffect)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
......@@ -6056,9 +6163,9 @@ MA_API ma_result ma_sound_set_effect(ma_engine* pEngine, ma_sound* pSound, ma_ef
return MA_SUCCESS;
}
MA_API ma_result ma_sound_set_pitch(ma_engine* pEngine, ma_sound* pSound, float pitch)
MA_API ma_result ma_sound_set_pitch(ma_sound* pSound, float pitch)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
......@@ -6067,36 +6174,36 @@ MA_API ma_result ma_sound_set_pitch(ma_engine* pEngine, ma_sound* pSound, float
return MA_SUCCESS;
}
MA_API ma_result ma_sound_set_pan(ma_engine* pEngine, ma_sound* pSound, float pan)
MA_API ma_result ma_sound_set_pan(ma_sound* pSound, float pan)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
return ma_panner_set_pan(&pSound->effect.panner, pan);
}
MA_API ma_result ma_sound_set_position(ma_engine* pEngine, ma_sound* pSound, ma_vec3 position)
MA_API ma_result ma_sound_set_position(ma_sound* pSound, ma_vec3 position)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
return ma_spatializer_set_position(&pSound->effect.spatializer, position);
}
MA_API ma_result ma_sound_set_rotation(ma_engine* pEngine, ma_sound* pSound, ma_quat rotation)
MA_API ma_result ma_sound_set_rotation(ma_sound* pSound, ma_quat rotation)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
return ma_spatializer_set_rotation(&pSound->effect.spatializer, rotation);
}
MA_API ma_result ma_sound_set_looping(ma_engine* pEngine, ma_sound* pSound, ma_bool32 isLooping)
MA_API ma_result ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
......@@ -6117,66 +6224,70 @@ MA_API ma_result ma_sound_set_looping(ma_engine* pEngine, ma_sound* pSound, ma_b
return MA_SUCCESS;
}
MA_API ma_result ma_sound_set_fade_point_in_frames(ma_engine* pEngine, ma_sound* pSound, ma_uint32 fadePointIndex, float volumeBeg, float volumeEnd, ma_uint64 timeInFramesBeg, ma_uint64 timeInFramesEnd)
MA_API ma_result ma_sound_set_fade_point_in_frames(ma_sound* pSound, ma_uint32 fadePointIndex, float volumeBeg, float volumeEnd, ma_uint64 timeInFramesBeg, ma_uint64 timeInFramesEnd)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
return ma_dual_fader_set_fade(&pSound->effect.fader, fadePointIndex, volumeBeg, volumeEnd, timeInFramesBeg, timeInFramesEnd);
}
MA_API ma_result ma_sound_set_fade_point_in_milliseconds(ma_engine* pEngine, ma_sound* pSound, ma_uint32 fadePointIndex, float volumeBeg, float volumeEnd, ma_uint64 timeInMillisecondsBeg, ma_uint64 timeInMillisecondsEnd)
MA_API ma_result ma_sound_set_fade_point_in_milliseconds(ma_sound* pSound, ma_uint32 fadePointIndex, float volumeBeg, float volumeEnd, ma_uint64 timeInMillisecondsBeg, ma_uint64 timeInMillisecondsEnd)
{
ma_uint64 timeInFramesBeg;
ma_uint64 timeInFramesEnd;
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
timeInFramesBeg = (timeInMillisecondsBeg * pSound->effect.fader.config.sampleRate) / 1000;
timeInFramesEnd = (timeInMillisecondsEnd * pSound->effect.fader.config.sampleRate) / 1000;
return ma_sound_set_fade_point_in_frames(pEngine, pSound, fadePointIndex, volumeBeg, volumeEnd, timeInFramesBeg, timeInFramesEnd);
return ma_sound_set_fade_point_in_frames(pSound, fadePointIndex, volumeBeg, volumeEnd, timeInFramesBeg, timeInFramesEnd);
}
MA_API ma_result ma_sound_set_start_delay(ma_engine* pEngine, ma_sound* pSound, ma_uint64 delayInMilliseconds)
MA_API ma_result ma_sound_set_start_delay(ma_sound* pSound, ma_uint64 delayInMilliseconds)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
MA_ASSERT(pSound->pEngine != NULL);
/*
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.
*/
pSound->startDelayInEngineFrames = (pEngine->sampleRate * delayInMilliseconds) / 1000;
pSound->startDelayInEngineFrames = (pSound->pEngine->sampleRate * delayInMilliseconds) / 1000;
return MA_SUCCESS;
}
MA_API ma_result ma_sound_set_stop_delay(ma_engine* pEngine, ma_sound* pSound, ma_uint64 delayInMilliseconds)
MA_API ma_result ma_sound_set_stop_delay(ma_sound* pSound, ma_uint64 delayInMilliseconds)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
pSound->stopDelayInEngineFrames = (pEngine->sampleRate * delayInMilliseconds) / 1000;
MA_ASSERT(pSound->pEngine != NULL);
pSound->stopDelayInEngineFrames = (pSound->pEngine->sampleRate * delayInMilliseconds) / 1000;
return MA_SUCCESS;
}
MA_API ma_bool32 ma_sound_at_end(ma_engine* pEngine, const ma_sound* pSound)
MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_FALSE;
}
return pSound->atEnd;
}
MA_API ma_result ma_sound_get_time_in_frames(ma_engine* pEngine, const ma_sound* pSound, ma_uint64* pTimeInFrames)
MA_API ma_result ma_sound_get_time_in_frames(const ma_sound* pSound, ma_uint64* pTimeInFrames)
{
if (pTimeInFrames == NULL) {
return MA_INVALID_ARGS;
......@@ -6184,7 +6295,7 @@ MA_API ma_result ma_sound_get_time_in_frames(ma_engine* pEngine, const ma_sound*
*pTimeInFrames = 0;
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
......@@ -6193,9 +6304,9 @@ MA_API ma_result ma_sound_get_time_in_frames(ma_engine* pEngine, const ma_sound*
return MA_SUCCESS;
}
MA_API ma_result ma_sound_seek_to_pcm_frame(ma_engine* pEngine, ma_sound* pSound, ma_uint64 frameIndex)
MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
......@@ -6221,133 +6332,33 @@ MA_API ma_result ma_sound_seek_to_pcm_frame(ma_engine* pEngine, ma_sound* pSound
return MA_SUCCESS;
}
MA_API ma_result ma_sound_get_data_format(ma_engine* pEngine, ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate);
}
MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_engine* pEngine, ma_sound* pSound, ma_uint64* pCursor)
MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor);
}
MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_engine* pEngine, ma_sound* pSound, ma_uint64* pLength)
MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength)
{
if (pEngine == NULL || pSound == NULL) {
if (pSound == NULL) {
return MA_INVALID_ARGS;
}
return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength);
}
MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup)
{
ma_result result;
ma_sound* pSound = NULL;
ma_sound* pNextSound = NULL;
ma_uint32 dataSourceFlags = 0;
if (pEngine == NULL || pFilePath == NULL) {
return MA_INVALID_ARGS;
}
if (pGroup == NULL) {
pGroup = &pEngine->masterSoundGroup;
}
dataSourceFlags |= MA_DATA_SOURCE_FLAG_ASYNC;
/*
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.
*/
for (pNextSound = pGroup->pFirstSoundInGroup; pNextSound != NULL; pNextSound = pNextSound->pNextSoundInGroup) {
if (pNextSound->_isInternal) {
/*
We need to check that atEnd flag to determine if this sound is available. The problem is that another thread might be wanting to acquire this
sound at the same time. We want to avoid as much locking as possible, so we'll do this as a compare and swap.
*/
if (c89atomic_compare_and_swap_32(&pNextSound->atEnd, MA_TRUE, MA_FALSE) == MA_TRUE) {
/* We got it. */
pSound = pNextSound;
break;
} else {
/* The sound is not available for recycling. Move on to the next one. */
}
}
}
if (pSound != NULL) {
/*
An existing sound is being recycled. There's no need to allocate memory or re-insert into the group (it's already there). All we need to do is replace
the data source. The at-end flag has already been unset, and it will marked as playing at the end of this function.
*/
/* The at-end flag should have been set to false when we acquired the sound for recycling. */
MA_ASSERT(pSound->atEnd == MA_FALSE);
/* We're just going to reuse the same data source as before so we need to make sure we uninitialize the old one first. */
if (pSound->pDataSource != NULL) { /* <-- Safety. Should never happen. */
MA_ASSERT(pSound->ownsDataSource == MA_TRUE);
ma_resource_manager_data_source_uninit(&pSound->resourceManagerDataSource);
}
/* 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;
}
/* 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. */
pSound->pDataSource = &pSound->resourceManagerDataSource;
/* 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(pEngine, 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__malloc_from_callbacks(sizeof(*pSound), &pEngine->allocationCallbacks/*, MA_ALLOCATION_TYPE_SOUND*/); /* TODO: This can certainly be optimized. */
if (pSound == NULL) {
return MA_OUT_OF_MEMORY;
}
result = ma_sound_init_from_file(pEngine, pFilePath, dataSourceFlags, NULL, pGroup, pSound);
if (result != MA_SUCCESS) {
ma__free_from_callbacks(pEngine, &pEngine->allocationCallbacks);
return result;
}
/* 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. */
ma_sound_uninit(pEngine, pSound);
return result;
}
return MA_SUCCESS;
}
static ma_result ma_sound_group_attach(ma_engine* pEngine, ma_sound_group* pGroup, ma_sound_group* pParentGroup)
......@@ -6432,6 +6443,8 @@ MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_sound_group* pParent
return MA_INVALID_ARGS;
}
pGroup->pEngine = pEngine;
/* Use the master group if the parent group is NULL, so long as it's not the master group itself. */
if (pParentGroup == NULL && pGroup != &pEngine->masterSoundGroup) {
pParentGroup = &pEngine->masterSoundGroup;
......@@ -6492,6 +6505,8 @@ static void ma_sound_group_uninit_all_internal_sounds(ma_engine* pEngine, ma_sou
{
ma_sound* pCurrentSound;
(void)pEngine;
/* We need to be careful here that we keep our iteration valid. */
pCurrentSound = pGroup->pFirstSoundInGroup;
while (pCurrentSound != NULL) {
......@@ -6499,7 +6514,7 @@ static void ma_sound_group_uninit_all_internal_sounds(ma_engine* pEngine, ma_sou
pCurrentSound = pCurrentSound->pNextSoundInGroup;
if (pSoundToDelete->_isInternal) {
ma_sound_uninit(pEngine, pSoundToDelete);
ma_sound_uninit(pSoundToDelete);
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment