mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-21 15:56:58 +02:00
Move ma_paged_audio_buffer into the main library.
This commit is contained in:
+447
@@ -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] = {
|
||||
|
||||
Reference in New Issue
Block a user