Commit 5df38de8 authored by David Reid's avatar David Reid

Improvements to the async notification system.

These changes expand on the notification system which allows a program
to be notified on when a sound has reached various stages in the async
loading process. There are two stages:

  1) Decoder initialization
  2) Completion of decoding

The previous system has the following problems:

  * There's no way to wait on only the decoder initialization
  * The callback doesn't work well if you want to pass it through
    multiple layers. Example:
    - Client wants to wait for the sound to complete
    - Pass the callback into ma_sound_init_from_file(), which passes
      it into ma_resource_manager_data_source_init(). The latter will
      fire the callback, but will do so before ma_sound_init_from_file()
      has returned. The client will think the sound has finished
      loading and will reference the `ma_sound` object before it's
      completed initialization.
  * It's not easy to wait on object in bulk. Clients may want to pump a
    bunch of ASYNC sounds in one go, and then just have a single
    notification for the group.

The new system introduces the notion of a "fence". A fence contains a
counter which is incremented and decremented from anywhere. It also has
a wait routine which will block until the counter hits zero:

  ma_fence_acquire() - Increments the counter
  ma_fence_release() - Decrements the counter
  ma_fence_wait()    - Blocks until the counter is 0.

The fence system can be used to wait for a number of sounds to finish
decoding rather than only one at a time, which is the case with the
previous system. Example:

```
/* Fences must be initialized before use. */
ma_fence_init(&fence);

/* Loop over each sound and start loading them, passing in the fence. */
for each sound in sounds
{
    flags = MA_DATA_SOURCE_FLAG_DECODE | MA_DATA_SOURCE_FLAG_ASYNC;
    ma_sound_init_from_file(&engine, pFilePath, flags, &group, &fence, &sound);
}

... do some other stuff while sounds are loading ...

/* Wait for sounds to finish loading. */
ma_fence_wait(&fence);
ma_fence_uninit(&fence);
```

