mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-21 15:56:58 +02:00
Make ma_sound node processing a bit more robust.
This removes the dependency on querying the required input frame count from the resampler. This should in turn enable future work to support custom resamplers. Public issue https://github.com/mackron/miniaudio/issues/965
This commit is contained in:
+53
-73
@@ -11239,7 +11239,7 @@ struct ma_sound
|
||||
MA_ATOMIC(4, ma_bool32) atEnd;
|
||||
ma_sound_end_proc endCallback;
|
||||
void* pEndCallbackUserData;
|
||||
void* pProcessingCache; /* Will be null if pDataSource is null. */
|
||||
float* pProcessingCache; /* Will be null if pDataSource is null. */
|
||||
ma_uint32 processingCacheFramesRemaining;
|
||||
ma_uint32 processingCacheCap;
|
||||
ma_bool8 ownsDataSource;
|
||||
@@ -76597,22 +76597,6 @@ static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node*
|
||||
return !ma_atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, ma_atomic_memory_order_acquire);
|
||||
}
|
||||
|
||||
static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount)
|
||||
{
|
||||
ma_uint64 inputFrameCount = 0;
|
||||
|
||||
if (ma_engine_node_is_pitching_enabled(pEngineNode)) {
|
||||
ma_result result = ma_linear_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount, &inputFrameCount);
|
||||
if (result != MA_SUCCESS) {
|
||||
inputFrameCount = 0;
|
||||
}
|
||||
} else {
|
||||
inputFrameCount = outputFrameCount; /* No resampling, so 1:1. */
|
||||
}
|
||||
|
||||
return inputFrameCount;
|
||||
}
|
||||
|
||||
static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float volume)
|
||||
{
|
||||
if (pEngineNode == NULL) {
|
||||
@@ -76920,55 +76904,70 @@ static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float
|
||||
/* Keep reading until we've read as much as was requested or we reach the end of the data source. */
|
||||
while (totalFramesRead < frameCount) {
|
||||
ma_uint32 framesRemaining = frameCount - totalFramesRead;
|
||||
ma_uint32 framesToRead;
|
||||
ma_uint64 framesJustRead;
|
||||
ma_uint32 frameCountIn;
|
||||
ma_uint32 frameCountOut;
|
||||
const float* pRunningFramesIn;
|
||||
float* pRunningFramesOut;
|
||||
|
||||
/*
|
||||
The first thing we need to do is read into the temporary buffer. We can calculate exactly
|
||||
how many input frames we'll need after resampling.
|
||||
*/
|
||||
framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining);
|
||||
if (framesToRead > tempCapInFrames) {
|
||||
framesToRead = tempCapInFrames;
|
||||
}
|
||||
/* If there's any input frames sitting in the cache get those processed first. */
|
||||
if (pSound->processingCacheFramesRemaining > 0) {
|
||||
pRunningFramesIn = pSound->pProcessingCache;
|
||||
frameCountIn = pSound->processingCacheFramesRemaining;
|
||||
|
||||
result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead);
|
||||
pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_node_get_output_channels(pNode, 0));
|
||||
frameCountOut = framesRemaining;
|
||||
|
||||
/* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */
|
||||
if (result == MA_AT_END) {
|
||||
ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */
|
||||
}
|
||||
|
||||
pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_node_get_output_channels(pNode, 0));
|
||||
|
||||
frameCountIn = (ma_uint32)framesJustRead;
|
||||
frameCountOut = framesRemaining;
|
||||
|
||||
/* Convert if necessary. */
|
||||
if (dataSourceFormat == ma_format_f32) {
|
||||
/* Fast path. No data conversion necessary. */
|
||||
pRunningFramesIn = (float*)temp;
|
||||
ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);
|
||||
|
||||
MA_ASSERT(frameCountIn <= pSound->processingCacheFramesRemaining);
|
||||
pSound->processingCacheFramesRemaining -= frameCountIn;
|
||||
|
||||
/* Move any remaining data in the cache down. */
|
||||
if (pSound->processingCacheFramesRemaining > 0) {
|
||||
MA_MOVE_MEMORY(pSound->pProcessingCache, ma_offset_pcm_frames_ptr_f32(pSound->pProcessingCache, frameCountIn, dataSourceChannels), pSound->processingCacheFramesRemaining * ma_get_bytes_per_frame(ma_format_f32, dataSourceChannels));
|
||||
}
|
||||
|
||||
totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */
|
||||
|
||||
if (result != MA_SUCCESS || ma_sound_at_end(pSound)) {
|
||||
break; /* Might have reached the end. */
|
||||
}
|
||||
} else {
|
||||
/* Slow path. Need to do sample format conversion to f32. If we give the f32 buffer the same count as the first temp buffer, we're guaranteed it'll be large enough. */
|
||||
float tempf32[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* Do not do `MA_DATA_CONVERTER_STACK_BUFFER_SIZE/sizeof(float)` here like we've done in other places. */
|
||||
ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none);
|
||||
/* Getting here means there's nothing in the cache. Read more data from the data source. */
|
||||
if (dataSourceFormat == ma_format_f32) {
|
||||
/* Fast path. No conversion to f32 necessary. */
|
||||
result = ma_data_source_read_pcm_frames(pSound->pDataSource, pSound->pProcessingCache, pSound->processingCacheCap, &framesJustRead);
|
||||
} else {
|
||||
/* Slow path. Need to convert to f32. */
|
||||
ma_uint64 totalFramesConverted = 0;
|
||||
|
||||
/* Now that we have our samples in f32 format we can process like normal. */
|
||||
pRunningFramesIn = tempf32;
|
||||
ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);
|
||||
}
|
||||
while (totalFramesConverted < pSound->processingCacheCap) {
|
||||
ma_uint64 framesConverted;
|
||||
ma_uint32 framesToConvertThisIteration = pSound->processingCacheCap - framesConverted;
|
||||
if (framesToConvertThisIteration > tempCapInFrames) {
|
||||
framesToConvertThisIteration = tempCapInFrames;
|
||||
}
|
||||
|
||||
/* We should have processed all of our input frames since we calculated the required number of input frames at the top. */
|
||||
MA_ASSERT(frameCountIn == framesJustRead);
|
||||
totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */
|
||||
result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToConvertThisIteration, &framesConverted);
|
||||
if (result != MA_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (result != MA_SUCCESS || ma_sound_at_end(pSound)) {
|
||||
break; /* Might have reached the end. */
|
||||
ma_convert_pcm_frames_format(ma_offset_pcm_frames_ptr_f32(pSound->pProcessingCache, totalFramesConverted, dataSourceChannels), ma_format_f32, temp, dataSourceFormat, framesConverted, dataSourceChannels, ma_dither_mode_none);
|
||||
totalFramesConverted += framesConverted;
|
||||
}
|
||||
|
||||
framesJustRead = totalFramesConverted;
|
||||
}
|
||||
|
||||
/* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */
|
||||
if (result == MA_AT_END) {
|
||||
ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */
|
||||
}
|
||||
|
||||
MA_ASSERT(framesJustRead <= pSound->processingCacheCap);
|
||||
pSound->processingCacheFramesRemaining = (ma_uint32)framesJustRead;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76991,25 +76990,6 @@ static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float
|
||||
ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
|
||||
}
|
||||
|
||||
static ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount)
|
||||
{
|
||||
ma_uint64 inputFrameCount;
|
||||
|
||||
MA_ASSERT(pInputFrameCount != NULL);
|
||||
|
||||
/* Our pitch will affect this calculation. We need to update it. */
|
||||
ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
|
||||
|
||||
inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount);
|
||||
if (inputFrameCount > 0xFFFFFFFF) {
|
||||
inputFrameCount = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */
|
||||
}
|
||||
|
||||
*pInputFrameCount = (ma_uint32)inputFrameCount;
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static ma_node_vtable g_ma_engine_node_vtable__sound =
|
||||
{
|
||||
@@ -77023,7 +77003,7 @@ static ma_node_vtable g_ma_engine_node_vtable__sound =
|
||||
static ma_node_vtable g_ma_engine_node_vtable__group =
|
||||
{
|
||||
ma_engine_node_process_pcm_frames__group,
|
||||
ma_engine_node_get_required_input_frame_count__group,
|
||||
NULL, /* onGetRequiredInputFrameCount */
|
||||
1, /* Groups have one input bus. */
|
||||
1, /* Groups have one output bus. */
|
||||
MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */
|
||||
|
||||
Reference in New Issue
Block a user