From d93eaf22c101430b7230662ecf940b3f7223097d Mon Sep 17 00:00:00 2001 From: David Reid Date: Fri, 16 Jul 2021 17:38:36 +1000 Subject: [PATCH] Move ma_paged_audio_buffer into the main library. --- miniaudio.h | 447 ++++++++++++++++++++++++++++++++++++ research/miniaudio_engine.h | 446 ----------------------------------- 2 files changed, 447 insertions(+), 446 deletions(-) diff --git a/miniaudio.h b/miniaudio.h index adad1f41..f4a9c7f1 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -6023,6 +6023,69 @@ MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); +/* +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_uint64 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_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); +MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); +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); +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); + + /************************************************************************************************************************************************************ @@ -44647,6 +44710,390 @@ MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAu + + +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_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage) +{ + ma_paged_audio_buffer_page* pPage; + ma_uint64 allocationSize; + + if (ppPage == NULL) { + return MA_INVALID_ARGS; + } + + *ppPage = NULL; + + if (pData == NULL) { + return MA_INVALID_ARGS; + } + + allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels)); + if (allocationSize > MA_SIZE_MAX) { + return MA_OUT_OF_MEMORY; /* Too big. */ + } + + pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */ + 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_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pData == NULL || pPage == NULL) { + return MA_INVALID_ARGS; + } + + /* It's assumed the page is not attached to the list. */ + ma_free(pPage, pAllocationCallbacks); + + 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_channel* pChannelMap, size_t channelMapCap) +{ + 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. */ + ma_get_standard_channel_map(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels); + + 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, + 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) +{ + if (pPagedAudioBuffer == NULL) { + return; + } + + /* Nothing to do. The data needs to be deleted separately. */ +} + +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); +} + + + /************************************************************************************************************************************************************** VFS diff --git a/research/miniaudio_engine.h b/research/miniaudio_engine.h index 184e705c..2dff4477 100644 --- a/research/miniaudio_engine.h +++ b/research/miniaudio_engine.h @@ -35,70 +35,6 @@ The best resource to use when understanding the API is the function declarations extern "C" { #endif - -/* -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_uint64 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_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); -MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); -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); -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 =================== @@ -2357,388 +2293,6 @@ MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode); #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_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage) -{ - ma_paged_audio_buffer_page* pPage; - ma_uint64 allocationSize; - - if (ppPage == NULL) { - return MA_INVALID_ARGS; - } - - *ppPage = NULL; - - if (pData == NULL) { - return MA_INVALID_ARGS; - } - - allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels)); - if (allocationSize > MA_SIZE_MAX) { - return MA_OUT_OF_MEMORY; /* Too big. */ - } - - pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */ - 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_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks) -{ - if (pData == NULL || pPage == NULL) { - return MA_INVALID_ARGS; - } - - /* It's assumed the page is not attached to the list. */ - ma_free(pPage, pAllocationCallbacks); - - 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_channel* pChannelMap, size_t channelMapCap) -{ - 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. */ - ma_get_standard_channel_map(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels); - - 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, - 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) -{ - if (pPagedAudioBuffer == NULL) { - return; - } - - /* Nothing to do. The data needs to be deleted separately. */ -} - -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) { size_t bytesPerSample[ma_format_count] = {