The above example initializes a sound, but it can also be used by
resource managed data sources. When loading data sources directly from
the resource manager, you can specify a second fence which is used to
only wait until the internal decoder has been initialized.
parent b4d6c37d
...@@ -1199,6 +1199,21 @@ MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* p ...@@ -1199,6 +1199,21 @@ MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* p
MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent); MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent);
typedef struct
{
ma_async_notification* pNotification;
ma_fence* pFence;
} ma_pipeline_stage_notification;
typedef struct
{
ma_pipeline_stage_notification init; /* Initialization of the decoder. */
ma_pipeline_stage_notification done; /* Decoding fully completed. */
} ma_pipeline_notifications;
MA_API ma_pipeline_notifications ma_pipeline_notifications_init(void);
typedef struct typedef struct
{ {
union union
...@@ -1224,53 +1239,52 @@ typedef struct ...@@ -1224,53 +1239,52 @@ typedef struct
wchar_t* pFilePathW; wchar_t* pFilePathW;
ma_bool32 decode; /* When set to true, the data buffer will be decoded. Otherwise it'll be encoded and will use a decoder for the connector. */ ma_bool32 decode; /* When set to true, the data buffer will be decoded. Otherwise it'll be encoded and will use a decoder for the connector. */
ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */
ma_async_notification* pCompletedNotification; /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_PAGE_DATA_BUFFER_NODE when decoding. */ ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_PAGE_DATA_BUFFER_NODE when decoding. */
ma_fence* pInitFence; /* Released when initialization of the decoder is complete. */
ma_fence* pDoneFence; /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */
} loadDataBufferNode; } loadDataBufferNode;
struct struct
{ {
ma_resource_manager_data_buffer_node* pDataBufferNode; ma_resource_manager_data_buffer_node* pDataBufferNode;
ma_async_notification* pNotification; ma_async_notification* pDoneNotification;
ma_fence* pDoneFence;
} freeDataBufferNode; } freeDataBufferNode;
struct struct
{ {
ma_resource_manager_data_buffer_node* pDataBufferNode; ma_resource_manager_data_buffer_node* pDataBufferNode;
ma_decoder* pDecoder; ma_decoder* pDecoder;
ma_async_notification* pCompletedNotification; /* Signalled when the data buffer has been fully decoded. */ ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */
ma_fence* pDoneFence; /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */
} pageDataBufferNode; } pageDataBufferNode;
struct struct
{ {
ma_resource_manager_data_buffer* pDataBuffer; ma_resource_manager_data_buffer* pDataBuffer;
ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */
ma_async_notification* pCompletedNotification; /* Signalled when the data buffer has been fully decoded. */ ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */
ma_fence* pInitFence; /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */
ma_fence* pDoneFence; /* Released when the data buffer has been fully decoded. */
} loadDataBuffer; } loadDataBuffer;
struct struct
{ {
ma_resource_manager_data_buffer* pDataBuffer; ma_resource_manager_data_buffer* pDataBuffer;
ma_async_notification* pNotification; ma_async_notification* pDoneNotification;
ma_fence* pDoneFence;
} freeDataBuffer; } freeDataBuffer;
struct
{
ma_resource_manager_data_buffer* pDataBuffer;
ma_decoder* pDecoder;
ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized, but not necessarily fully decoded. */
ma_async_notification* pCompletedNotification; /* Signalled when the data buffer has been fully decoded. */
void* pData;
size_t dataSizeInBytes;
ma_uint64 decodedFrameCount;
} pageDataBuffer;
struct struct
{ {
ma_resource_manager_data_stream* pDataStream; ma_resource_manager_data_stream* pDataStream;
char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */ char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */
wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */ wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */
ma_async_notification* pNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */ ma_async_notification* pInitNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */
ma_fence* pInitFence;
} loadDataStream; } loadDataStream;
struct struct
{ {
ma_resource_manager_data_stream* pDataStream; ma_resource_manager_data_stream* pDataStream;
ma_async_notification* pNotification; ma_async_notification* pDoneNotification;
ma_fence* pDoneFence;
} freeDataStream; } freeDataStream;
struct struct
{ {
...@@ -1483,8 +1497,8 @@ MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResou ...@@ -1483,8 +1497,8 @@ MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResou
MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName); MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName);
/* Data Buffers. */ /* Data Buffers. */
MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, ma_async_notification* pNotification, ma_resource_manager_data_buffer* pDataBuffer); MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer);
MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, ma_async_notification* pNotification, ma_resource_manager_data_buffer* pDataBuffer); MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer);
MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer); MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer);
MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer); MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer);
MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
...@@ -1498,8 +1512,8 @@ MA_API ma_result ma_resource_manager_data_buffer_get_looping(const ma_resource_m ...@@ -1498,8 +1512,8 @@ MA_API ma_result ma_resource_manager_data_buffer_get_looping(const ma_resource_m
MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames); MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames);
/* Data Streams. */ /* Data Streams. */
MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, ma_async_notification* pNotification, ma_resource_manager_data_stream* pDataStream); MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream);
MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, ma_async_notification* pNotification, ma_resource_manager_data_stream* pDataStream); MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream);
MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream); MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream);
MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex); MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex);
...@@ -1514,8 +1528,8 @@ MA_API ma_result ma_resource_manager_data_stream_get_looping(const ma_resource_m ...@@ -1514,8 +1528,8 @@ MA_API ma_result ma_resource_manager_data_stream_get_looping(const ma_resource_m
MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames); MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames);
/* Data Sources. */ /* Data Sources. */
MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, ma_async_notification* pNotification, ma_resource_manager_data_source* pDataSource); MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource);
MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, ma_async_notification* pNotification, ma_resource_manager_data_source* pDataSource); MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource);
MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource); MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource);
MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource); MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource);
MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
...@@ -1811,6 +1825,7 @@ typedef struct ...@@ -1811,6 +1825,7 @@ typedef struct
ma_uint32 channelsIn; /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */ ma_uint32 channelsIn; /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */
ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. */ ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. */
ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */ ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */
ma_fence* pDoneFence; /* Released when the resource manager has finished decoding the entire sound. Not used with streams. */
} ma_sound_config; } ma_sound_config;
MA_API ma_sound_config ma_sound_config_init(void); MA_API ma_sound_config ma_sound_config_init(void);
...@@ -1917,8 +1932,8 @@ MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ...@@ -1917,8 +1932,8 @@ MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath,
#ifndef MA_NO_RESOURCE_MANAGER #ifndef MA_NO_RESOURCE_MANAGER
MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound);
MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound);
MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound);
#endif #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 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);
...@@ -5819,6 +5834,7 @@ MA_API ma_result ma_fence_acquire(ma_fence* pFence) ...@@ -5819,6 +5834,7 @@ MA_API ma_result ma_fence_acquire(ma_fence* pFence)
/* Make sure we're not about to exceed our maximum value. */ /* Make sure we're not about to exceed our maximum value. */
if (newCounter > MA_FENCE_COUNTER_MAX) { if (newCounter > MA_FENCE_COUNTER_MAX) {
MA_ASSERT(MA_FALSE);
return MA_OUT_OF_RANGE; return MA_OUT_OF_RANGE;
} }
...@@ -5826,6 +5842,7 @@ MA_API ma_result ma_fence_acquire(ma_fence* pFence) ...@@ -5826,6 +5842,7 @@ MA_API ma_result ma_fence_acquire(ma_fence* pFence)
return MA_SUCCESS; return MA_SUCCESS;
} else { } else {
if (oldCounter == MA_FENCE_COUNTER_MAX) { if (oldCounter == MA_FENCE_COUNTER_MAX) {
MA_ASSERT(MA_FALSE);
return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */ return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */
} }
} }
...@@ -5846,6 +5863,7 @@ MA_API ma_result ma_fence_release(ma_fence* pFence) ...@@ -5846,6 +5863,7 @@ MA_API ma_result ma_fence_release(ma_fence* pFence)
ma_uint32 newCounter = oldCounter - 1; ma_uint32 newCounter = oldCounter - 1;
if (oldCounter == 0) { if (oldCounter == 0) {
MA_ASSERT(MA_FALSE);
return MA_INVALID_OPERATION; /* Acquire/release mismatch. */ return MA_INVALID_OPERATION; /* Acquire/release mismatch. */
} }
...@@ -5857,6 +5875,7 @@ MA_API ma_result ma_fence_release(ma_fence* pFence) ...@@ -5857,6 +5875,7 @@ MA_API ma_result ma_fence_release(ma_fence* pFence)
return MA_SUCCESS; return MA_SUCCESS;
} else { } else {
if (oldCounter == 0) { if (oldCounter == 0) {
MA_ASSERT(MA_FALSE);
return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */ return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */
} }
} }
...@@ -5995,6 +6014,47 @@ MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* ...@@ -5995,6 +6014,47 @@ MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event*
MA_API ma_pipeline_notifications ma_pipeline_notifications_init(void)
{
ma_pipeline_notifications notifications;
MA_ZERO_OBJECT(&notifications);
return notifications;
}
static void ma_pipeline_notifications_signal_all_notifications(const ma_pipeline_notifications* pPipelineNotifications)
{
if (pPipelineNotifications == NULL) {
return;
}
if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); }
if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); }
}
static void ma_pipeline_notifications_acquire_all_fences(const ma_pipeline_notifications* pPipelineNotifications)
{
if (pPipelineNotifications == NULL) {
return;
}
if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); }
if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); }
}
static void ma_pipeline_notifications_release_all_fences(const ma_pipeline_notifications* pPipelineNotifications)
{
if (pPipelineNotifications == NULL) {
return;
}
if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); }
if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); }
}
#define MA_JOB_ID_NONE ~((ma_uint64)0) #define MA_JOB_ID_NONE ~((ma_uint64)0)
#define MA_JOB_SLOT_NONE (ma_uint16)(~0) #define MA_JOB_SLOT_NONE (ma_uint16)(~0)
...@@ -6934,7 +6994,7 @@ static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourc ...@@ -6934,7 +6994,7 @@ static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourc
} }
} }
static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, ma_async_notification* pInitNotification) static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, ma_async_notification* pInitNotification, ma_fence* pInitFence)
{ {
ma_result result; ma_result result;
...@@ -6995,6 +7055,10 @@ static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_mana ...@@ -6995,6 +7055,10 @@ static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_mana
if (pInitNotification != NULL) { if (pInitNotification != NULL) {
ma_async_notification_signal(pInitNotification); ma_async_notification_signal(pInitNotification);
} }
if (pInitFence != NULL) {
ma_fence_release(pInitFence);
}
} }
/* At this point the backend should be initialized. We do *not* want to set pDataSource->result here - that needs to be done at a higher level to ensure it's done as the last step. */ /* At this point the backend should be initialized. We do *not* want to set pDataSource->result here - that needs to be done at a higher level to ensure it's done as the last step. */
...@@ -7250,7 +7314,7 @@ static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resour ...@@ -7250,7 +7314,7 @@ static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resour
return result; return result;
} }
static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_resource_manager_data_buffer_node** ppDataBufferNode) static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_data_buffer_node** ppDataBufferNode)
{ {
ma_result result = MA_SUCCESS; ma_result result = MA_SUCCESS;
ma_bool32 nodeAlreadyExists = MA_FALSE; ma_bool32 nodeAlreadyExists = MA_FALSE;
...@@ -7364,19 +7428,33 @@ static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manage ...@@ -7364,19 +7428,33 @@ static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manage
ma_resource_manager_inline_notification_init(pResourceManager, &initNotification); ma_resource_manager_inline_notification_init(pResourceManager, &initNotification);
} }
/* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */
if (pInitFence != NULL) { ma_fence_acquire(pInitFence); }
if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); }
/* We now have everything we need to post the job to the job thread. */ /* We now have everything we need to post the job to the job thread. */
job = ma_job_init(MA_JOB_LOAD_DATA_BUFFER_NODE); job = ma_job_init(MA_JOB_LOAD_DATA_BUFFER_NODE);
job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
job.loadDataBufferNode.pDataBufferNode = pDataBufferNode; job.loadDataBufferNode.pDataBufferNode = pDataBufferNode;
job.loadDataBufferNode.pFilePath = pFilePathCopy; job.loadDataBufferNode.pFilePath = pFilePathCopy;
job.loadDataBufferNode.pFilePathW = pFilePathWCopy; job.loadDataBufferNode.pFilePathW = pFilePathWCopy;
job.loadDataBufferNode.decode = (flags & MA_DATA_SOURCE_FLAG_DECODE ) != 0; job.loadDataBufferNode.decode = (flags & MA_DATA_SOURCE_FLAG_DECODE ) != 0;
job.loadDataBufferNode.pInitNotification = ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : NULL; job.loadDataBufferNode.pInitNotification = ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : NULL;
job.loadDataBufferNode.pCompletedNotification = NULL; job.loadDataBufferNode.pDoneNotification = NULL;
job.loadDataBufferNode.pInitFence = pInitFence;
job.loadDataBufferNode.pDoneFence = pDoneFence;
result = ma_resource_manager_post_job(pResourceManager, &job); result = ma_resource_manager_post_job(pResourceManager, &job);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
/* TODO: Post an error message. Failed to post job. Probably ran out of memory. */ /* TODO: Post an error message. Failed to post job. Probably ran out of memory. */
/*
Fences were acquired before posting the job, but since the job was not able to
be posted, we need to make sure we release them so nothing gets stuck waiting.
*/
if (pInitFence != NULL) { ma_fence_release(pInitFence); }
if (pDoneFence != NULL) { ma_fence_release(pDoneFence); }
ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/); ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/);
ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/); ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/);
goto done; goto done;
...@@ -7476,8 +7554,8 @@ done: ...@@ -7476,8 +7554,8 @@ done:
The init notification needs to be uninitialized. This will be used if the node does not already The init notification needs to be uninitialized. This will be used if the node does not already
exist, and we've specified ASYNC | WAIT_INIT. exist, and we've specified ASYNC | WAIT_INIT.
*/ */
if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager) { if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_DATA_SOURCE_FLAG_ASYNC) != 0) {
if ((flags & MA_DATA_SOURCE_FLAG_ASYNC) != 0 && (flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { if ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
ma_resource_manager_inline_notification_uninit(&initNotification); ma_resource_manager_inline_notification_uninit(&initNotification);
} }
} }
...@@ -7632,14 +7710,23 @@ static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable = ...@@ -7632,14 +7710,23 @@ static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable =
ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames
}; };
static ma_result ma_resource_manager_data_buffer_init_internal(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, ma_async_notification* pNotification, ma_resource_manager_data_buffer* pDataBuffer) static ma_result ma_resource_manager_data_buffer_init_internal(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer)
{ {
ma_result result; ma_result result = MA_SUCCESS;
ma_resource_manager_data_buffer_node* pDataBufferNode; ma_resource_manager_data_buffer_node* pDataBufferNode;
ma_data_source_config dataSourceConfig; ma_data_source_config dataSourceConfig;
ma_bool32 async; ma_bool32 async;
ma_pipeline_notifications notifications;
if (pNotifications != NULL) {
notifications = *pNotifications;
pNotifications = NULL; /* From here on out we should be referencing `notifications` instead of `pNotifications`. Set this to NULL to catch errors at testing time. */
} else {
MA_ZERO_OBJECT(&notifications);
}
if (pDataBuffer == NULL) { if (pDataBuffer == NULL) {
ma_pipeline_notifications_signal_all_notifications(&notifications);
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
...@@ -7652,95 +7739,127 @@ static ma_result ma_resource_manager_data_buffer_init_internal(ma_resource_manag ...@@ -7652,95 +7739,127 @@ static ma_result ma_resource_manager_data_buffer_init_internal(ma_resource_manag
async = (flags & MA_DATA_SOURCE_FLAG_ASYNC) != 0; async = (flags & MA_DATA_SOURCE_FLAG_ASYNC) != 0;
/* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */ /*
result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, NULL, &pDataBufferNode); Fences need to be acquired before doing anything. These must be aquired and released outside of
if (result != MA_SUCCESS) { the node to ensure there's no holes where ma_fence_wait() could prematurely return before the
return result; data buffer has completed initialization.
}
dataSourceConfig = ma_data_source_config_init();
dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable;
result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds);
if (result != MA_SUCCESS) {
ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
return result;
}
pDataBuffer->pResourceManager = pResourceManager;
pDataBuffer->pNode = pDataBufferNode;
pDataBuffer->flags = flags;
pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */
/* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */ When loading asynchronously, the node acquisition routine below will acquire the fences on this
if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) { thread and then release them on the async thread when the operation is complete.
/* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */
result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, NULL);
c89atomic_exchange_i32(&pDataBuffer->result, result);
if (pNotification != NULL) { These fences are always released at the "done" tag at the end of this function. They'll be
ma_async_notification_signal(pNotification); acquired a second if loading asynchronously. This double acquisition system is just done to
simplify code maintanence.
*/
ma_pipeline_notifications_acquire_all_fences(&notifications);
{
/* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */
result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode);
if (result != MA_SUCCESS) {
ma_pipeline_notifications_signal_all_notifications(&notifications);
goto done;
} }
} else {
/* The node's data supply isn't initialized yet. The caller has requested that we load asynchronously so we need to post a job to do this. */
ma_job job;
ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */
if ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { dataSourceConfig = ma_data_source_config_init();
ma_resource_manager_inline_notification_init(pResourceManager, &initNotification); dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable;
}
/* result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds);
The status of the data buffer needs to be set to MA_BUSY before posting the job so that the if (result != MA_SUCCESS) {
worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
than MA_BUSY, it'll assume an error and fall through to an early exit. ma_pipeline_notifications_signal_all_notifications(&notifications);
*/ goto done;
c89atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); }
job = ma_job_init(MA_JOB_LOAD_DATA_BUFFER); pDataBuffer->pResourceManager = pResourceManager;
job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); pDataBuffer->pNode = pDataBufferNode;
job.loadDataBuffer.pDataBuffer = pDataBuffer; pDataBuffer->flags = flags;
job.loadDataBuffer.pCompletedNotification = pNotification; pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */
job.loadDataBuffer.pInitNotification = ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : NULL;
result = ma_resource_manager_post_job(pResourceManager, &job); /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */
if (result != MA_SUCCESS) { if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) {
/* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */ /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */
/* TODO: Post an error here. */ result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, NULL, NULL);
c89atomic_exchange_i32(&pDataBuffer->result, result); c89atomic_exchange_i32(&pDataBuffer->result, result);
ma_pipeline_notifications_signal_all_notifications(&notifications);
goto done;
} else { } else {
/* The node's data supply isn't initialized yet. The caller has requested that we load asynchronously so we need to post a job to do this. */
ma_job job;
ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */
if ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { if ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
ma_resource_manager_inline_notification_wait(&initNotification); ma_resource_manager_inline_notification_init(pResourceManager, &initNotification);
}
/*
The status of the data buffer needs to be set to MA_BUSY before posting the job so that the
worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other
than MA_BUSY, it'll assume an error and fall through to an early exit.
*/
c89atomic_exchange_i32(&pDataBuffer->result, MA_BUSY);
/* Acquire fences a second time. These will be released by the async thread. */
ma_pipeline_notifications_acquire_all_fences(&notifications);
job = ma_job_init(MA_JOB_LOAD_DATA_BUFFER);
job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
job.loadDataBuffer.pDataBuffer = pDataBuffer;
job.loadDataBuffer.pInitNotification = ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification;
job.loadDataBuffer.pDoneNotification = notifications.done.pNotification;
job.loadDataBuffer.pInitFence = notifications.init.pFence;
job.loadDataBuffer.pDoneFence = notifications.done.pFence;
result = ma_resource_manager_post_job(pResourceManager, &job);
if (result != MA_SUCCESS) {
/* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */
/* TODO: Post an error here. */
c89atomic_exchange_i32(&pDataBuffer->result, result);
/* Release the fences after the result has been set on the data buffer. */
ma_pipeline_notifications_release_all_fences(&notifications);
} else {
if ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
ma_resource_manager_inline_notification_wait(&initNotification);
/* Make sure we return an error if initialization failed on the async thread. */ if (notifications.init.pNotification != NULL) {
result = ma_resource_manager_data_buffer_result(pDataBuffer); ma_async_notification_signal(notifications.init.pNotification);
if (result == MA_BUSY) { }
result = MA_SUCCESS;
} /* NOTE: Do not release the init fence here. It will have been done by the job. */
/* Make sure we return an error if initialization failed on the async thread. */
result = ma_resource_manager_data_buffer_result(pDataBuffer);
if (result == MA_BUSY) {
result = MA_SUCCESS;
}
}
} }
}
if ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { if ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
ma_resource_manager_inline_notification_uninit(&initNotification); ma_resource_manager_inline_notification_uninit(&initNotification);
}
} }
}
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
return result; goto done;
}
} }
done:
ma_pipeline_notifications_release_all_fences(&notifications);
return MA_SUCCESS; return result;
} }
MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, ma_async_notification* pNotification, ma_resource_manager_data_buffer* pDataBuffer) MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer)
{ {
return ma_resource_manager_data_buffer_init_internal(pResourceManager, pFilePath, NULL, 0, flags, pNotification, pDataBuffer); return ma_resource_manager_data_buffer_init_internal(pResourceManager, pFilePath, NULL, 0, flags, pNotifications, pDataBuffer);
} }
MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, ma_async_notification* pNotification, ma_resource_manager_data_buffer* pDataBuffer) MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer)
{ {
return ma_resource_manager_data_buffer_init_internal(pResourceManager, NULL, pFilePath, 0, flags, pNotification, pDataBuffer); return ma_resource_manager_data_buffer_init_internal(pResourceManager, NULL, pFilePath, 0, flags, pNotifications, pDataBuffer);
} }
MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer) MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer)
...@@ -7803,8 +7922,9 @@ MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data ...@@ -7803,8 +7922,9 @@ MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data
job = ma_job_init(MA_JOB_FREE_DATA_BUFFER); job = ma_job_init(MA_JOB_FREE_DATA_BUFFER);
job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
job.freeDataBuffer.pDataBuffer = pDataBuffer; job.freeDataBuffer.pDataBuffer = pDataBuffer;
job.freeDataBuffer.pNotification = &notification; job.freeDataBuffer.pDoneNotification = &notification;
job.freeDataBuffer.pDoneFence = NULL;
result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job); result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
...@@ -8127,18 +8247,18 @@ MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resourc ...@@ -8127,18 +8247,18 @@ MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resourc
MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags) MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags)
{ {
return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL); return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL);
} }
MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags) MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags)
{ {
return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL); return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL);
} }
static ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData) static ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData)
{ {
return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL); return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL);
} }
static ma_result ma_resource_manager_register_decoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) static ma_result ma_resource_manager_register_decoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
...@@ -8272,7 +8392,7 @@ static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable = ...@@ -8272,7 +8392,7 @@ static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable =
ma_resource_manager_data_stream_cb__get_length_in_pcm_frames ma_resource_manager_data_stream_cb__get_length_in_pcm_frames
}; };
static ma_result ma_resource_manager_data_stream_init_internal(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 flags, ma_async_notification* pNotification, ma_resource_manager_data_stream* pDataStream) static ma_result ma_resource_manager_data_stream_init_internal(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream)
{ {
ma_result result; ma_result result;
ma_data_source_config dataSourceConfig; ma_data_source_config dataSourceConfig;
...@@ -8281,12 +8401,17 @@ static ma_result ma_resource_manager_data_stream_init_internal(ma_resource_manag ...@@ -8281,12 +8401,17 @@ static ma_result ma_resource_manager_data_stream_init_internal(ma_resource_manag
ma_job job; ma_job job;
ma_bool32 waitBeforeReturning = MA_FALSE; ma_bool32 waitBeforeReturning = MA_FALSE;
ma_resource_manager_inline_notification waitNotification; ma_resource_manager_inline_notification waitNotification;
ma_pipeline_notifications notifications;
if (pDataStream == NULL) { if (pNotifications != NULL) {
if (pNotification != NULL) { notifications = *pNotifications;
ma_async_notification_signal(pNotification); pNotifications = NULL; /* From here on out, `notifications` should be used instead of `pNotifications`. Setting this to NULL to catch any errors at testing time. */
} } else {
MA_ZERO_OBJECT(&notifications);
}
if (pDataStream == NULL) {
ma_pipeline_notifications_signal_all_notifications(&notifications);
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
...@@ -8297,6 +8422,7 @@ static ma_result ma_resource_manager_data_stream_init_internal(ma_resource_manag ...@@ -8297,6 +8422,7 @@ static ma_result ma_resource_manager_data_stream_init_internal(ma_resource_manag
result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds); result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
ma_pipeline_notifications_signal_all_notifications(&notifications);
return result; return result;
} }
...@@ -8305,10 +8431,7 @@ static ma_result ma_resource_manager_data_stream_init_internal(ma_resource_manag ...@@ -8305,10 +8431,7 @@ static ma_result ma_resource_manager_data_stream_init_internal(ma_resource_manag
pDataStream->result = MA_BUSY; pDataStream->result = MA_BUSY;
if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL)) { if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL)) {
if (pNotification != NULL) { ma_pipeline_notifications_signal_all_notifications(&notifications);
ma_async_notification_signal(pNotification);
}
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
...@@ -8322,10 +8445,7 @@ static ma_result ma_resource_manager_data_stream_init_internal(ma_resource_manag ...@@ -8322,10 +8445,7 @@ static ma_result ma_resource_manager_data_stream_init_internal(ma_resource_manag
} }
if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {
if (pNotification != NULL) { ma_pipeline_notifications_signal_all_notifications(&notifications);
ma_async_notification_signal(pNotification);
}
return MA_OUT_OF_MEMORY; return MA_OUT_OF_MEMORY;
} }
...@@ -8338,18 +8458,20 @@ static ma_result ma_resource_manager_data_stream_init_internal(ma_resource_manag ...@@ -8338,18 +8458,20 @@ static ma_result ma_resource_manager_data_stream_init_internal(ma_resource_manag
ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification); ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification);
} }
ma_pipeline_notifications_acquire_all_fences(&notifications);
/* We now have everything we need to post the job. This is the last thing we need to do from here. The rest will be done by the job thread. */ /* We now have everything we need to post the job. This is the last thing we need to do from here. The rest will be done by the job thread. */
job = ma_job_init(MA_JOB_LOAD_DATA_STREAM); job = ma_job_init(MA_JOB_LOAD_DATA_STREAM);
job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
job.loadDataStream.pDataStream = pDataStream; job.loadDataStream.pDataStream = pDataStream;
job.loadDataStream.pFilePath = pFilePathCopy; job.loadDataStream.pFilePath = pFilePathCopy;
job.loadDataStream.pFilePathW = pFilePathWCopy; job.loadDataStream.pFilePathW = pFilePathWCopy;
job.loadDataStream.pNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : pNotification; job.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification;
job.loadDataStream.pInitFence = notifications.init.pFence;
result = ma_resource_manager_post_job(pResourceManager, &job); result = ma_resource_manager_post_job(pResourceManager, &job);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
if (pNotification != NULL) { ma_pipeline_notifications_signal_all_notifications(&notifications);
ma_async_notification_signal(pNotification); ma_pipeline_notifications_release_all_fences(&notifications);
}
if (waitBeforeReturning) { if (waitBeforeReturning) {
ma_resource_manager_inline_notification_uninit(&waitNotification); ma_resource_manager_inline_notification_uninit(&waitNotification);
...@@ -8362,25 +8484,26 @@ static ma_result ma_resource_manager_data_stream_init_internal(ma_resource_manag ...@@ -8362,25 +8484,26 @@ static ma_result ma_resource_manager_data_stream_init_internal(ma_resource_manag
/* Wait if needed. */ /* Wait if needed. */
if (waitBeforeReturning) { if (waitBeforeReturning) {
ma_resource_manager_inline_notification_wait(&waitNotification); ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification);
ma_resource_manager_inline_notification_uninit(&waitNotification);
if (pNotification != NULL) { if (notifications.init.pNotification != NULL) {
ma_async_notification_signal(pNotification); ma_async_notification_signal(notifications.init.pNotification);
} }
/* NOTE: Do not release pInitFence here. That will be done by the job. */
} }
return MA_SUCCESS; return MA_SUCCESS;
} }
MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, ma_async_notification* pNotification, ma_resource_manager_data_stream* pDataStream) MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream)
{ {
return ma_resource_manager_data_stream_init_internal(pResourceManager, pFilePath, NULL, flags, pNotification, pDataStream); return ma_resource_manager_data_stream_init_internal(pResourceManager, pFilePath, NULL, flags, pNotifications, pDataStream);
} }
MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, ma_async_notification* pNotification, ma_resource_manager_data_stream* pDataStream) MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream)
{ {
return ma_resource_manager_data_stream_init_internal(pResourceManager, NULL, pFilePath, flags, pNotification, pDataStream); return ma_resource_manager_data_stream_init_internal(pResourceManager, NULL, pFilePath, flags, pNotifications, pDataStream);
} }
MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream) MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream)
...@@ -8403,13 +8526,13 @@ MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data ...@@ -8403,13 +8526,13 @@ MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data
job = ma_job_init(MA_JOB_FREE_DATA_STREAM); job = ma_job_init(MA_JOB_FREE_DATA_STREAM);
job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
job.freeDataStream.pDataStream = pDataStream; job.freeDataStream.pDataStream = pDataStream;
job.freeDataStream.pNotification = &freeEvent; job.freeDataStream.pDoneNotification = &freeEvent;
job.freeDataStream.pDoneFence = NULL;
ma_resource_manager_post_job(pDataStream->pResourceManager, &job); ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
/* We need to wait for the job to finish processing before we return. */ /* We need to wait for the job to finish processing before we return. */
ma_resource_manager_inline_notification_wait(&freeEvent); ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent);
ma_resource_manager_inline_notification_uninit(&freeEvent);
return MA_SUCCESS; return MA_SUCCESS;
} }
...@@ -8901,7 +9024,7 @@ static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pR ...@@ -8901,7 +9024,7 @@ static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pR
return MA_SUCCESS; return MA_SUCCESS;
} }
MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, ma_async_notification* pNotification, ma_resource_manager_data_source* pDataSource) MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource)
{ {
ma_result result; ma_result result;
...@@ -8912,13 +9035,13 @@ MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pReso ...@@ -8912,13 +9035,13 @@ MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pReso
/* The data source itself is just a data stream or a data buffer. */ /* The data source itself is just a data stream or a data buffer. */
if ((flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) { if ((flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
return ma_resource_manager_data_stream_init(pResourceManager, pName, flags, pNotification, &pDataSource->stream); return ma_resource_manager_data_stream_init(pResourceManager, pName, flags, pNotifications, &pDataSource->stream);
} else { } else {
return ma_resource_manager_data_buffer_init(pResourceManager, pName, flags, pNotification, &pDataSource->buffer); return ma_resource_manager_data_buffer_init(pResourceManager, pName, flags, pNotifications, &pDataSource->buffer);
} }
} }
MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, ma_async_notification* pNotification, ma_resource_manager_data_source* pDataSource) MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource)
{ {
ma_result result; ma_result result;
...@@ -8929,9 +9052,9 @@ MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pRe ...@@ -8929,9 +9052,9 @@ MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pRe
/* The data source itself is just a data stream or a data buffer. */ /* The data source itself is just a data stream or a data buffer. */
if ((flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) { if ((flags & MA_DATA_SOURCE_FLAG_STREAM) != 0) {
return ma_resource_manager_data_stream_init_w(pResourceManager, pName, flags, pNotification, &pDataSource->stream); return ma_resource_manager_data_stream_init_w(pResourceManager, pName, flags, pNotifications, &pDataSource->stream);
} else { } else {
return ma_resource_manager_data_buffer_init_w(pResourceManager, pName, flags, pNotification, &pDataSource->buffer); return ma_resource_manager_data_buffer_init_w(pResourceManager, pName, flags, pNotifications, &pDataSource->buffer);
} }
} }
...@@ -9222,10 +9345,11 @@ static ma_result ma_resource_manager_process_job__load_data_buffer_node(ma_resou ...@@ -9222,10 +9345,11 @@ static ma_result ma_resource_manager_process_job__load_data_buffer_node(ma_resou
Note that if an error occurred at an earlier point, this section will have been skipped. Note that if an error occurred at an earlier point, this section will have been skipped.
*/ */
pageDataBufferNodeJob = ma_job_init(MA_JOB_PAGE_DATA_BUFFER_NODE); pageDataBufferNodeJob = ma_job_init(MA_JOB_PAGE_DATA_BUFFER_NODE);
pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pJob->loadDataBufferNode.pDataBufferNode); pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pJob->loadDataBufferNode.pDataBufferNode);
pageDataBufferNodeJob.pageDataBufferNode.pDataBufferNode = pJob->loadDataBufferNode.pDataBufferNode; pageDataBufferNodeJob.pageDataBufferNode.pDataBufferNode = pJob->loadDataBufferNode.pDataBufferNode;
pageDataBufferNodeJob.pageDataBufferNode.pDecoder = pDecoder; pageDataBufferNodeJob.pageDataBufferNode.pDecoder = pDecoder;
pageDataBufferNodeJob.pageDataBufferNode.pCompletedNotification = pJob->loadDataBufferNode.pCompletedNotification; pageDataBufferNodeJob.pageDataBufferNode.pDoneNotification = pJob->loadDataBufferNode.pDoneNotification;
pageDataBufferNodeJob.pageDataBufferNode.pDoneFence = pJob->loadDataBufferNode.pDoneFence;
/* The job has been set up so it can now be posted. */ /* The job has been set up so it can now be posted. */
result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob); result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob);
...@@ -9265,21 +9389,19 @@ done: ...@@ -9265,21 +9389,19 @@ done:
/* At this point initialization is complete and we can signal the notification if any. */ /* At this point initialization is complete and we can signal the notification if any. */
if (pJob->loadDataBufferNode.pInitNotification != NULL) { if (pJob->loadDataBufferNode.pInitNotification != NULL) {
if (result == MA_SUCCESS || result == MA_BUSY) { ma_async_notification_signal(pJob->loadDataBufferNode.pInitNotification);
ma_async_notification_signal(pJob->loadDataBufferNode.pInitNotification); }
} else { if (pJob->loadDataBufferNode.pInitFence != NULL) {
ma_async_notification_signal(pJob->loadDataBufferNode.pInitNotification); ma_fence_release(pJob->loadDataBufferNode.pInitFence);
}
} }
/* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */ /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */
if (pJob->loadDataBufferNode.pCompletedNotification != NULL) { if (result != MA_BUSY) {
if (result != MA_BUSY) { if (pJob->loadDataBufferNode.pDoneNotification != NULL) {
if (result == MA_SUCCESS) { ma_async_notification_signal(pJob->loadDataBufferNode.pDoneNotification);
ma_async_notification_signal(pJob->loadDataBufferNode.pCompletedNotification); }
} else { if (pJob->loadDataBufferNode.pDoneFence != NULL) {
ma_async_notification_signal(pJob->loadDataBufferNode.pCompletedNotification); ma_fence_release(pJob->loadDataBufferNode.pDoneFence);
}
} }
} }
...@@ -9301,8 +9423,12 @@ static ma_result ma_resource_manager_process_job__free_data_buffer_node(ma_resou ...@@ -9301,8 +9423,12 @@ static ma_result ma_resource_manager_process_job__free_data_buffer_node(ma_resou
ma_resource_manager_data_buffer_node_free(pResourceManager, pJob->freeDataBufferNode.pDataBufferNode); ma_resource_manager_data_buffer_node_free(pResourceManager, pJob->freeDataBufferNode.pDataBufferNode);
/* The event needs to be signalled last. */ /* The event needs to be signalled last. */
if (pJob->freeDataBuffer.pNotification != NULL) { if (pJob->freeDataBufferNode.pDoneNotification != NULL) {
ma_async_notification_signal(pJob->freeDataBufferNode.pNotification); ma_async_notification_signal(pJob->freeDataBufferNode.pDoneNotification);
}
if (pJob->freeDataBufferNode.pDoneFence != NULL) {
ma_fence_release(pJob->freeDataBufferNode.pDoneFence);
} }
c89atomic_fetch_add_32(&pJob->freeDataBufferNode.pDataBufferNode->executionPointer, 1); c89atomic_fetch_add_32(&pJob->freeDataBufferNode.pDataBufferNode->executionPointer, 1);
...@@ -9362,11 +9488,13 @@ done: ...@@ -9362,11 +9488,13 @@ done:
c89atomic_compare_and_swap_32(&pJob->pageDataBufferNode.pDataBufferNode->result, MA_BUSY, result); c89atomic_compare_and_swap_32(&pJob->pageDataBufferNode.pDataBufferNode->result, MA_BUSY, result);
/* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */ /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */
if (pJob->pageDataBufferNode.pCompletedNotification != NULL && result != MA_BUSY) { if (result != MA_BUSY) {
if (result == MA_SUCCESS) { if (pJob->pageDataBufferNode.pDoneNotification != NULL) {
ma_async_notification_signal(pJob->pageDataBuffer.pCompletedNotification); ma_async_notification_signal(pJob->pageDataBufferNode.pDoneNotification);
} else { }
ma_async_notification_signal(pJob->pageDataBuffer.pCompletedNotification);
if (pJob->pageDataBufferNode.pDoneFence != NULL) {
ma_fence_release(pJob->pageDataBufferNode.pDoneFence);
} }
} }
...@@ -9404,7 +9532,7 @@ static ma_result ma_resource_manager_process_job__load_data_buffer(ma_resource_m ...@@ -9404,7 +9532,7 @@ static ma_result ma_resource_manager_process_job__load_data_buffer(ma_resource_m
if (pJob->loadDataBuffer.pDataBuffer->isConnectorInitialized == MA_FALSE) { if (pJob->loadDataBuffer.pDataBuffer->isConnectorInitialized == MA_FALSE) {
if (ma_resource_manager_data_buffer_node_get_data_supply_type(pJob->loadDataBuffer.pDataBuffer->pNode) != ma_resource_manager_data_supply_type_unknown) { if (ma_resource_manager_data_buffer_node_get_data_supply_type(pJob->loadDataBuffer.pDataBuffer->pNode) != ma_resource_manager_data_supply_type_unknown) {
/* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */ /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */
result = ma_resource_manager_data_buffer_init_connector(pJob->loadDataBuffer.pDataBuffer, pJob->loadDataBuffer.pInitNotification); result = ma_resource_manager_data_buffer_init_connector(pJob->loadDataBuffer.pDataBuffer, pJob->loadDataBuffer.pInitNotification, pJob->loadDataBuffer.pInitFence);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
/* TODO: Post error here. */ /* TODO: Post error here. */
goto done; goto done;
...@@ -9427,24 +9555,24 @@ done: ...@@ -9427,24 +9555,24 @@ done:
c89atomic_compare_and_swap_32(&pJob->loadDataBuffer.pDataBuffer->result, MA_BUSY, result); c89atomic_compare_and_swap_32(&pJob->loadDataBuffer.pDataBuffer->result, MA_BUSY, result);
/* Only signal the other threads after the result has been set just for cleanliness sake. */ /* Only signal the other threads after the result has been set just for cleanliness sake. */
if (pJob->loadDataBuffer.pCompletedNotification != NULL) { if (pJob->loadDataBuffer.pDoneNotification != NULL) {
if (result == MA_SUCCESS) { ma_async_notification_signal(pJob->loadDataBuffer.pDoneNotification);
ma_async_notification_signal(pJob->loadDataBuffer.pCompletedNotification); }
} else { if (pJob->loadDataBuffer.pDoneFence != NULL) {
ma_async_notification_signal(pJob->loadDataBuffer.pCompletedNotification); ma_fence_release(pJob->loadDataBuffer.pDoneFence);
}
} }
/* /*
If at this point the data buffer has not had it's connector initialized, it means the If at this point the data buffer has not had it's connector initialized, it means the
notification event was never signalled which means we need to signal it here. notification event was never signalled which means we need to signal it here.
*/ */
if (pJob->loadDataBuffer.pDataBuffer->isConnectorInitialized == MA_FALSE) { if (pJob->loadDataBuffer.pDataBuffer->isConnectorInitialized == MA_FALSE && result != MA_SUCCESS) {
if (pJob->loadDataBuffer.pInitNotification != NULL) { if (pJob->loadDataBuffer.pInitNotification != NULL) {
if (result != MA_SUCCESS) { ma_async_notification_signal(pJob->loadDataBuffer.pInitNotification);
ma_async_notification_signal(pJob->loadDataBuffer.pInitNotification);
}
} }
if (pJob->loadDataBuffer.pInitFence != NULL) {
ma_fence_release(pJob->loadDataBuffer.pInitFence);
}
} }
c89atomic_fetch_add_32(&pJob->loadDataBuffer.pDataBuffer->executionPointer, 1); c89atomic_fetch_add_32(&pJob->loadDataBuffer.pDataBuffer->executionPointer, 1);
...@@ -9464,8 +9592,12 @@ static ma_result ma_resource_manager_process_job__free_data_buffer(ma_resource_m ...@@ -9464,8 +9592,12 @@ static ma_result ma_resource_manager_process_job__free_data_buffer(ma_resource_m
ma_resource_manager_data_buffer_uninit_internal(pJob->freeDataBuffer.pDataBuffer); ma_resource_manager_data_buffer_uninit_internal(pJob->freeDataBuffer.pDataBuffer);
/* The event needs to be signalled last. */ /* The event needs to be signalled last. */
if (pJob->freeDataBuffer.pNotification != NULL) { if (pJob->freeDataBuffer.pDoneNotification != NULL) {
ma_async_notification_signal(pJob->freeDataBuffer.pNotification); ma_async_notification_signal(pJob->freeDataBuffer.pDoneNotification);
}
if (pJob->freeDataBuffer.pDoneFence != NULL) {
ma_fence_release(pJob->freeDataBuffer.pDoneFence);
} }
c89atomic_fetch_add_32(&pJob->freeDataBuffer.pDataBuffer->executionPointer, 1); c89atomic_fetch_add_32(&pJob->freeDataBuffer.pDataBuffer->executionPointer, 1);
...@@ -9539,8 +9671,11 @@ done: ...@@ -9539,8 +9671,11 @@ done:
c89atomic_compare_and_swap_32(&pDataStream->result, MA_BUSY, result); c89atomic_compare_and_swap_32(&pDataStream->result, MA_BUSY, result);
/* Only signal the other threads after the result has been set just for cleanliness sake. */ /* Only signal the other threads after the result has been set just for cleanliness sake. */
if (pJob->loadDataStream.pNotification != NULL) { if (pJob->loadDataStream.pInitNotification != NULL) {
ma_async_notification_signal(pJob->loadDataStream.pNotification); ma_async_notification_signal(pJob->loadDataStream.pInitNotification);
}
if (pJob->loadDataStream.pInitFence != NULL) {
ma_fence_release(pJob->loadDataStream.pInitFence);
} }
c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);
...@@ -9576,8 +9711,11 @@ static ma_result ma_resource_manager_process_job__free_data_stream(ma_resource_m ...@@ -9576,8 +9711,11 @@ static ma_result ma_resource_manager_process_job__free_data_stream(ma_resource_m
ma_data_source_uninit(&pDataStream->ds); ma_data_source_uninit(&pDataStream->ds);
/* The event needs to be signalled last. */ /* The event needs to be signalled last. */
if (pJob->freeDataStream.pNotification != NULL) { if (pJob->freeDataStream.pDoneNotification != NULL) {
ma_async_notification_signal(pJob->freeDataStream.pNotification); ma_async_notification_signal(pJob->freeDataStream.pDoneNotification);
}
if (pJob->freeDataStream.pDoneFence != NULL) {
ma_fence_release(pJob->freeDataStream.pDoneFence);
} }
/*c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ /*c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/
...@@ -12320,7 +12458,7 @@ MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePa ...@@ -12320,7 +12458,7 @@ MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePa
soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */ soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */
soundFlags |= MA_SOUND_FLAG_NO_SPATIALIZATION; /* Not currently doing spatialization with inlined sounds, but this might actually change later. For now disable spatialization. Will be removed if we ever add support for spatialization here. */ soundFlags |= MA_SOUND_FLAG_NO_SPATIALIZATION; /* Not currently doing spatialization with inlined sounds, but this might actually change later. For now disable spatialization. Will be removed if we ever add support for spatialization here. */
result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, &pSound->sound); result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound);
if (result == MA_SUCCESS) { if (result == MA_SUCCESS) {
/* Now attach the sound to the graph. */ /* Now attach the sound to the graph. */
result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex); result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex);
...@@ -12452,9 +12590,10 @@ static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, con ...@@ -12452,9 +12590,10 @@ static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, con
#ifndef MA_NO_RESOURCE_MANAGER #ifndef MA_NO_RESOURCE_MANAGER
MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
{ {
ma_result result; ma_result result = MA_SUCCESS;
ma_uint32 flags; ma_uint32 flags;
ma_sound_config config; ma_sound_config config;
ma_pipeline_notifications notifications;
/* /*
The engine requires knowledge of the channel count of the underlying data source before it can The engine requires knowledge of the channel count of the underlying data source before it can
...@@ -12473,50 +12612,63 @@ MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_s ...@@ -12473,50 +12612,63 @@ MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_s
return MA_OUT_OF_MEMORY; return MA_OUT_OF_MEMORY;
} }
if (pConfig->pFilePath != NULL) { notifications = ma_pipeline_notifications_init();
result = ma_resource_manager_data_source_init(pEngine->pResourceManager, pConfig->pFilePath, flags, NULL, pSound->pResourceManagerDataSource); notifications.done.pFence = pConfig->pDoneFence;
} else {
result = ma_resource_manager_data_source_init_w(pEngine->pResourceManager, pConfig->pFilePathW, flags, NULL, pSound->pResourceManagerDataSource); /*
} We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does
not return prematurely before the sound has finished initializing.
*/
if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); }
{
if (pConfig->pFilePath != NULL) {
result = ma_resource_manager_data_source_init(pEngine->pResourceManager, pConfig->pFilePath, flags, &notifications, pSound->pResourceManagerDataSource);
} else {
result = ma_resource_manager_data_source_init_w(pEngine->pResourceManager, pConfig->pFilePathW, flags, &notifications, pSound->pResourceManagerDataSource);
}
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; goto done;
} }
pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */ pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */
/* We need to use a slightly customized version of the config so we'll need to make a copy. */ /* We need to use a slightly customized version of the config so we'll need to make a copy. */
config = *pConfig; config = *pConfig;
config.pFilePath = NULL; config.pFilePath = NULL;
config.pFilePathW = NULL; config.pFilePathW = NULL;
config.pDataSource = pSound->pResourceManagerDataSource; config.pDataSource = pSound->pResourceManagerDataSource;
result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource);
ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks);
MA_ZERO_OBJECT(pSound); MA_ZERO_OBJECT(pSound);
return result; goto done;
}
} }
done:
return MA_SUCCESS; if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); }
return result;
} }
MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound)
{ {
ma_sound_config config = ma_sound_config_init(); ma_sound_config config = ma_sound_config_init();
config.pFilePath = pFilePath; config.pFilePath = pFilePath;
config.flags = flags; config.flags = flags;
config.pInitialAttachment = pGroup; config.pInitialAttachment = pGroup;
config.pDoneFence = pDoneFence;
return ma_sound_init_ex(pEngine, &config, pSound); return ma_sound_init_ex(pEngine, &config, pSound);
} }
MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound)
{ {
ma_sound_config config = ma_sound_config_init(); ma_sound_config config = ma_sound_config_init();
config.pFilePathW = pFilePath; config.pFilePathW = pFilePath;
config.flags = flags; config.flags = flags;
config.pInitialAttachment = pGroup; config.pInitialAttachment = pGroup;
config.pDoneFence = pDoneFence;
return ma_sound_init_ex(pEngine, &config, pSound); return ma_sound_init_ex(pEngine, &config, pSound);
} }
......
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