mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-24 09:14:04 +02:00
Improve support for asynchronous decoding of sounds of unknown length.
This commit is contained in:
+538
-66
@@ -48,6 +48,69 @@ useful to be told exactly what it being allocated so you can optimize your alloc
|
|||||||
#define MA_ALLOCATION_TYPE_RESOURCE_MANAGER_DATA_STREAM 0x00000012 /* A ma_resource_manager_data_stream object. */
|
#define MA_ALLOCATION_TYPE_RESOURCE_MANAGER_DATA_STREAM 0x00000012 /* A ma_resource_manager_data_stream object. */
|
||||||
#define MA_ALLOCATION_TYPE_RESOURCE_MANAGER_DATA_SOURCE 0x00000013 /* A ma_resource_manager_data_source object. */
|
#define MA_ALLOCATION_TYPE_RESOURCE_MANAGER_DATA_SOURCE 0x00000013 /* A ma_resource_manager_data_source object. */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Paged Audio Buffer
|
||||||
|
==================
|
||||||
|
A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It
|
||||||
|
can be used for cases where audio data is streamed in asynchronously while allowing data to be read
|
||||||
|
at the same time.
|
||||||
|
|
||||||
|
This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across
|
||||||
|
simultaneously across different threads, however only one thread at a time can append, and only one
|
||||||
|
thread at a time can read and seek.
|
||||||
|
*/
|
||||||
|
typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page;
|
||||||
|
struct ma_paged_audio_buffer_page
|
||||||
|
{
|
||||||
|
MA_ATOMIC ma_paged_audio_buffer_page* pNext;
|
||||||
|
ma_uint32 sizeInFrames;
|
||||||
|
ma_uint8 pAudioData[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
ma_format format;
|
||||||
|
ma_uint32 channels;
|
||||||
|
ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */
|
||||||
|
MA_ATOMIC ma_paged_audio_buffer_page* pTail; /* Never null. Initially set to &head. */
|
||||||
|
} ma_paged_audio_buffer_data;
|
||||||
|
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData);
|
||||||
|
MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks);
|
||||||
|
MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData);
|
||||||
|
MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData);
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength);
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage);
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage);
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
ma_paged_audio_buffer_data* pData; /* Must not be null. */
|
||||||
|
} ma_paged_audio_buffer_config;
|
||||||
|
|
||||||
|
MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
ma_data_source_base ds;
|
||||||
|
ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */
|
||||||
|
ma_paged_audio_buffer_page* pCurrent;
|
||||||
|
ma_uint64 relativeCursor; /* Relative to the current page. */
|
||||||
|
ma_uint64 absoluteCursor;
|
||||||
|
} ma_paged_audio_buffer;
|
||||||
|
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer);
|
||||||
|
MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer, const ma_allocation_callbacks* pAllocationCallbacks);
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex);
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor);
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Resource Management
|
Resource Management
|
||||||
===================
|
===================
|
||||||
@@ -1021,7 +1084,8 @@ typedef enum
|
|||||||
{
|
{
|
||||||
ma_resource_manager_data_buffer_connector_unknown,
|
ma_resource_manager_data_buffer_connector_unknown,
|
||||||
ma_resource_manager_data_buffer_connector_decoder, /* ma_decoder */
|
ma_resource_manager_data_buffer_connector_decoder, /* ma_decoder */
|
||||||
ma_resource_manager_data_buffer_connector_buffer /* ma_audio_buffer */
|
ma_resource_manager_data_buffer_connector_buffer, /* ma_audio_buffer */
|
||||||
|
ma_resource_manager_data_buffer_connector_paged_buffer /* ma_paged_audio_buffer */
|
||||||
} ma_resource_manager_data_buffer_connector;
|
} ma_resource_manager_data_buffer_connector;
|
||||||
|
|
||||||
|
|
||||||
@@ -1167,7 +1231,6 @@ typedef struct
|
|||||||
void* pData;
|
void* pData;
|
||||||
size_t dataSizeInBytes;
|
size_t dataSizeInBytes;
|
||||||
ma_uint64 decodedFrameCount;
|
ma_uint64 decodedFrameCount;
|
||||||
ma_bool32 isUnknownLength; /* When set to true does not update the running frame count of the data buffer nor the data pointer until the last page has been decoded. */
|
|
||||||
} pageDataBuffer;
|
} pageDataBuffer;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
@@ -1241,9 +1304,18 @@ MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob); /* Retur
|
|||||||
#define MA_RESOURCE_MANAGER_FLAG_NO_THREADING 0x00000002
|
#define MA_RESOURCE_MANAGER_FLAG_NO_THREADING 0x00000002
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
ma_decoded_data_supplier_unknown,
|
||||||
|
ma_decoded_data_supplier_buffer,
|
||||||
|
ma_decoded_data_supplier_paged
|
||||||
|
} ma_decoded_data_supplier;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
const void* pData;
|
ma_decoded_data_supplier supplier;
|
||||||
|
const void* pData; /* Only used if `supplier` is ma_decoded_data_supplier_buffer. */
|
||||||
|
ma_paged_audio_buffer_data pagedData; /* Only used if `supplier` is ma_decoded_data_supplier_paged. */
|
||||||
ma_uint64 frameCount; /* The total number of PCM frames making up the decoded data. */
|
ma_uint64 frameCount; /* The total number of PCM frames making up the decoded data. */
|
||||||
ma_uint64 decodedFrameCount; /* For async decoding. Keeps track of how many frames are *currently* decoded. */
|
ma_uint64 decodedFrameCount; /* For async decoding. Keeps track of how many frames are *currently* decoded. */
|
||||||
ma_format format;
|
ma_format format;
|
||||||
@@ -1296,6 +1368,7 @@ struct ma_resource_manager_data_buffer
|
|||||||
{
|
{
|
||||||
ma_decoder decoder;
|
ma_decoder decoder;
|
||||||
ma_audio_buffer buffer;
|
ma_audio_buffer buffer;
|
||||||
|
ma_paged_audio_buffer pagedBuffer;
|
||||||
} connector;
|
} connector;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1926,6 +1999,380 @@ MA_API ma_result ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGr
|
|||||||
|
|
||||||
#if defined(MA_IMPLEMENTATION) || defined(MINIAUDIO_IMPLEMENTATION)
|
#if defined(MA_IMPLEMENTATION) || defined(MINIAUDIO_IMPLEMENTATION)
|
||||||
|
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData)
|
||||||
|
{
|
||||||
|
if (pData == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_ZERO_OBJECT(pData);
|
||||||
|
|
||||||
|
pData->format = format;
|
||||||
|
pData->channels = channels;
|
||||||
|
pData->pTail = &pData->head;
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks)
|
||||||
|
{
|
||||||
|
ma_paged_audio_buffer_page* pPage;
|
||||||
|
|
||||||
|
if (pData == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* All pages need to be freed. */
|
||||||
|
pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext);
|
||||||
|
while (pPage != NULL) {
|
||||||
|
ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext);
|
||||||
|
|
||||||
|
ma_free(pPage, pAllocationCallbacks);
|
||||||
|
pPage = pNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData)
|
||||||
|
{
|
||||||
|
if (pData == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pData->head;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData)
|
||||||
|
{
|
||||||
|
if (pData == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pData->pTail;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength)
|
||||||
|
{
|
||||||
|
ma_paged_audio_buffer_page* pPage;
|
||||||
|
|
||||||
|
if (pLength == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pLength = 0;
|
||||||
|
|
||||||
|
if (pData == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the length from the linked list. */
|
||||||
|
for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) {
|
||||||
|
*pLength += pPage->sizeInFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage)
|
||||||
|
{
|
||||||
|
ma_paged_audio_buffer_page* pPage;
|
||||||
|
|
||||||
|
if (ppPage == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ppPage = NULL;
|
||||||
|
|
||||||
|
if (pData == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
pPage = (ma_paged_audio_buffer_page*)ma_malloc(sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels)), pAllocationCallbacks);
|
||||||
|
if (pPage == NULL) {
|
||||||
|
return MA_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
pPage->pNext = NULL;
|
||||||
|
pPage->sizeInFrames = pageSizeInFrames;
|
||||||
|
|
||||||
|
if (pInitialData != NULL) {
|
||||||
|
ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
*ppPage = pPage;
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage)
|
||||||
|
{
|
||||||
|
if (pData == NULL || pPage == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function assumes the page has been filled with audio data by this point. As soon as we append, the page will be available for reading. */
|
||||||
|
|
||||||
|
/* First thing to do is update the tail. */
|
||||||
|
for (;;) {
|
||||||
|
ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->pTail);
|
||||||
|
ma_paged_audio_buffer_page* pNewTail = pPage;
|
||||||
|
|
||||||
|
if (c89atomic_compare_exchange_weak_ptr((void**)&pData->pTail, (void**)&pOldTail, pNewTail)) {
|
||||||
|
/* Here is where we append the page to the list. After this, the page is attached to the list and ready to be read from. */
|
||||||
|
c89atomic_exchange_ptr(&pOldTail->pNext, pPage);
|
||||||
|
break; /* Done. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks)
|
||||||
|
{
|
||||||
|
ma_result result;
|
||||||
|
ma_paged_audio_buffer_page* pPage;
|
||||||
|
|
||||||
|
result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ma_paged_audio_buffer_data_append_page(pData, pPage); /* <-- Should never fail. */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData)
|
||||||
|
{
|
||||||
|
ma_paged_audio_buffer_config config;
|
||||||
|
|
||||||
|
MA_ZERO_OBJECT(&config);
|
||||||
|
config.pData = pData;
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
||||||
|
{
|
||||||
|
return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
|
||||||
|
{
|
||||||
|
return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_result ma_paged_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
|
||||||
|
{
|
||||||
|
ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource;
|
||||||
|
|
||||||
|
*pFormat = pPagedAudioBuffer->pData->format;
|
||||||
|
*pChannels = pPagedAudioBuffer->pData->channels;
|
||||||
|
*pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
|
||||||
|
{
|
||||||
|
return ma_paged_audio_buffer_get_cursor_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
|
||||||
|
{
|
||||||
|
return ma_paged_audio_buffer_get_length_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable =
|
||||||
|
{
|
||||||
|
ma_paged_audio_buffer__data_source_on_read,
|
||||||
|
ma_paged_audio_buffer__data_source_on_seek,
|
||||||
|
NULL, /* onMap */
|
||||||
|
NULL, /* onUnmap */
|
||||||
|
ma_paged_audio_buffer__data_source_on_get_data_format,
|
||||||
|
ma_paged_audio_buffer__data_source_on_get_cursor,
|
||||||
|
ma_paged_audio_buffer__data_source_on_get_length
|
||||||
|
};
|
||||||
|
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer)
|
||||||
|
{
|
||||||
|
ma_result result;
|
||||||
|
ma_data_source_config dataSourceConfig;
|
||||||
|
|
||||||
|
if (pPagedAudioBuffer == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_ZERO_OBJECT(pPagedAudioBuffer);
|
||||||
|
|
||||||
|
/* A config is required for the format and channel count. */
|
||||||
|
if (pConfig == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pConfig->pData == NULL) {
|
||||||
|
return MA_INVALID_ARGS; /* No underlying data specified. */
|
||||||
|
}
|
||||||
|
|
||||||
|
dataSourceConfig = ma_data_source_config_init();
|
||||||
|
dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable;
|
||||||
|
|
||||||
|
result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pPagedAudioBuffer->pData = pConfig->pData;
|
||||||
|
pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData);
|
||||||
|
pPagedAudioBuffer->relativeCursor = 0;
|
||||||
|
pPagedAudioBuffer->absoluteCursor = 0;
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer, const ma_allocation_callbacks* pAllocationCallbacks)
|
||||||
|
{
|
||||||
|
ma_paged_audio_buffer_page* pPage;
|
||||||
|
|
||||||
|
if (pPagedAudioBuffer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* All pages need to be freed. */
|
||||||
|
pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext);
|
||||||
|
while (pPage != NULL) {
|
||||||
|
ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext);
|
||||||
|
|
||||||
|
ma_free(pPage, pAllocationCallbacks);
|
||||||
|
pPage = pNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
||||||
|
{
|
||||||
|
ma_result result = MA_SUCCESS;
|
||||||
|
ma_uint64 totalFramesRead = 0;
|
||||||
|
ma_format format;
|
||||||
|
ma_uint32 channels;
|
||||||
|
|
||||||
|
if (pPagedAudioBuffer == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
format = pPagedAudioBuffer->pData->format;
|
||||||
|
channels = pPagedAudioBuffer->pData->channels;
|
||||||
|
|
||||||
|
while (totalFramesRead < frameCount) {
|
||||||
|
/* Read from the current page. The buffer should never be in a state where this is NULL. */
|
||||||
|
ma_uint64 framesRemainingInCurrentPage;
|
||||||
|
ma_uint64 framesRemainingToRead = frameCount - totalFramesRead;
|
||||||
|
ma_uint64 framesToReadThisIteration;
|
||||||
|
|
||||||
|
MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL);
|
||||||
|
|
||||||
|
framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor;
|
||||||
|
|
||||||
|
framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead);
|
||||||
|
ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), ma_offset_pcm_frames_ptr(pPagedAudioBuffer->pCurrent->pAudioData, pPagedAudioBuffer->relativeCursor, format, channels), framesToReadThisIteration, format, channels);
|
||||||
|
totalFramesRead += framesToReadThisIteration;
|
||||||
|
|
||||||
|
pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration;
|
||||||
|
pPagedAudioBuffer->relativeCursor += framesToReadThisIteration;
|
||||||
|
|
||||||
|
/* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */
|
||||||
|
MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames);
|
||||||
|
|
||||||
|
if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) {
|
||||||
|
/* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */
|
||||||
|
ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext);
|
||||||
|
if (pNext == NULL) {
|
||||||
|
result = MA_AT_END;
|
||||||
|
break; /* We've reached the end. */
|
||||||
|
} else {
|
||||||
|
pPagedAudioBuffer->pCurrent = pNext;
|
||||||
|
pPagedAudioBuffer->relativeCursor = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pFramesRead != NULL) {
|
||||||
|
*pFramesRead = totalFramesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex)
|
||||||
|
{
|
||||||
|
if (pPagedAudioBuffer == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frameIndex == pPagedAudioBuffer->absoluteCursor) {
|
||||||
|
return MA_SUCCESS; /* Nothing to do. */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frameIndex < pPagedAudioBuffer->absoluteCursor) {
|
||||||
|
/* Moving backwards. Need to move the cursor back to the start, and then move forward. */
|
||||||
|
pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData);
|
||||||
|
pPagedAudioBuffer->absoluteCursor = 0;
|
||||||
|
pPagedAudioBuffer->relativeCursor = 0;
|
||||||
|
|
||||||
|
/* Fall through to the forward seeking section below. */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frameIndex > pPagedAudioBuffer->absoluteCursor) {
|
||||||
|
/* Moving forward. */
|
||||||
|
ma_paged_audio_buffer_page* pPage;
|
||||||
|
ma_uint64 runningCursor = 0;
|
||||||
|
|
||||||
|
for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) {
|
||||||
|
ma_uint64 pageRangeBeg = runningCursor;
|
||||||
|
ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames;
|
||||||
|
|
||||||
|
if (frameIndex >= pageRangeBeg) {
|
||||||
|
if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)c89atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */
|
||||||
|
/* We found the page. */
|
||||||
|
pPagedAudioBuffer->pCurrent = pPage;
|
||||||
|
pPagedAudioBuffer->absoluteCursor = frameIndex;
|
||||||
|
pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg;
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runningCursor = pageRangeEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Getting here means we tried seeking too far forward. Don't change any state. */
|
||||||
|
return MA_BAD_SEEK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor)
|
||||||
|
{
|
||||||
|
if (pCursor == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pCursor = 0; /* Safety. */
|
||||||
|
|
||||||
|
if (pPagedAudioBuffer == NULL) {
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pCursor = pPagedAudioBuffer->absoluteCursor;
|
||||||
|
|
||||||
|
return MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength)
|
||||||
|
{
|
||||||
|
return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MA_API size_t ma_get_accumulation_bytes_per_sample(ma_format format)
|
MA_API size_t ma_get_accumulation_bytes_per_sample(ma_format format)
|
||||||
{
|
{
|
||||||
size_t bytesPerSample[ma_format_count] = {
|
size_t bytesPerSample[ma_format_count] = {
|
||||||
@@ -6072,7 +6519,14 @@ static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_mana
|
|||||||
an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead.
|
an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead.
|
||||||
*/
|
*/
|
||||||
if (pDataBuffer->pNode->data.type == ma_resource_manager_data_buffer_encoding_decoded) {
|
if (pDataBuffer->pNode->data.type == ma_resource_manager_data_buffer_encoding_decoded) {
|
||||||
|
/* If the source is of an unknown length we need to use a paged buffer. Otherwise we'll use a regular buffer which is a bit more efficient. */
|
||||||
|
if (pDataBuffer->pNode->data.decoded.supplier == ma_decoded_data_supplier_buffer) {
|
||||||
pDataBuffer->connectorType = ma_resource_manager_data_buffer_connector_buffer;
|
pDataBuffer->connectorType = ma_resource_manager_data_buffer_connector_buffer;
|
||||||
|
} else if (pDataBuffer->pNode->data.decoded.supplier == ma_decoded_data_supplier_paged) {
|
||||||
|
pDataBuffer->connectorType = ma_resource_manager_data_buffer_connector_paged_buffer;
|
||||||
|
} else {
|
||||||
|
return MA_INVALID_ARGS; /* Unknown decoded data backend. */
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
pDataBuffer->connectorType = ma_resource_manager_data_buffer_connector_decoder;
|
pDataBuffer->connectorType = ma_resource_manager_data_buffer_connector_decoder;
|
||||||
}
|
}
|
||||||
@@ -6083,6 +6537,12 @@ static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_mana
|
|||||||
result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer);
|
result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer);
|
||||||
|
|
||||||
pDataBuffer->lengthInPCMFrames = pDataBuffer->connector.buffer.ref.sizeInFrames;
|
pDataBuffer->lengthInPCMFrames = pDataBuffer->connector.buffer.ref.sizeInFrames;
|
||||||
|
} else if (pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_paged_buffer) {
|
||||||
|
ma_paged_audio_buffer_config config;
|
||||||
|
config = ma_paged_audio_buffer_config_init(&pDataBuffer->pNode->data.decoded.pagedData);
|
||||||
|
result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer);
|
||||||
|
|
||||||
|
pDataBuffer->lengthInPCMFrames = 0; /* Length is unknown so far. */
|
||||||
} else {
|
} else {
|
||||||
ma_decoder_config configOut;
|
ma_decoder_config configOut;
|
||||||
configOut = ma_decoder_config_init(pDataBuffer->pResourceManager->config.decodedFormat, pDataBuffer->pResourceManager->config.decodedChannels, pDataBuffer->pResourceManager->config.decodedSampleRate);
|
configOut = ma_decoder_config_init(pDataBuffer->pResourceManager->config.decodedFormat, pDataBuffer->pResourceManager->config.decodedChannels, pDataBuffer->pResourceManager->config.decodedSampleRate);
|
||||||
@@ -6115,7 +6575,7 @@ static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_mana
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Initialization of the connector is when we can fire the MA_NOTIFICATION_COMPLETE notification. This will give the application access to
|
Initialization of the connector is when we can fire the init notification. This will give the application access to
|
||||||
the format/channels/rate of the data source.
|
the format/channels/rate of the data source.
|
||||||
*/
|
*/
|
||||||
if (result == MA_SUCCESS) {
|
if (result == MA_SUCCESS) {
|
||||||
@@ -6135,6 +6595,8 @@ static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_ma
|
|||||||
|
|
||||||
if (pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_decoder) {
|
if (pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_decoder) {
|
||||||
ma_decoder_uninit(&pDataBuffer->connector.decoder);
|
ma_decoder_uninit(&pDataBuffer->connector.decoder);
|
||||||
|
} else if (pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_paged_buffer) {
|
||||||
|
ma_paged_audio_buffer_uninit(&pDataBuffer->connector.pagedBuffer, &pResourceManager->config.allocationCallbacks);
|
||||||
} else {
|
} else {
|
||||||
ma_audio_buffer_uninit(&pDataBuffer->connector.buffer);
|
ma_audio_buffer_uninit(&pDataBuffer->connector.buffer);
|
||||||
}
|
}
|
||||||
@@ -6175,6 +6637,8 @@ static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource
|
|||||||
{
|
{
|
||||||
if (pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_buffer) {
|
if (pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_buffer) {
|
||||||
return &pDataBuffer->connector.buffer;
|
return &pDataBuffer->connector.buffer;
|
||||||
|
} else if (pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_paged_buffer) {
|
||||||
|
return &pDataBuffer->connector.pagedBuffer;
|
||||||
} else {
|
} else {
|
||||||
return &pDataBuffer->connector.decoder;
|
return &pDataBuffer->connector.decoder;
|
||||||
}
|
}
|
||||||
@@ -6507,10 +6971,12 @@ static ma_result ma_resource_manager_data_buffer_init_nolock(ma_resource_manager
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result == MA_SUCCESS) {
|
if (result == MA_SUCCESS) {
|
||||||
|
pDataBuffer->pNode->data.decoded.supplier = ma_decoded_data_supplier_buffer;
|
||||||
pDataBuffer->pNode->data.decoded.pData = pData;
|
pDataBuffer->pNode->data.decoded.pData = pData;
|
||||||
pDataBuffer->pNode->data.decoded.frameCount = totalFrameCount;
|
pDataBuffer->pNode->data.decoded.frameCount = totalFrameCount;
|
||||||
pDataBuffer->pNode->data.decoded.decodedFrameCount = totalFrameCount; /* We've decoded everything. */
|
pDataBuffer->pNode->data.decoded.decodedFrameCount = totalFrameCount; /* We've decoded everything. */
|
||||||
} else {
|
} else {
|
||||||
|
pDataBuffer->pNode->data.decoded.supplier = ma_decoded_data_supplier_unknown;
|
||||||
pDataBuffer->pNode->data.decoded.pData = NULL;
|
pDataBuffer->pNode->data.decoded.pData = NULL;
|
||||||
pDataBuffer->pNode->data.decoded.frameCount = 0;
|
pDataBuffer->pNode->data.decoded.frameCount = 0;
|
||||||
pDataBuffer->pNode->data.decoded.decodedFrameCount = 0;
|
pDataBuffer->pNode->data.decoded.decodedFrameCount = 0;
|
||||||
@@ -6744,6 +7210,16 @@ MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_man
|
|||||||
result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead, isLooping);
|
result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead, isLooping);
|
||||||
pDataBuffer->cursorInPCMFrames += framesRead;
|
pDataBuffer->cursorInPCMFrames += 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 (pFramesRead != NULL) {
|
if (pFramesRead != NULL) {
|
||||||
*pFramesRead = framesRead;
|
*pFramesRead = framesRead;
|
||||||
}
|
}
|
||||||
@@ -6833,7 +7309,7 @@ MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_man
|
|||||||
return MA_BUSY; /* Still loading. */
|
return MA_BUSY; /* Still loading. */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_buffer) {
|
if (pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_buffer || pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_paged_buffer) {
|
||||||
MA_ASSERT(pDataBuffer->pNode->data.type == ma_resource_manager_data_buffer_encoding_decoded);
|
MA_ASSERT(pDataBuffer->pNode->data.type == ma_resource_manager_data_buffer_encoding_decoded);
|
||||||
|
|
||||||
*pFormat = pDataBuffer->pNode->data.decoded.format;
|
*pFormat = pDataBuffer->pNode->data.decoded.format;
|
||||||
@@ -6938,7 +7414,7 @@ MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resourc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_buffer) {
|
if (pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_buffer || pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_paged_buffer) {
|
||||||
/* Retrieve the available frames based on how many frames we've currently decoded, and *not* the total capacity of the audio buffer. */
|
/* Retrieve the available frames based on how many frames we've currently decoded, and *not* the total capacity of the audio buffer. */
|
||||||
if (pDataBuffer->pNode->data.decoded.decodedFrameCount > pDataBuffer->cursorInPCMFrames) {
|
if (pDataBuffer->pNode->data.decoded.decodedFrameCount > pDataBuffer->cursorInPCMFrames) {
|
||||||
*pAvailableFrames = pDataBuffer->pNode->data.decoded.decodedFrameCount - pDataBuffer->cursorInPCMFrames;
|
*pAvailableFrames = pDataBuffer->pNode->data.decoded.decodedFrameCount - pDataBuffer->cursorInPCMFrames;
|
||||||
@@ -8188,6 +8664,7 @@ static ma_result ma_resource_manager_process_job__load_data_buffer(ma_resource_m
|
|||||||
framesRead = ma_decoder_read_pcm_frames(pDecoder, pData, pageSizeInFrames);
|
framesRead = ma_decoder_read_pcm_frames(pDecoder, pData, pageSizeInFrames);
|
||||||
if (framesRead < pageSizeInFrames) {
|
if (framesRead < pageSizeInFrames) {
|
||||||
/* We've read the entire sound. This is the simple case. We just need to set the result to MA_SUCCESS. */
|
/* We've read the entire sound. This is the simple case. We just need to set the result to MA_SUCCESS. */
|
||||||
|
pDataBuffer->pNode->data.decoded.supplier = ma_decoded_data_supplier_buffer;
|
||||||
pDataBuffer->pNode->data.decoded.pData = pData;
|
pDataBuffer->pNode->data.decoded.pData = pData;
|
||||||
pDataBuffer->pNode->data.decoded.frameCount = framesRead;
|
pDataBuffer->pNode->data.decoded.frameCount = framesRead;
|
||||||
|
|
||||||
@@ -8208,15 +8685,6 @@ static ma_result ma_resource_manager_process_job__load_data_buffer(ma_resource_m
|
|||||||
/* We've still got more to decode. We just set the result to MA_BUSY which will tell the next section below to post a paging event. */
|
/* We've still got more to decode. We just set the result to MA_BUSY which will tell the next section below to post a paging event. */
|
||||||
result = MA_BUSY;
|
result = MA_BUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* If we successfully initialized and the sound is of a known length we can start initialize the connector. */
|
|
||||||
if (result == MA_SUCCESS || result == MA_BUSY) {
|
|
||||||
if (pDataBuffer->pNode->data.decoded.decodedFrameCount > 0) {
|
|
||||||
result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pJob->loadDataBuffer.pInitNotification);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
@@ -8254,9 +8722,10 @@ done:
|
|||||||
pageDataBufferJob.pageDataBuffer.decodedFrameCount = framesRead;
|
pageDataBufferJob.pageDataBuffer.decodedFrameCount = framesRead;
|
||||||
|
|
||||||
if (totalFrameCount > 0) {
|
if (totalFrameCount > 0) {
|
||||||
pageDataBufferJob.pageDataBuffer.isUnknownLength = MA_FALSE;
|
/* The length is known. Use a simple buffer. */
|
||||||
pageDataBufferJob.pageDataBuffer.pInitNotification = NULL; /* <-- Clear this notification to NULL to ensure it's not signalled a second time at the end of decoding, which will be done for sounds of unknown length. */
|
pageDataBufferJob.pageDataBuffer.pInitNotification = NULL; /* <-- Clear this notification to NULL to ensure it's not signalled a second time at the end of decoding, which will be done for sounds of unknown length. */
|
||||||
|
|
||||||
|
pDataBuffer->pNode->data.decoded.supplier = ma_decoded_data_supplier_buffer;
|
||||||
pDataBuffer->pNode->data.decoded.pData = pData;
|
pDataBuffer->pNode->data.decoded.pData = pData;
|
||||||
pDataBuffer->pNode->data.decoded.frameCount = totalFrameCount;
|
pDataBuffer->pNode->data.decoded.frameCount = totalFrameCount;
|
||||||
|
|
||||||
@@ -8271,19 +8740,24 @@ done:
|
|||||||
/* The sound is of a known length so we can go ahead and initialize the connector now. */
|
/* The sound is of a known length so we can go ahead and initialize the connector now. */
|
||||||
result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pJob->loadDataBuffer.pInitNotification);
|
result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pJob->loadDataBuffer.pInitNotification);
|
||||||
} else {
|
} else {
|
||||||
pageDataBufferJob.pageDataBuffer.isUnknownLength = MA_TRUE;
|
/* The length is unknown. Need to use a paged buffer. */
|
||||||
pageDataBufferJob.pageDataBuffer.pInitNotification = pJob->loadDataBuffer.pInitNotification; /* <-- Set this to the init notification so that the PAGE_DATA_BUFFER job can signal it at the end of decoding. Only needed for sounds of unknown length. */
|
pDataBuffer->pNode->data.decoded.supplier = ma_decoded_data_supplier_paged;
|
||||||
|
pDataBuffer->pNode->data.decoded.pData = NULL; /* <-- Not used for paged buffers. Set to NULL for safety. */
|
||||||
|
pDataBuffer->pNode->data.decoded.frameCount = 0; /* <-- Will be set at the end of decoding. */
|
||||||
|
pDataBuffer->pNode->data.decoded.decodedFrameCount = framesRead;
|
||||||
|
|
||||||
/*
|
/* For a paged data buffer need to initialize the data structure for the paged data before initializing the connector. */
|
||||||
These members are all set after the last page has been decoded. The reason for this is that the application should not be attempting to
|
result = ma_paged_audio_buffer_data_init(pDataBuffer->pNode->data.decoded.format, pDataBuffer->pNode->data.decoded.channels, &pDataBuffer->pNode->data.decoded.pagedData);
|
||||||
read any data until the sound is fully decoded because we're going to be dynamically expanding pData and we'll be introducing complications
|
if (result == MA_SUCCESS) {
|
||||||
by letting the application get access to it.
|
/* A page needs to be allocated and appended for the initial data. Not doing this will result in the first page being missing. */
|
||||||
*/
|
result = ma_paged_audio_buffer_data_allocate_and_append_page(&pDataBuffer->pNode->data.decoded.pagedData, (ma_uint32)framesRead, pData, &pResourceManager->config.allocationCallbacks);
|
||||||
pDataBuffer->pNode->data.decoded.pData = NULL;
|
if (result == MA_SUCCESS) {
|
||||||
pDataBuffer->pNode->data.decoded.frameCount = 0;
|
result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pJob->loadDataBuffer.pInitNotification);
|
||||||
pDataBuffer->pNode->data.decoded.decodedFrameCount = 0;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result = MA_SUCCESS;
|
/* The pData pointer is no longer needed for the paged audio buffers (it's only used for regular buffers). */
|
||||||
|
ma_free(pData, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODED_BUFFER*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == MA_SUCCESS) {
|
if (result == MA_SUCCESS) {
|
||||||
@@ -8337,7 +8811,7 @@ static ma_result ma_resource_manager_process_job__free_data_buffer(ma_resource_m
|
|||||||
static ma_result ma_resource_manager_process_job__page_data_buffer(ma_resource_manager* pResourceManager, ma_job* pJob)
|
static ma_result ma_resource_manager_process_job__page_data_buffer(ma_resource_manager* pResourceManager, ma_job* pJob)
|
||||||
{
|
{
|
||||||
ma_result result = MA_SUCCESS;
|
ma_result result = MA_SUCCESS;
|
||||||
ma_uint64 pageSizeInFrames;
|
ma_uint32 pageSizeInFrames;
|
||||||
ma_uint64 framesRead;
|
ma_uint64 framesRead;
|
||||||
void* pRunningData;
|
void* pRunningData;
|
||||||
ma_job jobCopy;
|
ma_job jobCopy;
|
||||||
@@ -8363,52 +8837,60 @@ static ma_result ma_resource_manager_process_job__page_data_buffer(ma_resource_m
|
|||||||
/* We need to know the size of a page in frames to know how many frames to decode. */
|
/* We need to know the size of a page in frames to know how many frames to decode. */
|
||||||
pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (jobCopy.pageDataBuffer.pDecoder->outputSampleRate/1000);
|
pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (jobCopy.pageDataBuffer.pDecoder->outputSampleRate/1000);
|
||||||
|
|
||||||
/* If the total length is unknown we may need to expand the size of the buffer. */
|
|
||||||
if (jobCopy.pageDataBuffer.isUnknownLength == MA_TRUE) {
|
|
||||||
ma_uint64 requiredSize = (jobCopy.pageDataBuffer.decodedFrameCount + pageSizeInFrames) * ma_get_bytes_per_frame(jobCopy.pageDataBuffer.pDecoder->outputFormat, jobCopy.pageDataBuffer.pDecoder->outputChannels);
|
|
||||||
if (requiredSize <= MA_SIZE_MAX) {
|
|
||||||
if (requiredSize > jobCopy.pageDataBuffer.dataSizeInBytes) {
|
|
||||||
size_t newSize = (size_t)ma_max(requiredSize, jobCopy.pageDataBuffer.dataSizeInBytes * 2);
|
|
||||||
void *pNewData = ma__realloc_from_callbacks(jobCopy.pageDataBuffer.pData, newSize, jobCopy.pageDataBuffer.dataSizeInBytes, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODED_BUFFER*/);
|
|
||||||
if (pNewData != NULL) {
|
|
||||||
jobCopy.pageDataBuffer.pData = pNewData;
|
|
||||||
jobCopy.pageDataBuffer.dataSizeInBytes = newSize;
|
|
||||||
} else {
|
|
||||||
result = MA_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = MA_TOO_BIG;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We should have the memory set up so now we can decode the next page. */
|
/* We should have the memory set up so now we can decode the next page. */
|
||||||
if (result == MA_SUCCESS) {
|
if (result == MA_SUCCESS) {
|
||||||
ma_uint64 framesToTryReading = pageSizeInFrames;
|
ma_uint64 framesToTryReading = pageSizeInFrames;
|
||||||
|
ma_paged_audio_buffer_page* pPage = NULL; /* <-- Only used for sounds using a paged data buffer. */
|
||||||
|
|
||||||
/* Can't try reading more than what we originally retrieved when we first initialized the decoder. */
|
/* Can't try reading more than what we originally retrieved when we first initialized the decoder. */
|
||||||
if (jobCopy.pageDataBuffer.isUnknownLength == MA_FALSE) {
|
if (pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_buffer) {
|
||||||
|
/* Using a regular buffer. Don't read more than we originally retrieved when we first initialized the decoder. */
|
||||||
ma_uint64 framesRemaining = pDataBuffer->pNode->data.decoded.frameCount - pDataBuffer->pNode->data.decoded.decodedFrameCount;
|
ma_uint64 framesRemaining = pDataBuffer->pNode->data.decoded.frameCount - pDataBuffer->pNode->data.decoded.decodedFrameCount;
|
||||||
if (framesToTryReading > framesRemaining) {
|
if (framesToTryReading > framesRemaining) {
|
||||||
framesToTryReading = framesRemaining;
|
framesToTryReading = framesRemaining;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pRunningData = ma_offset_ptr(jobCopy.pageDataBuffer.pData, jobCopy.pageDataBuffer.decodedFrameCount * ma_get_bytes_per_frame(jobCopy.pageDataBuffer.pDecoder->outputFormat, jobCopy.pageDataBuffer.pDecoder->outputChannels));
|
pRunningData = ma_offset_ptr(jobCopy.pageDataBuffer.pData, jobCopy.pageDataBuffer.decodedFrameCount * ma_get_bytes_per_frame(jobCopy.pageDataBuffer.pDecoder->outputFormat, jobCopy.pageDataBuffer.pDecoder->outputChannels));
|
||||||
|
} else {
|
||||||
|
/* Using a paged buffer which means we don't know the length. Allocate a page and read as much as we can. This will be appended to the decoder. */
|
||||||
|
ma_paged_audio_buffer_data_allocate_page(&pDataBuffer->pNode->data.decoded.pagedData, pageSizeInFrames, NULL, &pResourceManager->config.allocationCallbacks, &pPage);
|
||||||
|
if (result == MA_SUCCESS) {
|
||||||
|
pPage->sizeInFrames = 0; /* <-- For safety just in case we get an error later on. We'll update this to it's proper value later. */
|
||||||
|
pRunningData = pPage->pAudioData;
|
||||||
|
} else {
|
||||||
|
pRunningData = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pRunningData != NULL) {
|
||||||
framesRead = ma_decoder_read_pcm_frames(jobCopy.pageDataBuffer.pDecoder, pRunningData, framesToTryReading);
|
framesRead = ma_decoder_read_pcm_frames(jobCopy.pageDataBuffer.pDecoder, pRunningData, framesToTryReading);
|
||||||
if (framesRead < framesToTryReading) {
|
if (framesRead < framesToTryReading) {
|
||||||
result = MA_AT_END;
|
result = MA_AT_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the total length is known we can increment out decoded frame count. Otherwise it needs to be left at 0 until the last page is decoded. */
|
if (pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_paged_buffer) {
|
||||||
if (jobCopy.pageDataBuffer.isUnknownLength == MA_FALSE) {
|
pPage->sizeInFrames = (ma_uint32)framesRead; /* Safe cast. */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
framesRead = 0;
|
||||||
|
result = MA_AT_END; /* We don't have a destination buffer which means we probably ran out of memory in ma_paged_audio_buffer_allocate_page(). Just terminate decoding by pretending we're at the end. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure the page is attached to the buffer if we're running a paged audio buffer. */
|
||||||
|
if (pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_paged_buffer) {
|
||||||
|
ma_paged_audio_buffer_data_append_page(&pDataBuffer->pNode->data.decoded.pagedData, pPage);
|
||||||
|
}
|
||||||
|
|
||||||
pDataBuffer->pNode->data.decoded.decodedFrameCount += framesRead;
|
pDataBuffer->pNode->data.decoded.decodedFrameCount += framesRead;
|
||||||
|
|
||||||
/* If we've read up to the length reported when we first loaded the file we've reached the end. */
|
/* If we've read up to the length reported when we first loaded the file we've reached the end. */
|
||||||
|
if (pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_buffer) {
|
||||||
if (pDataBuffer->pNode->data.decoded.decodedFrameCount == pDataBuffer->pNode->data.decoded.frameCount) {
|
if (pDataBuffer->pNode->data.decoded.decodedFrameCount == pDataBuffer->pNode->data.decoded.frameCount) {
|
||||||
result = MA_AT_END;
|
result = MA_AT_END;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
/* The length of a paged buffer needs to be dynamically expanded as we decode as we don't know the length until it's been fully decoded. */
|
||||||
|
pDataBuffer->lengthInPCMFrames += framesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -8431,21 +8913,16 @@ static ma_result ma_resource_manager_process_job__page_data_buffer(ma_resource_m
|
|||||||
ma_decoder_uninit(jobCopy.pageDataBuffer.pDecoder);
|
ma_decoder_uninit(jobCopy.pageDataBuffer.pDecoder);
|
||||||
ma__free_from_callbacks(jobCopy.pageDataBuffer.pDecoder, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODER*/);
|
ma__free_from_callbacks(jobCopy.pageDataBuffer.pDecoder, &pResourceManager->config.allocationCallbacks/*, MA_ALLOCATION_TYPE_DECODER*/);
|
||||||
|
|
||||||
/* When the length is unknown we were doubling the size of the buffer each time we needed more data. Let's try reducing this by doing a final realloc(). */
|
|
||||||
if (jobCopy.pageDataBuffer.isUnknownLength) {
|
|
||||||
ma_uint64 newSizeInBytes = jobCopy.pageDataBuffer.decodedFrameCount * ma_get_bytes_per_frame(pDataBuffer->pNode->data.decoded.format, pDataBuffer->pNode->data.decoded.channels);
|
|
||||||
void* pNewData = ma__realloc_from_callbacks(jobCopy.pageDataBuffer.pData, (size_t)newSizeInBytes, jobCopy.pageDataBuffer.dataSizeInBytes, &pResourceManager->config.allocationCallbacks);
|
|
||||||
if (pNewData != NULL) {
|
|
||||||
jobCopy.pageDataBuffer.pData = pNewData;
|
|
||||||
jobCopy.pageDataBuffer.dataSizeInBytes = (size_t)newSizeInBytes; /* <-- Don't really need to set this, but I think it's good practice. */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We can now set the frame counts appropriately. We want to set the frame count regardless of whether or not it had a known length just in case we have
|
We can now set the frame counts appropriately. We want to set the frame count regardless of whether or not it had a known length just in case we have
|
||||||
a weird situation where the frame count an opening time was different to the final count we got after reading.
|
a weird situation where the frame count an opening time was different to the final count we got after reading.
|
||||||
*/
|
*/
|
||||||
|
if (pDataBuffer->connectorType == ma_resource_manager_data_buffer_connector_buffer) {
|
||||||
pDataBuffer->pNode->data.decoded.pData = jobCopy.pageDataBuffer.pData;
|
pDataBuffer->pNode->data.decoded.pData = jobCopy.pageDataBuffer.pData;
|
||||||
|
} else {
|
||||||
|
pDataBuffer->pNode->data.decoded.pData = NULL; /* Must be null when a paged data buffer is being used. */
|
||||||
|
}
|
||||||
|
|
||||||
pDataBuffer->pNode->data.decoded.frameCount = jobCopy.pageDataBuffer.decodedFrameCount;
|
pDataBuffer->pNode->data.decoded.frameCount = jobCopy.pageDataBuffer.decodedFrameCount;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -8462,11 +8939,6 @@ static ma_result ma_resource_manager_process_job__page_data_buffer(ma_resource_m
|
|||||||
result = MA_SUCCESS;
|
result = MA_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If it was an unknown length, we can finally initialize the connector. For sounds of a known length, the connector was initialized when the first page was decoded in MA_JOB_LOAD_DATA_BUFFER. */
|
|
||||||
if (jobCopy.pageDataBuffer.isUnknownLength) {
|
|
||||||
result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, jobCopy.pageDataBuffer.pInitNotification);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We need to set the status of the page so other things can know about it. We can only change the status away from MA_BUSY. If it's anything else it cannot be changed. */
|
/* We need to set the status of the page so other things can know about it. We can only change the status away from MA_BUSY. If it's anything else it cannot be changed. */
|
||||||
c89atomic_compare_and_swap_32(&pDataBuffer->pNode->result, MA_BUSY, result);
|
c89atomic_compare_and_swap_32(&pDataBuffer->pNode->result, MA_BUSY, result);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user