mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
Initial work on the audio queue data source.
Public issue https://github.com/mackron/miniaudio/issues/744
This commit is contained in:
@@ -15,7 +15,7 @@ effect.
|
||||
#define DEVICE_CHANNELS 1 /* For this example, always set to 1. */
|
||||
|
||||
static ma_waveform g_sourceData; /* The underlying data source of the source node. */
|
||||
static ma_audio_buffer_ref g_exciteData; /* The underlying data source of the excite node. */
|
||||
static ma_audio_queue g_exciteData; /* The underlying data source of the excite node. */
|
||||
static ma_data_source_node g_sourceNode; /* A data source node containing the source data we'll be sending through to the vocoder. This will be routed into the first bus of the vocoder node. */
|
||||
static ma_data_source_node g_exciteNode; /* A data source node containing the excite data we'll be sending through to the vocoder. This will be routed into the second bus of the vocoder node. */
|
||||
static ma_vocoder_node g_vocoderNode; /* The vocoder node. */
|
||||
@@ -37,7 +37,7 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin
|
||||
the data source is our `pInput` buffer. We need to update the underlying data source so that it
|
||||
read data from `pInput`.
|
||||
*/
|
||||
ma_audio_buffer_ref_set_data(&g_exciteData, pInput, frameCount);
|
||||
ma_audio_queue_push_pcm_frames(&g_exciteData, pInput, frameCount);
|
||||
|
||||
/* With the source buffer configured we can now read directly from the node graph. */
|
||||
ma_node_graph_read_pcm_frames(&g_nodeGraph, pOutput, frameCount, NULL);
|
||||
@@ -53,6 +53,7 @@ int main(int argc, char** argv)
|
||||
ma_data_source_node_config sourceNodeConfig;
|
||||
ma_data_source_node_config exciteNodeConfig;
|
||||
ma_waveform_config waveformConfig;
|
||||
ma_audio_queue_config audioQueueConfig;
|
||||
|
||||
deviceConfig = ma_device_config_init(ma_device_type_duplex);
|
||||
deviceConfig.capture.pDeviceID = NULL;
|
||||
@@ -115,7 +116,9 @@ int main(int argc, char** argv)
|
||||
|
||||
|
||||
/* Excite/modulator. Attached to input bus 1 of the vocoder node. */
|
||||
result = ma_audio_buffer_ref_init(device.capture.format, device.capture.channels, device.sampleRate, NULL, 0, &g_exciteData);
|
||||
audioQueueConfig = ma_audio_queue_config_init(device.capture.format, device.capture.channels, device.sampleRate, 0);
|
||||
|
||||
result = ma_audio_queue_init(&audioQueueConfig, &g_exciteData);
|
||||
if (result != MA_SUCCESS) {
|
||||
printf("Failed to initialize audio buffer for source.");
|
||||
goto done2;
|
||||
|
||||
+321
@@ -6189,6 +6189,60 @@ MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_b
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Audio Queue
|
||||
===========
|
||||
The audio queue is a data source that allows you to push data from one thread and then read it from
|
||||
another. The audio queue can be fixed sized or dynamic. Dynamic is the default. To use a static
|
||||
queue set the the size to non-0.
|
||||
|
||||
With a static queue, if you attempt to push data but there is no room, the entire data will be rejected
|
||||
and MA_NO_SPACE will be returned. For a dynamic queue it is restricted by memory capacity.
|
||||
|
||||
There is no seeking and no notion of a cursor. The length will be how many PCM frames are currently
|
||||
sitting in the queue.
|
||||
|
||||
The total capacity of the queue in PCM frames is restricted to 32-bits.
|
||||
|
||||
This is thread safe, but not currently 100% lock-free. This currently uses a dumb spinlock for thread
|
||||
safety, but there are plans to improve on this later.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ma_format format;
|
||||
ma_uint32 channels;
|
||||
ma_uint32 sampleRate;
|
||||
ma_uint32 sizeInFrames; /* When set to 0 (the default), the queue will be dynamically expanding. When set to non-0, the audio queue will be static. */
|
||||
ma_allocation_callbacks allocationCallbacks;
|
||||
} ma_audio_queue_config;
|
||||
|
||||
MA_API ma_audio_queue_config ma_audio_queue_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 sizeInFrames);
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ma_data_source_base ds;
|
||||
ma_format format;
|
||||
ma_uint32 channels;
|
||||
ma_uint32 sampleRate;
|
||||
ma_bool32 isStatic;
|
||||
ma_uint32 capacityInFrames;
|
||||
ma_uint64 cursorWrite;
|
||||
ma_uint64 cursorRead;
|
||||
void* pBuffer;
|
||||
ma_allocation_callbacks allocationCallbacks;
|
||||
ma_spinlock lock; /* Temporary until we get a good lock-free solution working. */
|
||||
} ma_audio_queue;
|
||||
|
||||
MA_API ma_result ma_audio_queue_init(const ma_audio_queue_config* pConfig, ma_audio_queue* pAudioQueue);
|
||||
MA_API void ma_audio_queue_uninit(ma_audio_queue* pAudioQueue);
|
||||
MA_API ma_result ma_audio_queue_push_pcm_frames(ma_audio_queue* pAudioQueue, const void* pFrames, ma_uint64 frameCount);
|
||||
MA_API ma_result ma_audio_queue_read_pcm_frames(ma_audio_queue* pAudioQueue, void* pFrames, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
||||
MA_API ma_result ma_audio_queue_get_length_in_pcm_frames(ma_audio_queue* pAudioQueue, ma_uint64* pLength);
|
||||
|
||||
|
||||
|
||||
/************************************************************************************************************************************************************
|
||||
|
||||
Ring Buffer
|
||||
@@ -66125,6 +66179,273 @@ MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_b
|
||||
|
||||
|
||||
|
||||
|
||||
MA_API ma_audio_queue_config ma_audio_queue_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 sizeInFrames)
|
||||
{
|
||||
ma_audio_queue_config config;
|
||||
|
||||
MA_ZERO_OBJECT(&config);
|
||||
config.format = format;
|
||||
config.channels = channels;
|
||||
config.sampleRate = sampleRate;
|
||||
config.sizeInFrames = sizeInFrames;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
static ma_result ma_audio_queue__data_source_on_read(ma_data_source* pDataSource, void* pFrames, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
||||
{
|
||||
return ma_audio_queue_read_pcm_frames((ma_audio_queue*)pDataSource, pFrames, frameCount, pFramesRead);
|
||||
}
|
||||
|
||||
static ma_result ma_audio_queue__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_queue* pAudioQueue = (ma_audio_queue*)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_queue__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
|
||||
{
|
||||
return ma_audio_queue_get_length_in_pcm_frames((ma_audio_queue*)pDataSource, pLength);
|
||||
}
|
||||
|
||||
static ma_data_source_vtable ma_gDataSourceVTable_AudioQueue =
|
||||
{
|
||||
ma_audio_queue__data_source_on_read,
|
||||
NULL, /* No seeking in audio queues. */
|
||||
ma_audio_queue__data_source_on_get_data_format,
|
||||
NULL, /* No notion of a cursor. */
|
||||
ma_audio_queue__data_source_on_get_length,
|
||||
NULL, /* onSetLooping */
|
||||
0
|
||||
};
|
||||
|
||||
MA_API ma_result ma_audio_queue_init(const ma_audio_queue_config* pConfig, ma_audio_queue* pAudioQueue)
|
||||
{
|
||||
ma_result result;
|
||||
ma_data_source_config dataSourceConfig;
|
||||
|
||||
if (pAudioQueue == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
MA_ZERO_OBJECT(pAudioQueue);
|
||||
|
||||
if (pConfig == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize the data source first. */
|
||||
dataSourceConfig = ma_data_source_config_init();
|
||||
dataSourceConfig.vtable = &ma_gDataSourceVTable_AudioQueue;
|
||||
|
||||
result = ma_data_source_init(&dataSourceConfig, &pAudioQueue->ds);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
pAudioQueue->format = pConfig->format;
|
||||
pAudioQueue->channels = pConfig->channels;
|
||||
pAudioQueue->sampleRate = pConfig->sampleRate;
|
||||
pAudioQueue->capacityInFrames = pConfig->sizeInFrames;
|
||||
ma_allocation_callbacks_init_copy(&pAudioQueue->allocationCallbacks, &pConfig->allocationCallbacks);
|
||||
|
||||
if (pAudioQueue->capacityInFrames > 0) {
|
||||
pAudioQueue->pBuffer = ma_malloc(pAudioQueue->capacityInFrames * ma_get_bytes_per_frame(pAudioQueue->format, pAudioQueue->channels), &pAudioQueue->allocationCallbacks);
|
||||
if (pAudioQueue->pBuffer == NULL) {
|
||||
return MA_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
pAudioQueue->isStatic = MA_TRUE;
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
MA_API void ma_audio_queue_uninit(ma_audio_queue* pAudioQueue)
|
||||
{
|
||||
if (pAudioQueue == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ma_free(pAudioQueue->pBuffer, &pAudioQueue->allocationCallbacks);
|
||||
}
|
||||
|
||||
static void ma_audio_queue_write_pcm_frames_nolock(ma_audio_queue* pAudioQueue, const void* pFrames, ma_uint64 frameCount, ma_uint32 bpf)
|
||||
{
|
||||
ma_uint64 framesWritten = 0;
|
||||
|
||||
while (framesWritten < frameCount) {
|
||||
ma_uint64 framesRemaining = (frameCount - framesWritten);
|
||||
ma_uint64 framesToWriteThisIteration = framesRemaining;
|
||||
ma_uint64 writeOffset;
|
||||
|
||||
writeOffset = pAudioQueue->cursorWrite % pAudioQueue->capacityInFrames;
|
||||
|
||||
if (framesToWriteThisIteration + writeOffset > pAudioQueue->capacityInFrames) {
|
||||
framesToWriteThisIteration = pAudioQueue->capacityInFrames - writeOffset;
|
||||
}
|
||||
|
||||
MA_COPY_MEMORY(ma_offset_ptr(pAudioQueue->pBuffer, writeOffset * bpf), ma_offset_ptr(pFrames, framesWritten * bpf), (size_t)(framesToWriteThisIteration * bpf));
|
||||
pAudioQueue->cursorWrite += framesToWriteThisIteration;
|
||||
framesWritten += framesToWriteThisIteration;
|
||||
}
|
||||
}
|
||||
|
||||
static void ma_audio_queue_read_pcm_frames_nolock(ma_audio_queue* pAudioQueue, void* pFrames, ma_uint64 frameCount, ma_uint32 bpf)
|
||||
{
|
||||
ma_uint64 framesRead = 0;
|
||||
|
||||
while (framesRead < frameCount) {
|
||||
ma_uint64 framesRemaining = (frameCount - framesRead);
|
||||
ma_uint64 framesToReadThisIteration = framesRemaining;
|
||||
ma_uint64 readOffset;
|
||||
|
||||
readOffset = pAudioQueue->cursorRead % pAudioQueue->capacityInFrames;
|
||||
|
||||
if (framesToReadThisIteration + readOffset > pAudioQueue->capacityInFrames) {
|
||||
framesToReadThisIteration = pAudioQueue->capacityInFrames - readOffset;
|
||||
}
|
||||
|
||||
MA_COPY_MEMORY(ma_offset_ptr(pFrames, framesRead * bpf), ma_offset_ptr(pAudioQueue->pBuffer, readOffset * bpf), (size_t)(framesToReadThisIteration * bpf));
|
||||
pAudioQueue->cursorRead += framesToReadThisIteration;
|
||||
framesRead += framesToReadThisIteration;
|
||||
}
|
||||
}
|
||||
|
||||
MA_API ma_result ma_audio_queue_push_pcm_frames(ma_audio_queue* pAudioQueue, const void* pFrames, ma_uint64 frameCount)
|
||||
{
|
||||
ma_result result;
|
||||
ma_uint64 length;
|
||||
ma_uint32 bpf;
|
||||
ma_uint32 newCapacity;
|
||||
void* pNewBuffer = NULL;
|
||||
void* pOldBuffer = NULL;
|
||||
|
||||
if (pAudioQueue == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
if (pFrames == NULL) {
|
||||
return MA_INVALID_ARGS; /* Push silence instead? */
|
||||
}
|
||||
|
||||
result = ma_audio_queue_get_length_in_pcm_frames(pAudioQueue, &length);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bpf = ma_get_bytes_per_frame(pAudioQueue->format, pAudioQueue->channels);
|
||||
|
||||
if (frameCount > (pAudioQueue->capacityInFrames - length)) {
|
||||
if (pAudioQueue->isStatic) {
|
||||
return MA_NO_SPACE;
|
||||
} else {
|
||||
newCapacity = pAudioQueue->capacityInFrames * 2;
|
||||
if (newCapacity < length + frameCount) {
|
||||
newCapacity = length + frameCount;
|
||||
}
|
||||
|
||||
pNewBuffer = ma_malloc(newCapacity * bpf, &pAudioQueue->allocationCallbacks);
|
||||
if (pNewBuffer == NULL) {
|
||||
return MA_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ma_spinlock_lock(&pAudioQueue->lock);
|
||||
{
|
||||
length = pAudioQueue->cursorWrite - pAudioQueue->cursorRead;
|
||||
|
||||
/* If we have a new buffer we need to swap out the old one with the new. */
|
||||
if (pNewBuffer != NULL) {
|
||||
pOldBuffer = pAudioQueue->pBuffer;
|
||||
|
||||
/* Fill the new buffer. */
|
||||
ma_audio_queue_read_pcm_frames_nolock(pAudioQueue, pNewBuffer, length, bpf);
|
||||
|
||||
/* Normalize our cursors. */
|
||||
pAudioQueue->cursorRead = 0;
|
||||
pAudioQueue->cursorWrite = length;
|
||||
|
||||
/* Swap out the buffer. */
|
||||
pAudioQueue->pBuffer = pNewBuffer;
|
||||
pAudioQueue->capacityInFrames = newCapacity;
|
||||
}
|
||||
|
||||
ma_audio_queue_write_pcm_frames_nolock(pAudioQueue, pFrames, frameCount, bpf);
|
||||
}
|
||||
ma_spinlock_unlock(&pAudioQueue->lock);
|
||||
|
||||
if (pOldBuffer != NULL) {
|
||||
ma_free(pOldBuffer, &pAudioQueue->allocationCallbacks);
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
MA_API ma_result ma_audio_queue_read_pcm_frames(ma_audio_queue* pAudioQueue, void* pFrames, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
||||
{
|
||||
ma_result result;
|
||||
ma_uint64 length;
|
||||
ma_uint32 bpf;
|
||||
|
||||
/* Clamp the frame count to the length. */
|
||||
result = ma_audio_queue_get_length_in_pcm_frames(pAudioQueue, &length);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (frameCount > length) {
|
||||
frameCount = length;
|
||||
}
|
||||
|
||||
bpf = ma_get_bytes_per_frame(pAudioQueue->format, pAudioQueue->channels);
|
||||
|
||||
ma_spinlock_lock(&pAudioQueue->lock);
|
||||
{
|
||||
ma_audio_queue_read_pcm_frames_nolock(pAudioQueue, pFrames, frameCount, bpf);
|
||||
}
|
||||
ma_spinlock_unlock(&pAudioQueue->lock);
|
||||
|
||||
*pFramesRead = frameCount;
|
||||
|
||||
/* Note that we never returning MA_AT_END for a queue because there isn't really a notion of it. */
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
MA_API ma_result ma_audio_queue_get_length_in_pcm_frames(ma_audio_queue* pAudioQueue, ma_uint64* pLength)
|
||||
{
|
||||
if (pLength == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
*pLength = 0;
|
||||
|
||||
if (pAudioQueue == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
ma_spinlock_lock(&pAudioQueue->lock);
|
||||
{
|
||||
*pLength = (pAudioQueue->cursorWrite - pAudioQueue->cursorRead);
|
||||
}
|
||||
ma_spinlock_unlock(&pAudioQueue->lock);
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**************************************************************************************************************************************************************
|
||||
|
||||
VFS
|
||||
|
||||
Reference in New Issue
Block a user