diff --git a/research/miniaudio_engine.h b/research/miniaudio_engine.h index 288bf647..14f6118f 100644 --- a/research/miniaudio_engine.h +++ b/research/miniaudio_engine.h @@ -7181,348 +7181,6 @@ static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable = ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames }; -#if 0 -static ma_result ma_resource_manager_data_buffer_init_nolock(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) -{ - ma_result result; - ma_data_source_config dataSourceConfig; - ma_resource_manager_data_buffer_node* pInsertPoint; - char* pFilePathCopy = NULL; /* Allocated here, freed in the job thread. */ - wchar_t* pFilePathWCopy = NULL; - ma_bool32 async; - ma_bool32 decode; - - MA_ASSERT(pResourceManager != NULL); - MA_ASSERT(pDataBuffer != NULL); - - MA_ZERO_OBJECT(pDataBuffer); - - 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) { - return result; - } - - pDataBuffer->pResourceManager = pResourceManager; - pDataBuffer->flags = flags; - - - /* The encoding of the data buffer is taken from the flags. */ - decode = (flags & MA_DATA_SOURCE_FLAG_DECODE) != 0; - - /* The data buffer needs to be loaded by the calling thread if we're in synchronous mode. */ - async = (flags & MA_DATA_SOURCE_FLAG_ASYNC) != 0; - - /* - The first thing to do is find the insertion point. If it's already loaded it means we can just increment the reference counter and signal the event. Otherwise we - need to do a full load. - */ - result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint); - if (result == MA_ALREADY_EXISTS) { - /* Fast path. The data buffer already exists. We just need to increment the reference counter and signal the event, if any. */ - pDataBuffer->pNode = pInsertPoint; - - result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBuffer->pNode, NULL); - if (result != MA_SUCCESS) { - return result; /* Should never happen. Failed to increment the reference count. */ - } - - /* - The existing node may be in the middle of loading. We need to wait for the node to finish - loading before going any further or else we won't be able to initialize the connector. The - alternative to this could be to initialize the connector via the job queue when the data - source is being loaded asynchronously. - */ - if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY && ma_resource_manager_is_threading_enabled(pResourceManager) && async) { - /* Loading asynchronously. */ - - /* TODO: This needs to be improved so that when loading asynchronously we post a message to the job queue instead of just waiting. */ - if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_encoded) { - while (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { - ma_yield(); - } - } else { - while (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { - ma_yield(); - } - } - } else { - /* Not loading asychronously. We need to wait for the sound to be fully decoded so we can initialize a connector. */ - while (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { - if (ma_resource_manager_is_threading_enabled(pResourceManager)) { - /* We're not threading, so process the next job if there are any. */ - result = ma_resource_manager_process_next_job(pResourceManager); - if (result == MA_NO_DATA_AVAILABLE || result == MA_JOB_QUIT) { - break; - } - } else { - /* We're threading, so just keep spinning until some other thread finishes decoding of the original sound. */ - ma_yield(); - } - } - } - - result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, NULL); - if (result != MA_SUCCESS) { - ma_resource_manager_data_buffer_node_free(pDataBuffer->pResourceManager, pDataBuffer->pNode); - return result; - } - - if (pNotification != NULL) { - ma_async_notification_signal(pNotification, MA_NOTIFICATION_COMPLETE); - } - } else { - /* - Slow path. The data for this buffer has not yet been initialized. The first thing to do is - allocate the new data buffer node and insert it into the BST. - - Note that there's a possiblity that we're calling this function because we're wanting to - initialize a data buffer from an existing data buffer rather than by a file name. In this - case the file path will be NULL. This means the caller has uninitialized the last reference - to the underlying node. This is an invalid usage - the caller must ensure the existing data - buffer stays valid while cloning. - */ - if (pFilePath == NULL && pFilePathW == NULL) { - return MA_INVALID_OPERATION; - } - - pDataBuffer->pNode = (ma_resource_manager_data_buffer_node*)ma__malloc_from_callbacks(sizeof(*pDataBuffer->pNode), &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_RESOURCE_MANAGER_DATA_BUFFER*/); - if (pDataBuffer->pNode == NULL) { - return MA_OUT_OF_MEMORY; - } - - MA_ZERO_OBJECT(pDataBuffer->pNode); - pDataBuffer->pNode->hashedName32 = hashedName32; - pDataBuffer->pNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */ - pDataBuffer->pNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */ - pDataBuffer->pNode->result = MA_BUSY; /* I think it's good practice to set the status to MA_BUSY by default. */ - - result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBuffer->pNode, pInsertPoint); - if (result != MA_SUCCESS) { - ma__free_from_callbacks(pDataBuffer->pNode, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_RESOURCE_MANAGER_DATA_BUFFER*/); - return result; /* Should never happen. Failed to insert the data buffer into the BST. */ - } - - /* - The new data buffer has been inserted into the BST. We now need to load the data. If we are loading synchronously we need to load - everything from the calling thread because we may be in a situation where there are no job threads running and therefore the data - will never get loaded. If we are loading asynchronously, we can assume at least one job thread exists and we can do everything - from there. - */ - pDataBuffer->pNode->isDataOwnedByResourceManager = MA_TRUE; - pDataBuffer->pNode->result = MA_BUSY; - - if (async) { - /* Asynchronous. Post to the job thread. */ - ma_job job; - ma_bool32 waitInit = MA_FALSE; - ma_resource_manager_inline_notification initNotification; - - /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ - if (pFilePath != NULL) { - pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/); - } else { - pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/); - } - - if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { - if (pNotification != NULL) { - ma_async_notification_signal(pNotification, MA_NOTIFICATION_FAILED); - } - - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBuffer->pNode); - ma__free_from_callbacks(pDataBuffer->pNode, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_RESOURCE_MANAGER_DATA_BUFFER*/); - return MA_OUT_OF_MEMORY; - } - - if ((flags & MA_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - waitInit = MA_TRUE; - ma_resource_manager_inline_notification_init(pResourceManager, &initNotification); - } - - /* We now have everything we need to post the job to the job thread. */ - job = ma_job_init(MA_JOB_LOAD_DATA_BUFFER); - job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBuffer->pNode); - job.loadDataBuffer.pDataBuffer = pDataBuffer; - job.loadDataBuffer.pFilePath = pFilePathCopy; - job.loadDataBuffer.pFilePathW = pFilePathWCopy; - job.loadDataBuffer.decode = decode; - job.loadDataBuffer.pInitNotification = (waitInit == MA_TRUE) ? &initNotification : NULL; - job.loadDataBuffer.pCompletedNotification = pNotification; - result = ma_resource_manager_post_job(pResourceManager, &job); - if (result != MA_SUCCESS) { - /* Failed to post the job to the queue. Probably ran out of space. */ - if (pNotification != NULL) { - ma_async_notification_signal(pNotification, MA_NOTIFICATION_FAILED); - } - - if (waitInit == MA_TRUE) { - ma_resource_manager_inline_notification_uninit(&initNotification); - } - - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBuffer->pNode); - ma__free_from_callbacks(pDataBuffer->pNode, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_RESOURCE_MANAGER_DATA_BUFFER*/); - ma__free_from_callbacks(pFilePathCopy, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/); - ma__free_from_callbacks(pFilePathWCopy, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_TRANSIENT_STRING*/); - return result; - } - - /* If we're waiting for initialization of the connector, do so here before returning. */ - if (waitInit == MA_TRUE) { - ma_resource_manager_inline_notification_wait(&initNotification); - ma_resource_manager_inline_notification_uninit(&initNotification); - } - } else { - /* Synchronous. Do everything here. */ - if (decode == MA_FALSE) { - /* No decoding. Just store the file contents in memory. */ - void* pData; - size_t sizeInBytes; - result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &sizeInBytes, &pResourceManager->config.allocationCallbacks, MA_ALLOCATION_TYPE_ENCODED_BUFFER); - if (result == MA_SUCCESS) { - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBuffer->pNode, ma_resource_manager_data_supply_type_encoded); - pDataBuffer->pNode->data.encoded.pData = pData; - pDataBuffer->pNode->data.encoded.sizeInBytes = sizeInBytes; - } - } else { - /* Decoding. */ - ma_decoder decoder; - - result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, &decoder); - if (result == MA_SUCCESS) { - ma_uint64 totalFrameCount; - ma_uint64 dataSizeInBytes; - void* pData = NULL; - - pDataBuffer->pNode->data.decoded.format = decoder.outputFormat; - pDataBuffer->pNode->data.decoded.channels = decoder.outputChannels; - pDataBuffer->pNode->data.decoded.sampleRate = decoder.outputSampleRate; - - totalFrameCount = ma_decoder_get_length_in_pcm_frames(&decoder); - if (totalFrameCount > 0) { - /* It's a known length. We can use an optimized allocation strategy in this case. */ - dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); - if (dataSizeInBytes <= MA_SIZE_MAX) { - pData = ma__malloc_from_callbacks((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODED_BUFFER*/); - if (pData != NULL) { - totalFrameCount = ma_decoder_read_pcm_frames(&decoder, pData, totalFrameCount); - } else { - result = MA_OUT_OF_MEMORY; - } - } else { - result = MA_TOO_BIG; - } - } else { - /* It's an unknown length. We need to dynamically expand the buffer as we decode. To start with we allocate space for one page. We'll then double it as we need more space. */ - ma_uint64 bytesPerFrame; - ma_uint64 pageSizeInFrames; - ma_uint64 dataSizeInFrames; - - bytesPerFrame = ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); - pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (decoder.outputSampleRate/1000); - dataSizeInFrames = 0; - - /* Keep loading, page-by-page. */ - for (;;) { - ma_uint64 framesRead; - - /* Expand the buffer if need be. */ - if (totalFrameCount + pageSizeInFrames > dataSizeInFrames) { - ma_uint64 oldDataSizeInFrames; - ma_uint64 oldDataSizeInBytes; - ma_uint64 newDataSizeInFrames; - ma_uint64 newDataSizeInBytes; - void* pNewData; - - oldDataSizeInFrames = (dataSizeInFrames); - newDataSizeInFrames = (dataSizeInFrames == 0) ? pageSizeInFrames : dataSizeInFrames * 2; - - oldDataSizeInBytes = bytesPerFrame * oldDataSizeInFrames; - newDataSizeInBytes = bytesPerFrame * newDataSizeInFrames; - - if (newDataSizeInBytes > MA_SIZE_MAX) { - result = MA_TOO_BIG; - break; - } - - pNewData = ma__realloc_from_callbacks(pData, (size_t)newDataSizeInBytes, (size_t)oldDataSizeInBytes, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODED_BUFFER*/); - if (pNewData == NULL) { - ma__free_from_callbacks(pData, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODED_BUFFER*/); - result = MA_OUT_OF_MEMORY; - break; - } - - pData = pNewData; - dataSizeInFrames = newDataSizeInFrames; - } - - framesRead = ma_decoder_read_pcm_frames(&decoder, ma_offset_ptr(pData, bytesPerFrame * totalFrameCount), pageSizeInFrames); - totalFrameCount += framesRead; - - if (framesRead < pageSizeInFrames) { - /* We've reached the end. As we were loading we were doubling the size of the buffer each time we needed more memory. Let's try reducing this by doing a final realloc(). */ - size_t newDataSizeInBytes = (size_t)(totalFrameCount * bytesPerFrame); - size_t oldDataSizeInBytes = (size_t)(dataSizeInFrames * bytesPerFrame); - void* pNewData = ma__realloc_from_callbacks(pData, newDataSizeInBytes, oldDataSizeInBytes, &pResourceManager->config.allocationCallbacks); - if (pNewData != NULL) { - pData = pNewData; - } - - /* We're done, so get out of the loop. */ - break; - } - } - } - - if (result == MA_SUCCESS) { - pDataBuffer->pNode->data.decoded.pData = pData; - pDataBuffer->pNode->data.decoded.totalFrameCount = totalFrameCount; - pDataBuffer->pNode->data.decoded.decodedFrameCount = totalFrameCount; /* We've decoded everything. */ - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBuffer->pNode, ma_resource_manager_data_supply_type_decoded); - } else { - pDataBuffer->pNode->data.decoded.pData = NULL; - pDataBuffer->pNode->data.decoded.totalFrameCount = 0; - pDataBuffer->pNode->data.decoded.decodedFrameCount = 0; - ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBuffer->pNode, ma_resource_manager_data_supply_type_unknown); - } - - ma_decoder_uninit(&decoder); - } - } - - /* When loading synchronously we need to initialize the connector straight away. */ - if (result == MA_SUCCESS) { - result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, NULL); - } - - pDataBuffer->pNode->result = result; - } - - /* If we failed to initialize make sure we fire the event and free memory. */ - if (result != MA_SUCCESS) { - if (pNotification != NULL) { - ma_async_notification_signal(pNotification, MA_NOTIFICATION_COMPLETE); - } - - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBuffer->pNode); - ma__free_from_callbacks(pDataBuffer->pNode, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_RESOURCE_MANAGER_DATA_BUFFER*/); - return result; - } - - /* We'll need to fire the event if we have one in synchronous mode. */ - if (async == MA_FALSE) { - if (pNotification != NULL) { - ma_async_notification_signal(pNotification, MA_NOTIFICATION_COMPLETE); - } - } - } - - return MA_SUCCESS; -} -#endif - 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) { ma_result result;