Add a new ma_audio_ring_buffer data source.

This is a wrapper around `ma_ring_buffer` and is more specialized
towards audio. It is a data source and replaces `ma_pcm_rb` which will
be removed in a future commit.

Public issue https://github.com/mackron/miniaudio/issues/671
This commit is contained in:
David Reid
2026-01-25 16:50:28 +10:00
parent edb64e6017
commit af2cf5d161
+262 -43
View File
@@ -6270,6 +6270,29 @@ MA_API ma_uint32 ma_ring_buffer_length(const ma_ring_buffer* pRingBuffer);
MA_API ma_uint32 ma_ring_buffer_capacity(const ma_ring_buffer* pRingBuffer);
/* END ma_ring_buffer.h */
typedef struct ma_audio_ring_buffer
{
ma_data_source_base ds;
ma_ring_buffer rb;
ma_format format;
ma_uint32 channels;
ma_uint32 sampleRate; /* Not required for the ring buffer itself, but useful for associating the data with some sample rate, particularly for data sources. */
void* pBuffer;
ma_allocation_callbacks allocationCallbacks;
} ma_audio_ring_buffer;
MA_API ma_result ma_audio_ring_buffer_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 capacityInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_audio_ring_buffer* pRingBuffer);
MA_API void ma_audio_ring_buffer_uninit(ma_audio_ring_buffer* pRingBuffer);
MA_API ma_uint64 ma_audio_ring_buffer_map_produce(ma_audio_ring_buffer* pRingBuffer, ma_uint64 frameCount, void** ppMappedBuffer); /* Returns the number of frames actually mapped. */
MA_API void ma_audio_ring_buffer_unmap_produce(ma_audio_ring_buffer* pRingBuffer, ma_uint64 frameCount);
MA_API ma_uint64 ma_audio_ring_buffer_map_consume(ma_audio_ring_buffer* pRingBuffer, ma_uint64 frameCount, void** ppMappedBuffer); /* Returns the number of frames actually mapped. */
MA_API void ma_audio_ring_buffer_unmap_consume(ma_audio_ring_buffer* pRingBuffer, ma_uint64 frameCount);
MA_API ma_result ma_audio_ring_buffer_write_pcm_frames(ma_audio_ring_buffer* pRingBuffer, const void* pFrames, ma_uint64 frameCount, ma_uint64* pFramesWritten);
MA_API ma_result ma_audio_ring_buffer_read_pcm_frames(ma_audio_ring_buffer* pRingBuffer, void* pFrames, ma_uint64 frameCount, ma_uint64* pFramesRead);
MA_API ma_result ma_audio_ring_buffer_get_length_in_pcm_frames(ma_audio_ring_buffer* pRingBuffer, ma_uint64* pLength);
typedef struct
{
void* pBuffer;
@@ -6346,7 +6369,7 @@ in frames. The internal sample rate of the capture device is also needed in orde
*/
typedef struct
{
ma_pcm_rb rb;
ma_audio_ring_buffer rb;
} ma_duplex_rb;
MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB);
@@ -20750,7 +20773,7 @@ static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frame
}
}
static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB)
static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_audio_ring_buffer* pRB)
{
ma_result result;
ma_uint32 totalDeviceFramesProcessed = 0;
@@ -20763,22 +20786,15 @@ static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, m
/* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */
for (;;) {
ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
ma_uint64 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
ma_uint64 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
ma_uint64 framesProcessedInDeviceFormat;
ma_uint64 framesProcessedInClientFormat;
void* pFramesInClientFormat;
result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat);
if (result != MA_SUCCESS) {
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer.");
break;
}
framesToProcessInClientFormat = ma_audio_ring_buffer_map_produce(pRB, framesToProcessInClientFormat, &pFramesInClientFormat);
if (framesToProcessInClientFormat == 0) {
if (ma_pcm_rb_pointer_distance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) {
break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */
}
break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */
}
/* Convert. */
@@ -20789,11 +20805,7 @@ static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, m
break;
}
result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat); /* Safe cast. */
if (result != MA_SUCCESS) {
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer.");
break;
}
ma_audio_ring_buffer_unmap_produce(pRB, framesProcessedInClientFormat);
pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */
@@ -20807,9 +20819,8 @@ static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, m
return MA_SUCCESS;
}
static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB)
static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_audio_ring_buffer* pRB)
{
ma_result result;
ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
ma_uint32 totalFramesReadOut = 0;
@@ -20838,38 +20849,29 @@ static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice,
pDevice->playback.inputCacheConsumed += framesConvertedIn;
pDevice->playback.inputCacheRemaining -= framesConvertedIn;
totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */
pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */
pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
}
/* If there's no more data in the cache we'll need to fill it with some. */
if (totalFramesReadOut < frameCount && pDevice->playback.inputCacheRemaining == 0) {
ma_uint32 inputFrameCount;
ma_uint64 inputFrameCount;
void* pInputFrames;
inputFrameCount = (ma_uint32)pDevice->playback.inputCacheCap;
result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames);
if (result == MA_SUCCESS) {
if (inputFrameCount > 0) {
ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount);
} else {
if (ma_pcm_rb_pointer_distance(pRB) == 0) {
break; /* Underrun. */
}
}
inputFrameCount = pDevice->playback.inputCacheCap;
inputFrameCount = ma_audio_ring_buffer_map_consume(pRB, inputFrameCount, &pInputFrames);
if (inputFrameCount > 0) {
ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount);
ma_audio_ring_buffer_unmap_consume(pRB, inputFrameCount);
} else {
/* No capture data available. Feed in silence. */
/* Underrun. No capture data available. Feed in silence. */
inputFrameCount = (ma_uint32)ma_min(pDevice->playback.inputCacheCap, sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, silentInputFrames, inputFrameCount);
ma_audio_ring_buffer_unmap_consume(pRB, 0); /* <-- Don't *actually* have to unmap this since it's a count of zero, but it makes me feel better having it be properly paired. */
}
pDevice->playback.inputCacheConsumed = 0;
pDevice->playback.inputCacheRemaining = inputFrameCount;
result = ma_pcm_rb_commit_read(pRB, inputFrameCount);
if (result != MA_SUCCESS) {
return result; /* Should never happen. */
}
}
}
@@ -63661,6 +63663,222 @@ MA_API ma_uint32 ma_ring_buffer_capacity(const ma_ring_buffer* pRingBuffer)
/* END ma_ring_buffer.c */
static ma_result ma_audio_ring_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFrames, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
return ma_audio_ring_buffer_read_pcm_frames((ma_audio_ring_buffer*)pDataSource, pFrames, frameCount, pFramesRead);
}
static ma_result ma_audio_ring_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_audio_ring_buffer* pAudioQueue = (ma_audio_ring_buffer*)pDataSource;
*pFormat = pAudioQueue->format;
*pChannels = pAudioQueue->channels;
*pSampleRate = pAudioQueue->sampleRate;
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioQueue->channels);
return MA_SUCCESS;
}
static ma_result ma_audio_ring_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
{
return ma_audio_ring_buffer_get_length_in_pcm_frames((ma_audio_ring_buffer*)pDataSource, pLength);
}
static ma_data_source_vtable ma_gDataSourceVTable_AudioRingBuffer =
{
ma_audio_ring_buffer__data_source_on_read,
NULL, /* No seeking in ring buffers. */
ma_audio_ring_buffer__data_source_on_get_data_format,
NULL, /* No notion of a cursor. */
ma_audio_ring_buffer__data_source_on_get_length,
NULL, /* onSetLooping */
0
};
MA_API ma_result ma_audio_ring_buffer_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 capacityInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_audio_ring_buffer* pRingBuffer)
{
ma_result result;
ma_data_source_config dataSourceConfig;
ma_uint32 bpf;
void* pBuffer;
if (pRingBuffer == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pRingBuffer);
if (capacityInFrames == 0) {
return MA_INVALID_ARGS;
}
bpf = ma_get_bytes_per_frame(format, channels);
if (bpf == 0) {
return MA_INVALID_ARGS;
}
pBuffer = ma_malloc(capacityInFrames * bpf * 2, pAllocationCallbacks);
if (pBuffer == NULL) {
return MA_OUT_OF_MEMORY;
}
/* Initialize the data source. */
dataSourceConfig = ma_data_source_config_init();
dataSourceConfig.vtable = &ma_gDataSourceVTable_AudioRingBuffer;
result = ma_data_source_init(&dataSourceConfig, &pRingBuffer->ds);
if (result != MA_SUCCESS) {
ma_free(pBuffer, pAllocationCallbacks);
return result;
}
/* Now for the ring buffer. */
ma_ring_buffer_init(capacityInFrames, bpf, 0, pBuffer, &pRingBuffer->rb);
pRingBuffer->format = format;
pRingBuffer->channels = channels;
pRingBuffer->sampleRate = sampleRate;
pRingBuffer->pBuffer = pBuffer;
ma_allocation_callbacks_init_copy(&pRingBuffer->allocationCallbacks, pAllocationCallbacks);
return MA_SUCCESS;
}
MA_API void ma_audio_ring_buffer_uninit(ma_audio_ring_buffer* pRingBuffer)
{
if (pRingBuffer == NULL) {
return;
}
ma_free(pRingBuffer->pBuffer, &pRingBuffer->allocationCallbacks);
ma_data_source_uninit(&pRingBuffer->ds);
}
MA_API ma_uint64 ma_audio_ring_buffer_map_produce(ma_audio_ring_buffer* pRingBuffer, ma_uint64 frameCount, void** ppMappedBuffer)
{
if (pRingBuffer == NULL) {
return 0;
}
/* We're not going to write more than 32-bits worth of frames at a time. */
if (frameCount > 0xFFFFFFFF) {
frameCount = 0xFFFFFFFF;
}
return (ma_uint64)ma_ring_buffer_map_produce(&pRingBuffer->rb, (size_t)frameCount, ppMappedBuffer);
}
MA_API void ma_audio_ring_buffer_unmap_produce(ma_audio_ring_buffer* pRingBuffer, ma_uint64 frameCount)
{
if (pRingBuffer == NULL) {
return;
}
ma_ring_buffer_unmap_produce(&pRingBuffer->rb, (size_t)frameCount);
}
MA_API ma_uint64 ma_audio_ring_buffer_map_consume(ma_audio_ring_buffer* pRingBuffer, ma_uint64 frameCount, void** ppMappedBuffer)
{
if (pRingBuffer == NULL) {
return 0;
}
/* We're not going to write more than 32-bits worth of frames at a time. */
if (frameCount > 0xFFFFFFFF) {
frameCount = 0xFFFFFFFF;
}
return (ma_uint64)ma_ring_buffer_map_consume(&pRingBuffer->rb, (size_t)frameCount, ppMappedBuffer);
}
MA_API void ma_audio_ring_buffer_unmap_consume(ma_audio_ring_buffer* pRingBuffer, ma_uint64 frameCount)
{
if (pRingBuffer == NULL) {
return;
}
ma_ring_buffer_unmap_consume(&pRingBuffer->rb, (size_t)frameCount);
}
MA_API ma_result ma_audio_ring_buffer_write_pcm_frames(ma_audio_ring_buffer* pRingBuffer, const void* pFrames, ma_uint64 frameCount, ma_uint64* pFramesWritten)
{
void* pMappedBuffer;
if (pFramesWritten != NULL) {
*pFramesWritten = 0;
}
if (pRingBuffer == NULL) {
return MA_INVALID_ARGS;
}
frameCount = ma_ring_buffer_map_produce(&pRingBuffer->rb, frameCount, &pMappedBuffer);
{
size_t bytesToCopy = (size_t)(frameCount * ma_get_bytes_per_frame(pRingBuffer->format, pRingBuffer->channels));
if (pFrames != NULL) {
MA_COPY_MEMORY(pMappedBuffer, pFrames, bytesToCopy);
} else {
MA_ZERO_MEMORY(pMappedBuffer, bytesToCopy);
}
}
ma_ring_buffer_unmap_produce(&pRingBuffer->rb, frameCount);
if (pFramesWritten != NULL) {
*pFramesWritten = frameCount;
}
return MA_SUCCESS;
}
MA_API ma_result ma_audio_ring_buffer_read_pcm_frames(ma_audio_ring_buffer* pRingBuffer, void* pFrames, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
void* pMappedBuffer;
if (pFramesRead != NULL) {
*pFramesRead = 0;
}
if (pRingBuffer == NULL) {
return MA_INVALID_ARGS;
}
frameCount = ma_ring_buffer_map_consume(&pRingBuffer->rb, frameCount, &pMappedBuffer);
{
size_t bytesToCopy = (size_t)(frameCount * ma_get_bytes_per_frame(pRingBuffer->format, pRingBuffer->channels));
if (pFrames != NULL) {
MA_COPY_MEMORY(pFrames, pMappedBuffer, bytesToCopy);
}
}
ma_ring_buffer_unmap_consume(&pRingBuffer->rb, frameCount);
if (pFramesRead != NULL) {
*pFramesRead = frameCount;
}
return MA_SUCCESS;
}
MA_API ma_result ma_audio_ring_buffer_get_length_in_pcm_frames(ma_audio_ring_buffer* pRingBuffer, ma_uint64* pLength)
{
if (pLength == NULL) {
return MA_INVALID_ARGS;
}
*pLength = 0;
if (pRingBuffer == NULL) {
return MA_INVALID_ARGS;
}
*pLength = ma_ring_buffer_length(&pRingBuffer->rb);
return MA_SUCCESS;
}
static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset)
@@ -64460,25 +64678,25 @@ MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureCha
ma_result result;
ma_uint32 sizeInFrames;
sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5);
sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 3);
if (sizeInFrames == 0) {
return MA_INVALID_ARGS;
}
result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb);
result = ma_audio_ring_buffer_init(captureFormat, captureChannels, sampleRate, sizeInFrames, pAllocationCallbacks, &pRB->rb);
if (result != MA_SUCCESS) {
return result;
}
/* Seek forward a bit so we have a bit of a buffer in case of desyncs. */
ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2);
ma_audio_ring_buffer_write_pcm_frames(&pRB->rb, NULL, captureInternalPeriodSizeInFrames * 2, NULL);
return MA_SUCCESS;
}
MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB)
{
ma_pcm_rb_uninit((ma_pcm_rb*)pRB);
ma_audio_ring_buffer_uninit(&pRB->rb);
return MA_SUCCESS;
}
@@ -66551,6 +66769,7 @@ MA_API void ma_audio_queue_uninit(ma_audio_queue* pAudioQueue)
}
ma_free(pAudioQueue->pBuffer, &pAudioQueue->allocationCallbacks);
ma_data_source_uninit(&pAudioQueue->ds);
}
static void ma_audio_queue_write_pcm_frames_nolock(ma_audio_queue* pAudioQueue, const void* pFrames, ma_uint64 frameCount, ma_uint32 bpf)