Commit d93eaf22 authored by David Reid's avatar David Reid

Move ma_paged_audio_buffer into the main library.

parent ebdc79ab
......@@ -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
......@@ -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] = {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment