MHFS-XS
view release on metacpan or search on metacpan
miniaudio/miniaudio.h view on Meta::CPAN
MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_dup...
{
ma_result result;
ma_uint32 sizeInFrames;
sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5);
if (sizeInFrames == 0) {
return MA_INVALID_ARGS;
}
result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb);
if (result != MA_SUCCESS) {
return result;
}
/* Seek forward a bit so we have a bit of a buffer in case of desyncs. */
ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2);
return MA_SUCCESS;
}
MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB)
{
ma_pcm_rb_uninit((ma_pcm_rb*)pRB);
return MA_SUCCESS;
}
/**************************************************************************************************************************************************************
Miscellaneous Helpers
**************************************************************************************************************************************************************/
MA_API const char* ma_result_description(ma_result result)
{
switch (result)
{
case MA_SUCCESS: return "No error";
case MA_ERROR: return "Unknown error";
case MA_INVALID_ARGS: return "Invalid argument";
case MA_INVALID_OPERATION: return "Invalid operation";
case MA_OUT_OF_MEMORY: return "Out of memory";
case MA_OUT_OF_RANGE: return "Out of range";
case MA_ACCESS_DENIED: return "Permission denied";
case MA_DOES_NOT_EXIST: return "Resource does not exist";
case MA_ALREADY_EXISTS: return "Resource already exists";
case MA_TOO_MANY_OPEN_FILES: return "Too many open files";
case MA_INVALID_FILE: return "Invalid file";
case MA_TOO_BIG: return "Too large";
case MA_PATH_TOO_LONG: return "Path too long";
case MA_NAME_TOO_LONG: return "Name too long";
case MA_NOT_DIRECTORY: return "Not a directory";
case MA_IS_DIRECTORY: return "Is a directory";
case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty";
case MA_AT_END: return "At end";
case MA_NO_SPACE: return "No space available";
case MA_BUSY: return "Device or resource busy";
case MA_IO_ERROR: return "Input/output error";
case MA_INTERRUPT: return "Interrupted";
case MA_UNAVAILABLE: return "Resource unavailable";
case MA_ALREADY_IN_USE: return "Resource already in use";
case MA_BAD_ADDRESS: return "Bad address";
case MA_BAD_SEEK: return "Illegal seek";
case MA_BAD_PIPE: return "Broken pipe";
case MA_DEADLOCK: return "Deadlock";
case MA_TOO_MANY_LINKS: return "Too many links";
case MA_NOT_IMPLEMENTED: return "Not implemented";
case MA_NO_MESSAGE: return "No message of desired type";
case MA_BAD_MESSAGE: return "Invalid message";
case MA_NO_DATA_AVAILABLE: return "No data available";
case MA_INVALID_DATA: return "Invalid data";
case MA_TIMEOUT: return "Timeout";
case MA_NO_NETWORK: return "Network unavailable";
case MA_NOT_UNIQUE: return "Not unique";
case MA_NOT_SOCKET: return "Socket operation on non-socket";
case MA_NO_ADDRESS: return "Destination address required";
case MA_BAD_PROTOCOL: return "Protocol wrong type for socket";
case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available";
case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported";
case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported";
case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported";
case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported";
case MA_CONNECTION_RESET: return "Connection reset";
case MA_ALREADY_CONNECTED: return "Already connected";
case MA_NOT_CONNECTED: return "Not connected";
case MA_CONNECTION_REFUSED: return "Connection refused";
case MA_NO_HOST: return "No host";
case MA_IN_PROGRESS: return "Operation in progress";
case MA_CANCELLED: return "Operation cancelled";
case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped";
case MA_FORMAT_NOT_SUPPORTED: return "Format not supported";
case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported";
case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported";
case MA_NO_BACKEND: return "No backend";
case MA_NO_DEVICE: return "No device";
case MA_API_NOT_FOUND: return "API not found";
case MA_INVALID_DEVICE_CONFIG: return "Invalid device config";
case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized";
case MA_DEVICE_NOT_STARTED: return "Device not started";
case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend";
case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device";
case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device";
case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device";
default: return "Unknown error";
}
}
MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
{
if (pAllocationCallbacks != NULL) {
if (pAllocationCallbacks->onMalloc != NULL) {
return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
} else {
miniaudio/miniaudio.h view on Meta::CPAN
flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC;
}
async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0;
/*
Fences need to be acquired before doing anything. These must be aquired and released outside of
the node to ensure there's no holes where ma_fence_wait() could prematurely return before the
data buffer has completed initialization.
When loading asynchronously, the node acquisition routine below will acquire the fences on this
thread and then release them on the async thread when the operation is complete.
These fences are always released at the "done" tag at the end of this function. They'll be
acquired a second if loading asynchronously. This double acquisition system is just done to
simplify code maintanence.
*/
ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications);
{
/* 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, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode);
if (result != MA_SUCCESS) {
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
goto done;
}
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);
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
goto done;
}
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. */
if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) {
/* 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, pConfig, NULL, NULL);
c89atomic_exchange_i32(&pDataBuffer->result, result);
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
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_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
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_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications);
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER);
job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
job.data.resourceManager.loadDataBuffer.pDataBuffer = pDataBuffer;
job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification;
job.data.resourceManager.loadDataBuffer.pDoneNotification = notifications.done.pNotification;
job.data.resourceManager.loadDataBuffer.pInitFence = notifications.init.pFence;
job.data.resourceManager.loadDataBuffer.pDoneFence = notifications.done.pFence;
job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames;
job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames;
job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames;
job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames;
job.data.resourceManager.loadDataBuffer.isLooping = pConfig->isLooping;
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. */
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result));
c89atomic_exchange_i32(&pDataBuffer->result, result);
/* Release the fences after the result has been set on the data buffer. */
ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications);
} else {
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
ma_resource_manager_inline_notification_wait(&initNotification);
if (notifications.init.pNotification != NULL) {
ma_async_notification_signal(notifications.init.pNotification);
}
/* 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_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
ma_resource_manager_inline_notification_uninit(&initNotification);
}
}
if (result != MA_SUCCESS) {
ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
goto done;
}
}
done:
if (result == MA_SUCCESS) {
if (pConfig->initialSeekPointInPCMFrames > 0) {
ma_resource_manager_data_buffer_seek_to_pcm_frame(pDataBuffer, pConfig->initialSeekPointInPCMFrames);
miniaudio/miniaudio.h view on Meta::CPAN
return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
} else {
/*
The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will
be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event
to get processed before returning.
*/
ma_resource_manager_inline_notification notification;
ma_job job;
/*
We need to mark the node as unavailable so we don't try reading from it anymore, but also to
let the loading thread know that it needs to abort it's loading procedure.
*/
c89atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE);
result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, ¬ification);
if (result != MA_SUCCESS) {
return result; /* Failed to create the notification. This should rarely, if ever, happen. */
}
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER);
job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
job.data.resourceManager.freeDataBuffer.pDataBuffer = pDataBuffer;
job.data.resourceManager.freeDataBuffer.pDoneNotification = ¬ification;
job.data.resourceManager.freeDataBuffer.pDoneFence = NULL;
result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job);
if (result != MA_SUCCESS) {
ma_resource_manager_inline_notification_uninit(¬ification);
return result;
}
ma_resource_manager_inline_notification_wait_and_uninit(¬ification);
}
return result;
}
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_result result = MA_SUCCESS;
ma_uint64 framesRead = 0;
ma_bool32 isDecodedBufferBusy = MA_FALSE;
/* Safety. */
if (pFramesRead != NULL) {
*pFramesRead = 0;
}
if (frameCount == 0) {
return MA_INVALID_ARGS;
}
/*
We cannot be using the data buffer after it's been uninitialized. If you trigger this assert it means you're trying to read from the data buffer after
it's been uninitialized or is in the process of uninitializing.
*/
MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
/* If the node is not initialized we need to abort with a busy code. */
if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
return MA_BUSY; /* Still loading. */
}
if (pDataBuffer->seekToCursorOnNextRead) {
pDataBuffer->seekToCursorOnNextRead = MA_FALSE;
result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames);
if (result != MA_SUCCESS) {
return result;
}
}
/*
For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot
exceed this amount. We'll read as much as we can, and then return MA_BUSY.
*/
if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) {
ma_uint64 availableFrames;
isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY);
if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) {
/* Don't try reading more than the available frame count. */
if (frameCount > availableFrames) {
frameCount = availableFrames;
/*
If there's no frames available we want to set the status to MA_AT_END. The logic below
will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this
is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count
is 0 because that'll result in a situation where it's possible MA_AT_END won't get
returned.
*/
if (frameCount == 0) {
result = MA_AT_END;
}
} else {
isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */
}
}
}
/* Don't attempt to read anything if we've got no frames available. */
if (frameCount > 0) {
result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead);
}
/*
If we returned MA_AT_END, but the node is still loading, we don't want to return that code or else the caller will interpret the sound
as at the end and terminate decoding.
*/
if (result == MA_AT_END) {
if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) {
result = MA_BUSY;
}
}
if (isDecodedBufferBusy) {
result = MA_BUSY;
}
if (pFramesRead != NULL) {
*pFramesRead = framesRead;
}
if (result == MA_SUCCESS && framesRead == 0) {
result = MA_AT_END;
}
return result;
}
MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex)
{
ma_result result;
/* We cannot be using the data source after it's been uninitialized. */
MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
/* If we haven't yet got a connector we need to abort. */
if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
pDataBuffer->seekTargetInPCMFrames = frameIndex;
pDataBuffer->seekToCursorOnNextRead = MA_TRUE;
return MA_BUSY; /* Still loading. */
}
result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex);
if (result != MA_SUCCESS) {
return result;
miniaudio/miniaudio.h view on Meta::CPAN
ma_uint64 rangeBeg;
ma_uint64 rangeEnd;
ma_uint64 loopPointBeg;
ma_uint64 loopPointEnd;
ma_data_source_set_looping(&pDataStream->decoder, ma_resource_manager_data_stream_is_looping(pDataStream));
ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd);
ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd);
ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd);
ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd);
}
/* Just read straight from the decoder. It will deal with ranges and looping for us. */
result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage);
if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) {
c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE);
}
c89atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage);
c89atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE);
}
static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream)
{
ma_uint32 iPage;
MA_ASSERT(pDataStream != NULL);
for (iPage = 0; iPage < 2; iPage += 1) {
ma_resource_manager_data_stream_fill_page(pDataStream, iPage);
}
}
static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount)
{
ma_uint64 framesAvailable;
ma_uint64 frameCount = 0;
/* We cannot be using the data source after it's been uninitialized. */
MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
if (pFrameCount != NULL) {
frameCount = *pFrameCount;
*pFrameCount = 0;
}
if (ppFramesOut != NULL) {
*ppFramesOut = NULL;
}
if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) {
return MA_INVALID_ARGS;
}
if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
return MA_INVALID_OPERATION;
}
/* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */
if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {
return MA_BUSY;
}
/* If the page we're on is invalid it means we've caught up to the job thread. */
if (c89atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) {
framesAvailable = 0;
} else {
/*
The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is
that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler.
*/
ma_uint32 currentPageFrameCount = c89atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]);
MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor);
framesAvailable = currentPageFrameCount - pDataStream->relativeCursor;
}
/* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */
if (framesAvailable == 0) {
if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) {
return MA_AT_END;
} else {
return MA_BUSY; /* There are no frames available, but we're not marked as EOF so we might have caught up to the job thread. Need to return MA_BUSY and wait for more data. */
}
}
MA_ASSERT(framesAvailable > 0);
if (frameCount > framesAvailable) {
frameCount = framesAvailable;
}
*ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor);
*pFrameCount = frameCount;
return MA_SUCCESS;
}
static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount)
{
ma_uint32 newRelativeCursor;
ma_uint32 pageSizeInFrames;
ma_job job;
/* We cannot be using the data source after it's been uninitialized. */
MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
if (pDataStream == NULL) {
return MA_INVALID_ARGS;
}
if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
return MA_INVALID_OPERATION;
}
/* The frame count should always fit inside a 32-bit integer. */
if (frameCount > 0xFFFFFFFF) {
return MA_INVALID_ARGS;
}
miniaudio/miniaudio.h view on Meta::CPAN
pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream);
/* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */
ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, c89atomic_load_64(&pDataStream->absoluteCursor) + frameCount);
/* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */
newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount;
/* If the new cursor has flowed over to the next page we need to mark the old one as invalid and post an event for it. */
if (newRelativeCursor >= pageSizeInFrames) {
newRelativeCursor -= pageSizeInFrames;
/* Here is where we post the job start decoding. */
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM);
job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
job.data.resourceManager.pageDataStream.pDataStream = pDataStream;
job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex;
/* The page needs to be marked as invalid so that the public API doesn't try reading from it. */
c89atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE);
/* Before posting the job we need to make sure we set some state. */
pDataStream->relativeCursor = newRelativeCursor;
pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01;
return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
} else {
/* We haven't moved into a new page so we can just move the cursor forward. */
pDataStream->relativeCursor = newRelativeCursor;
return MA_SUCCESS;
}
}
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_result result = MA_SUCCESS;
ma_uint64 totalFramesProcessed;
ma_format format;
ma_uint32 channels;
/* Safety. */
if (pFramesRead != NULL) {
*pFramesRead = 0;
}
if (frameCount == 0) {
return MA_INVALID_ARGS;
}
/* We cannot be using the data source after it's been uninitialized. */
MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
if (pDataStream == NULL) {
return MA_INVALID_ARGS;
}
if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
return MA_INVALID_OPERATION;
}
/* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */
if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {
return MA_BUSY;
}
ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0);
/* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */
totalFramesProcessed = 0;
while (totalFramesProcessed < frameCount) {
void* pMappedFrames;
ma_uint64 mappedFrameCount;
mappedFrameCount = frameCount - totalFramesProcessed;
result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount);
if (result != MA_SUCCESS) {
break;
}
/* Copy the mapped data to the output buffer if we have one. It's allowed for pFramesOut to be NULL in which case a relative forward seek is performed. */
if (pFramesOut != NULL) {
ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels);
}
totalFramesProcessed += mappedFrameCount;
result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount);
if (result != MA_SUCCESS) {
break; /* This is really bad - will only get an error here if we failed to post a job to the queue for loading the next page. */
}
}
if (pFramesRead != NULL) {
*pFramesRead = totalFramesProcessed;
}
if (result == MA_SUCCESS && totalFramesProcessed == 0) {
result = MA_AT_END;
}
return result;
}
MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex)
{
ma_job job;
ma_result streamResult;
streamResult = ma_resource_manager_data_stream_result(pDataStream);
/* We cannot be using the data source after it's been uninitialized. */
MA_ASSERT(streamResult != MA_UNAVAILABLE);
if (pDataStream == NULL) {
return MA_INVALID_ARGS;
}
if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) {
return MA_INVALID_OPERATION;
}
miniaudio/miniaudio.h view on Meta::CPAN
job.data.resourceManager.seekDataStream.pDataStream = pDataStream;
job.data.resourceManager.seekDataStream.frameIndex = frameIndex;
return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
}
MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
{
/* We cannot be using the data source after it's been uninitialized. */
MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
if (pFormat != NULL) {
*pFormat = ma_format_unknown;
}
if (pChannels != NULL) {
*pChannels = 0;
}
if (pSampleRate != NULL) {
*pSampleRate = 0;
}
if (pChannelMap != NULL) {
MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
}
if (pDataStream == NULL) {
return MA_INVALID_ARGS;
}
if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
return MA_INVALID_OPERATION;
}
/*
We're being a little bit naughty here and accessing the internal decoder from the public API. The output data format is constant, and we've defined this function
such that the application is responsible for ensuring it's not called while uninitializing so it should be safe.
*/
return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
}
MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor)
{
ma_result result;
if (pCursor == NULL) {
return MA_INVALID_ARGS;
}
*pCursor = 0;
/* We cannot be using the data source after it's been uninitialized. */
MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
if (pDataStream == NULL) {
return MA_INVALID_ARGS;
}
/*
If the stream is in an erroneous state we need to return an invalid operation. We can allow
this to be called when the data stream is in a busy state because the caller may have asked
for an initial seek position and it's convenient to return that as the cursor position.
*/
result = ma_resource_manager_data_stream_result(pDataStream);
if (result != MA_SUCCESS && result != MA_BUSY) {
return MA_INVALID_OPERATION;
}
*pCursor = c89atomic_load_64(&pDataStream->absoluteCursor);
return MA_SUCCESS;
}
MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength)
{
ma_result streamResult;
if (pLength == NULL) {
return MA_INVALID_ARGS;
}
*pLength = 0;
streamResult = ma_resource_manager_data_stream_result(pDataStream);
/* We cannot be using the data source after it's been uninitialized. */
MA_ASSERT(streamResult != MA_UNAVAILABLE);
if (pDataStream == NULL) {
return MA_INVALID_ARGS;
}
if (streamResult != MA_SUCCESS) {
return streamResult;
}
/*
We most definitely do not want to be calling ma_decoder_get_length_in_pcm_frames() directly. Instead we want to use a cached value that we
calculated when we initialized it on the job thread.
*/
*pLength = pDataStream->totalLengthInPCMFrames;
if (*pLength == 0) {
return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */
}
return MA_SUCCESS;
}
MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream)
{
if (pDataStream == NULL) {
return MA_INVALID_ARGS;
}
return (ma_result)c89atomic_load_i32(&pDataStream->result);
}
MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping)
{
return ma_data_source_set_looping(pDataStream, isLooping);
}
miniaudio/miniaudio.h view on Meta::CPAN
static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob)
{
ma_result result = MA_SUCCESS;
ma_resource_manager* pResourceManager;
ma_resource_manager_data_buffer_node* pDataBufferNode;
MA_ASSERT(pJob != NULL);
pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.loadDataBufferNode.pResourceManager;
MA_ASSERT(pResourceManager != NULL);
pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.loadDataBufferNode.pDataBufferNode;
MA_ASSERT(pDataBufferNode != NULL);
MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */
/* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */
if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) {
return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */
}
/* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */
if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) {
result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */
goto done;
}
/*
We're ready to start loading. Essentially what we're doing here is initializing the data supply
of the node. Once this is complete, data buffers can have their connectors initialized which
will allow then to have audio data read from them.
Note that when the data supply type has been moved away from "unknown", that is when other threads
will determine that the node is available for data delivery and the data buffer connectors can be
initialized. Therefore, it's important that it is set after the data supply has been initialized.
*/
if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) {
/*
Decoding. This is the complex case because we're not going to be doing the entire decoding
process here. Instead it's going to be split of multiple jobs and loaded in pages. The
reason for this is to evenly distribute decoding time across multiple sounds, rather than
having one huge sound hog all the available processing resources.
The first thing we do is initialize a decoder. This is allocated on the heap and is passed
around to the paging jobs. When the last paging job has completed it's processing, it'll
free the decoder for us.
This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job
which is where the actual decoding work will be done. However, once this job is complete,
the node will be in a state where data buffer connectors can be initialized.
*/
ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */
ma_job pageDataBufferNodeJob;
/* Allocate the decoder by initializing a decoded data supply. */
result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, pJob->data.resourceManager...
/*
Don't ever propagate an MA_BUSY result code or else the resource manager will think the
node is just busy decoding rather than in an error state. This should never happen, but
including this logic for safety just in case.
*/
if (result == MA_BUSY) {
result = MA_ERROR;
}
if (result != MA_SUCCESS) {
if (pJob->data.resourceManager.loadDataBufferNode.pFilePath != NULL) {
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%s\". %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePath, ma_result_description(result));
} else {
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%ls\", %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePathW, ma_result_description(result));
#endif
}
goto done;
}
/*
At this point the node's data supply is initialized and other threads can start initializing
their data buffer connectors. However, no data will actually be available until we start to
actually decode it. To do this, we need to post a paging job which is where the decoding
work is done.
Note that if an error occurred at an earlier point, this section will have been skipped.
*/
pageDataBufferNodeJob = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE);
pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager;
pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode;
pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder;
pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneNotification = pJob->data.resourceManager.loadDataBufferNode.pDoneNotification;
pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneFence = pJob->data.resourceManager.loadDataBufferNode.pDoneFence;
/* The job has been set up so it can now be posted. */
result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob);
/*
When we get here, we want to make sure the result code is set to MA_BUSY. The reason for
this is that the result will be copied over to the node's internal result variable. In
this case, since the decoding is still in-progress, we need to make sure the result code
is set to MA_BUSY.
*/
if (result != MA_SUCCESS) {
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE job. %s\n", ma_result_description(result));
ma_decoder_uninit(pDecoder);
ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
} else {
result = MA_BUSY;
}
} else {
/* No decoding. This is the simple case. We need only read the file content into memory and we're done. */
result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW);
}
done:
/* File paths are no longer needed. */
ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePath, &pResourceManager->config.allocationCallbacks);
ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks);
miniaudio/miniaudio.h view on Meta::CPAN
pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode;
MA_ASSERT(pDataBufferNode != NULL);
if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) {
return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
}
ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
/* The event needs to be signalled last. */
if (pJob->data.resourceManager.freeDataBufferNode.pDoneNotification != NULL) {
ma_async_notification_signal(pJob->data.resourceManager.freeDataBufferNode.pDoneNotification);
}
if (pJob->data.resourceManager.freeDataBufferNode.pDoneFence != NULL) {
ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence);
}
c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
return MA_SUCCESS;
}
static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob)
{
ma_result result = MA_SUCCESS;
ma_resource_manager* pResourceManager;
ma_resource_manager_data_buffer_node* pDataBufferNode;
MA_ASSERT(pJob != NULL);
pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.pageDataBufferNode.pResourceManager;
MA_ASSERT(pResourceManager != NULL);
pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode;
MA_ASSERT(pDataBufferNode != NULL);
if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) {
return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
}
/* Don't do any more decoding if the data buffer has started the uninitialization process. */
result = ma_resource_manager_data_buffer_node_result(pDataBufferNode);
if (result != MA_BUSY) {
goto done;
}
/* We're ready to decode the next page. */
result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder);
/*
If we have a success code by this point, we want to post another job. We're going to set the
result back to MA_BUSY to make it clear that there's still more to load.
*/
if (result == MA_SUCCESS) {
ma_job newJob;
newJob = *pJob; /* Everything is the same as the input job, except the execution order. */
newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); /* We need a fresh execution order. */
result = ma_resource_manager_post_job(pResourceManager, &newJob);
/* Since the sound isn't yet fully decoded we want the status to be set to busy. */
if (result == MA_SUCCESS) {
result = MA_BUSY;
}
}
done:
/* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */
if (result != MA_BUSY) {
ma_decoder_uninit((ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder);
ma_free(pJob->data.resourceManager.pageDataBufferNode.pDecoder, &pResourceManager->config.allocationCallbacks);
}
/* If we reached the end we need to treat it as successful. */
if (result == MA_AT_END) {
result = MA_SUCCESS;
}
/* Make sure we set the result of node in case some error occurred. */
c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result);
/* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */
if (result != MA_BUSY) {
if (pJob->data.resourceManager.pageDataBufferNode.pDoneNotification != NULL) {
ma_async_notification_signal(pJob->data.resourceManager.pageDataBufferNode.pDoneNotification);
}
if (pJob->data.resourceManager.pageDataBufferNode.pDoneFence != NULL) {
ma_fence_release(pJob->data.resourceManager.pageDataBufferNode.pDoneFence);
}
}
c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
return result;
}
static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob)
{
ma_result result = MA_SUCCESS;
ma_resource_manager* pResourceManager;
ma_resource_manager_data_buffer* pDataBuffer;
ma_resource_manager_data_supply_type dataSupplyType = ma_resource_manager_data_supply_type_unknown;
ma_bool32 isConnectorInitialized = MA_FALSE;
/*
All we're doing here is checking if the node has finished loading. If not, we just re-post the job
and keep waiting. Otherwise we increment the execution counter and set the buffer's result code.
*/
MA_ASSERT(pJob != NULL);
pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.loadDataBuffer.pDataBuffer;
MA_ASSERT(pDataBuffer != NULL);
pResourceManager = pDataBuffer->pResourceManager;
if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) {
return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */
}
/*
First thing we need to do is check whether or not the data buffer is getting deleted. If so we
just abort, but making sure we increment the execution pointer.
*/
result = ma_resource_manager_data_buffer_result(pDataBuffer);
if (result != MA_BUSY) {
goto done; /* <-- This will ensure the exucution pointer is incremented. */
} else {
result = MA_SUCCESS; /* <-- Make sure this is reset. */
}
/* Try initializing the connector if we haven't already. */
isConnectorInitialized = pDataBuffer->isConnectorInitialized;
if (isConnectorInitialized == MA_FALSE) {
dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode);
if (dataSupplyType != 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. */
ma_resource_manager_data_source_config dataSourceConfig; /* For setting initial looping state and range. */
dataSourceConfig = ma_resource_manager_data_source_config_init();
dataSourceConfig.rangeBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames;
dataSourceConfig.rangeEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames;
dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames;
dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames;
dataSourceConfig.isLooping = pJob->data.resourceManager.loadDataBuffer.isLooping;
result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence);
if (result != MA_SUCCESS) {
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to initialize connector for data buffer. %s.\n", ma_result_description(result));
goto done;
}
} else {
/* Don't have a known data supply type. Most likely the data buffer node is still loading, but it could be that an error occurred. */
}
} else {
/* The connector is already initialized. Nothing to do here. */
}
/*
If the data node is still loading, we need to repost the job and *not* increment the execution
pointer (i.e. we need to not fall through to the "done" label).
There is a hole between here and the where the data connector is initialized where the data
buffer node may have finished initializing. We need to check for this by checking the result of
the data buffer node and whether or not we had an unknown data supply type at the time of
trying to initialize the data connector.
*/
result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode);
if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) {
return ma_resource_manager_post_job(pResourceManager, pJob);
}
done:
/* Only move away from a busy code so that we don't trash any existing error codes. */
c89atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result);
/* Only signal the other threads after the result has been set just for cleanliness sake. */
if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) {
ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pDoneNotification);
}
if (pJob->data.resourceManager.loadDataBuffer.pDoneFence != NULL) {
ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pDoneFence);
}
/*
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.
*/
if (pDataBuffer->isConnectorInitialized == MA_FALSE && result != MA_SUCCESS) {
if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) {
ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification);
}
if (pJob->data.resourceManager.loadDataBuffer.pInitFence != NULL) {
ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pInitFence);
}
}
c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1);
return result;
}
static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob)
{
ma_resource_manager* pResourceManager;
ma_resource_manager_data_buffer* pDataBuffer;
MA_ASSERT(pJob != NULL);
pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.freeDataBuffer.pDataBuffer;
MA_ASSERT(pDataBuffer != NULL);
pResourceManager = pDataBuffer->pResourceManager;
if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) {
return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
}
ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
/* The event needs to be signalled last. */
if (pJob->data.resourceManager.freeDataBuffer.pDoneNotification != NULL) {
ma_async_notification_signal(pJob->data.resourceManager.freeDataBuffer.pDoneNotification);
}
if (pJob->data.resourceManager.freeDataBuffer.pDoneFence != NULL) {
ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence);
}
c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1);
return MA_SUCCESS;
}
static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob)
{
( run in 1.008 second using v1.01-cache-2.11-cpan-364913b4093 )