API CHANGE: Remove some functions relating to resampling.

The following functions are removed:

    ma_linear_resampler_get_required_input_frame_count()
    ma_linear_resampler_get_expected_output_frame_count()
    ma_resampler_get_required_input_frame_count()
    ma_resampler_get_expected_output_frame_count()
    ma_data_converter_get_required_input_frame_count()
    ma_data_converter_get_expected_output_frame_count()

These functions were used for calculating the required number of input
frames given an output capacity, and the number of expected number of
output frames given an input frame count. In practice these have proven
to be extremely annoying and finicky to get right. I myself have had
trouble keeping this working consistently as I make changes to the
processing function and I have zero confidence custom resampling
backends will implement this correctly.

If you need this functionality, take a copy of the resampler from
miniaudio 0.11.x and maintain that yourself.
This commit is contained in:
David Reid
2026-02-14 17:08:11 +10:00
parent fd1369b3fc
commit 6adcbf9034
+89 -323
View File
@@ -3182,11 +3182,6 @@ input buffer in which case it will be treated as an infinitely large
buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated
as seek. as seek.
Sometimes it's useful to know exactly how many input frames will be required to output a specific
number of frames. You can calculate this with `ma_data_converter_get_required_input_frame_count()`.
Likewise, it's sometimes useful to know exactly how many frames would be output given a certain
number of input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`.
Due to the nature of how resampling works, the data converter introduces some latency if resampling Due to the nature of how resampling works, the data converter introduces some latency if resampling
is required. This can be retrieved in terms of both the input rate and the output rate with is required. This can be retrieved in terms of both the input rate and the output rate with
`ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`. `ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`.
@@ -5674,11 +5669,9 @@ MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, m
MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut); MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut);
MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler); MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler);
MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler); MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler);
MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);
MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler); MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler);
/* Helper function for calculating the final frame count as if it were resampled by the linear resampler. This is a specialization of ma_linear_resampler_get_expected_output_frame_count(). */ /* Helper function for calculating the final frame count as if it were resampled by the linear resampler. */
MA_API ma_uint64 ma_linear_resampler_calculate_frame_count_after_resampling(ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_uint64 frameCountIn); MA_API ma_uint64 ma_linear_resampler_calculate_frame_count_after_resampling(ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_uint64 frameCountIn);
@@ -5691,11 +5684,9 @@ typedef struct
ma_result (* onInit )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend); ma_result (* onInit )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend);
void (* onUninit )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); void (* onUninit )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks);
ma_result (* onProcess )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); ma_result (* onProcess )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
ma_result (* onSetRate )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); /* Optional. Rate changes will be disabled. */ ma_result (* onSetRate )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); /* Optional. Rate changes will be disabled. */
ma_uint64 (* onGetInputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ ma_uint64 (* onGetInputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */
ma_uint64 (* onGetOutputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ ma_uint64 (* onGetOutputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */
ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); /* Optional. Latency mitigation will be disabled. */
ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* Optional. Latency mitigation will be disabled. */
ma_result (* onReset )(void* pUserData, ma_resampling_backend* pBackend); ma_result (* onReset )(void* pUserData, ma_resampling_backend* pBackend);
} ma_resampling_backend_vtable; } ma_resampling_backend_vtable;
@@ -5792,25 +5783,6 @@ Retrieves the latency introduced by the resampler in output frames.
*/ */
MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler); MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler);
/*
Calculates the number of whole input frames that would need to be read from the client in order to output the specified
number of output frames.
The returned value does not include cached input frames. It only returns the number of extra frames that would need to be
read from the input buffer in order to output the specified number of output frames.
This is not implemented by all resampling backends and you should not assume it is generally available.
*/
MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);
/*
Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of
input frames.
This is not implemented by all resampling backends and you should not assume it is generally available.
*/
MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
/* /*
Resets the resampler's timer and clears its internal cache. Resets the resampler's timer and clears its internal cache.
*/ */
@@ -5953,8 +5925,6 @@ MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_ui
MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut); MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut);
MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter); MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter);
MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter); MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter);
MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);
MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);
MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);
MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter); MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter);
@@ -20936,8 +20906,8 @@ static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 fra
/* /*
We run slightly different logic depending on whether or not we're using a heap-allocated We run slightly different logic depending on whether or not we're using a heap-allocated
buffer for caching input data. This will be the case if the data converter does not have buffer for caching input data. This will be the case if the data converter is doing resampling
the ability to retrieve the required input frame count for a given output frame count. because in that case the number of input frames required is not known.
*/ */
if (pDevice->playback.pInputCache != NULL) { if (pDevice->playback.pInputCache != NULL) {
while (totalFramesReadOut < frameCount) { while (totalFramesReadOut < frameCount) {
@@ -20984,7 +20954,6 @@ static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 fra
ma_uint64 framesReadThisIterationIn; ma_uint64 framesReadThisIterationIn;
ma_uint64 framesToReadThisIterationOut; ma_uint64 framesToReadThisIterationOut;
ma_uint64 framesReadThisIterationOut; ma_uint64 framesReadThisIterationOut;
ma_uint64 requiredInputFrameCount;
framesToReadThisIterationOut = (frameCount - totalFramesReadOut); framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
framesToReadThisIterationIn = framesToReadThisIterationOut; framesToReadThisIterationIn = framesToReadThisIterationOut;
@@ -20992,11 +20961,6 @@ static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 fra
framesToReadThisIterationIn = intermediaryBufferCap; framesToReadThisIterationIn = intermediaryBufferCap;
} }
ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut, &requiredInputFrameCount);
if (framesToReadThisIterationIn > requiredInputFrameCount) {
framesToReadThisIterationIn = requiredInputFrameCount;
}
ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn); ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn);
/* /*
@@ -47616,29 +47580,22 @@ MA_API ma_result ma_device_update_descriptor(ma_device* pDevice, ma_device_type
The first is a duplex device for backends that use a callback for data delivery. The reason The first is a duplex device for backends that use a callback for data delivery. The reason
this is needed is that the input stage needs to have a buffer to place the input data while it this is needed is that the input stage needs to have a buffer to place the input data while it
waits for the playback stage, after which the miniaudio data callback will get fired. This is waits for the playback stage, after which the miniaudio data callback will get fired.
not needed for backends that use a blocking API because miniaudio manages temporary buffers on
the stack to achieve this.
The other situation is when the data converter does not have the ability to query the number The other situation is when resampling is required. This is because when resampling, a different
of input frames that are required in order to process a given number of output frames. When number of input frames will be consumed to the number that is produced. When it is not exactly
performing data conversion, it's useful if miniaudio know exactly how many frames it needs equal we'll need to cache any residual.
from the client in order to generate a given number of output frames. This way, only exactly
the number of frames are needed to be read from the client which means no cache is necessary.
On the other hand, if miniaudio doesn't know how many frames to read, it is forced to read
in fixed sized chunks and then cache any residual unused input frames, those of which will be
processed at a later stage.
*/ */
if (deviceType == ma_device_type_playback) { if (deviceType == ma_device_type_playback) {
ma_uint64 unused; ma_bool32 isResampling;
pDevice->playback.inputCacheConsumed = 0; pDevice->playback.inputCacheConsumed = 0;
pDevice->playback.inputCacheRemaining = 0; pDevice->playback.inputCacheRemaining = 0;
if (pDevice->type == ma_device_type_duplex || /* Duplex. backend may decide to use ma_device_handle_backend_data_callback() which will require this cache. */ isResampling = publicDescriptor.sampleRate != internalDescriptor.sampleRate;
ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) /* Data conversion required input frame calculation not supported. */
{ if (pDevice->type == ma_device_type_duplex || isResampling) {
/* We need a heap allocated cache. We want to size this based on the period size. */ /* We need a heap allocated cache. We want to size this based on the period size. The size need not be exact, but it's best to keep it approximately correct. */
void* pNewInputCache; void* pNewInputCache;
ma_uint64 newInputCacheCap; ma_uint64 newInputCacheCap;
ma_uint64 newInputCacheSizeInBytes; ma_uint64 newInputCacheSizeInBytes;
@@ -61400,7 +61357,7 @@ MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler
return 0; return 0;
} }
return 1 + pResampler->lpfOrder; /*pResampler->lpfCount*2;*/ return 1 + pResampler->lpfOrder;
} }
MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler) MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler)
@@ -61412,111 +61369,6 @@ MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resample
return ma_linear_resampler_get_input_latency(pResampler) * pResampler->sampleRateOut / pResampler->sampleRateIn; return ma_linear_resampler_get_input_latency(pResampler) * pResampler->sampleRateOut / pResampler->sampleRateIn;
} }
MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
{
#if 1
ma_uint64 inputFrameCount;
if (pInputFrameCount == NULL) {
return MA_INVALID_ARGS;
}
*pInputFrameCount = 0;
if (pResampler == NULL) {
return MA_INVALID_ARGS;
}
if (outputFrameCount == 0) {
return MA_SUCCESS;
}
/* Any whole input frames are consumed before the first output frame is generated. */
inputFrameCount = pResampler->inTimeInt;
outputFrameCount -= 1;
/* The rest of the output frames can be calculated in constant time. */
inputFrameCount += outputFrameCount * pResampler->inAdvanceInt;
inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->sampleRateOut;
*pInputFrameCount = inputFrameCount;
return MA_SUCCESS;
#else
{
(void)pResampler;
(void)outputFrameCount;
(void)pInputFrameCount;
return MA_NOT_IMPLEMENTED;
}
#endif
}
static ma_result ma_linear_resampler_get_expected_output_frame_count_ex(ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_uint32 inTimeInt, ma_uint32 inTimeFrac, ma_uint32 inAdvanceInt, ma_uint32 inAdvanceFrac, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
{
ma_uint64 outputFrameCount;
ma_uint64 preliminaryInputFrameCountFromFrac;
ma_uint64 preliminaryInputFrameCount;
if (pOutputFrameCount == NULL) {
return MA_INVALID_ARGS;
}
*pOutputFrameCount = 0;
/*
The first step is to get a preliminary output frame count. This will either be exactly equal to what we need, or less by 1. We need to
determine how many input frames will be consumed by this value. If it's greater than our original input frame count it means we won't
be able to generate an extra frame because we will have run out of input data. Otherwise we will have enough input for the generation
of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames.
*/
outputFrameCount = (inputFrameCount * sampleRateOut) / sampleRateIn;
/*
We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is
used in the logic below to determine whether or not we need to add an extra output frame.
*/
preliminaryInputFrameCountFromFrac = (inTimeFrac + outputFrameCount*inAdvanceFrac) / sampleRateOut;
preliminaryInputFrameCount = (inTimeInt + outputFrameCount*inAdvanceInt ) + preliminaryInputFrameCountFromFrac;
/*
If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greater than
the amount of whole input frames we have available as input we need to *not* add an extra output frame as there won't be enough data
to actually process. Otherwise we need to add the extra output frame.
*/
if (preliminaryInputFrameCount <= inputFrameCount) {
outputFrameCount += 1;
}
*pOutputFrameCount = outputFrameCount;
return MA_SUCCESS;
}
MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
{
#if 1
if (pOutputFrameCount == NULL) {
return MA_INVALID_ARGS;
}
*pOutputFrameCount = 0;
if (pResampler == NULL) {
return MA_INVALID_ARGS;
}
return ma_linear_resampler_get_expected_output_frame_count_ex(pResampler->sampleRateIn, pResampler->sampleRateOut, pResampler->inTimeInt, pResampler->inTimeFrac, pResampler->inAdvanceInt, pResampler->inAdvanceFrac, inputFrameCount, pOutputFrameCount);
#else
{
(void)pResampler;
(void)inputFrameCount;
(void)pOutputFrameCount;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler) MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler)
{ {
ma_uint32 iChannel; ma_uint32 iChannel;
@@ -61557,6 +61409,48 @@ MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler)
return MA_SUCCESS; return MA_SUCCESS;
} }
static ma_result ma_linear_resampler_get_expected_output_frame_count_ex(ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_uint32 inTimeInt, ma_uint32 inTimeFrac, ma_uint32 inAdvanceInt, ma_uint32 inAdvanceFrac, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
{
ma_uint64 outputFrameCount;
ma_uint64 preliminaryInputFrameCountFromFrac;
ma_uint64 preliminaryInputFrameCount;
if (pOutputFrameCount == NULL) {
return MA_INVALID_ARGS;
}
*pOutputFrameCount = 0;
/*
The first step is to get a preliminary output frame count. This will either be exactly equal to what we need, or less by 1. We need to
determine how many input frames will be consumed by this value. If it's greater than our original input frame count it means we won't
be able to generate an extra frame because we will have run out of input data. Otherwise we will have enough input for the generation
of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames.
*/
outputFrameCount = (inputFrameCount * sampleRateOut) / sampleRateIn;
/*
We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is
used in the logic below to determine whether or not we need to add an extra output frame.
*/
preliminaryInputFrameCountFromFrac = (inTimeFrac + outputFrameCount*inAdvanceFrac) / sampleRateOut;
preliminaryInputFrameCount = (inTimeInt + outputFrameCount*inAdvanceInt ) + preliminaryInputFrameCountFromFrac;
/*
If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greater than
the amount of whole input frames we have available as input we need to *not* add an extra output frame as there won't be enough data
to actually process. Otherwise we need to add the extra output frame.
*/
if (preliminaryInputFrameCount <= inputFrameCount) {
outputFrameCount += 1;
}
*pOutputFrameCount = outputFrameCount;
return MA_SUCCESS;
}
MA_API ma_uint64 ma_linear_resampler_calculate_frame_count_after_resampling(ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_uint64 frameCountIn) MA_API ma_uint64 ma_linear_resampler_calculate_frame_count_after_resampling(ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_uint64 frameCountIn)
{ {
ma_result result; ma_result result;
@@ -61658,20 +61552,6 @@ static ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserDat
return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend); return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend);
} }
static ma_result ma_resampling_backend_get_required_input_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
{
(void)pUserData;
return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount);
}
static ma_result ma_resampling_backend_get_expected_output_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
{
(void)pUserData;
return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount);
}
static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend) static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend)
{ {
(void)pUserData; (void)pUserData;
@@ -61688,8 +61568,6 @@ static ma_resampling_backend_vtable g_ma_linear_resampler_vtable =
ma_resampling_backend_set_rate__linear, ma_resampling_backend_set_rate__linear,
ma_resampling_backend_get_input_latency__linear, ma_resampling_backend_get_input_latency__linear,
ma_resampling_backend_get_output_latency__linear, ma_resampling_backend_get_output_latency__linear,
ma_resampling_backend_get_required_input_frame_count__linear,
ma_resampling_backend_get_expected_output_frame_count__linear,
ma_resampling_backend_reset__linear ma_resampling_backend_reset__linear
}; };
@@ -61954,44 +61832,6 @@ MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler)
return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend); return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend);
} }
MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
{
if (pInputFrameCount == NULL) {
return MA_INVALID_ARGS;
}
*pInputFrameCount = 0;
if (pResampler == NULL) {
return MA_INVALID_ARGS;
}
if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) {
return MA_NOT_IMPLEMENTED;
}
return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount);
}
MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
{
if (pOutputFrameCount == NULL) {
return MA_INVALID_ARGS;
}
*pOutputFrameCount = 0;
if (pResampler == NULL) {
return MA_INVALID_ARGS;
}
if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) {
return MA_NOT_IMPLEMENTED;
}
return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount);
}
MA_API ma_result ma_resampler_reset(ma_resampler* pResampler) MA_API ma_result ma_resampler_reset(ma_resampler* pResampler)
{ {
if (pResampler == NULL) { if (pResampler == NULL) {
@@ -64400,20 +64240,12 @@ static ma_result ma_data_converter_process_pcm_frames__resample_first(ma_data_co
/* We need to ensure we don't try to process too many input frames that we run out of room in the output buffer. If this happens we'll end up glitching. */ /* We need to ensure we don't try to process too many input frames that we run out of room in the output buffer. If this happens we'll end up glitching. */
/* /*
We need to try to predict how many input frames will be required for the resampler. If the We need to try to predict how many input frames will be required for the resampler. The further
resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further
off we are from this, the more wasted format conversions we'll end up doing. off we are from this, the more wasted format conversions we'll end up doing.
*/ */
#if 1 #if 1
{ {
ma_uint64 requiredInputFrameCount; ma_uint64 requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;
result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount);
if (result != MA_SUCCESS) {
/* Fall back to a best guess. */
requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;
}
if (frameCountInThisIteration > requiredInputFrameCount) { if (frameCountInThisIteration > requiredInputFrameCount) {
frameCountInThisIteration = requiredInputFrameCount; frameCountInThisIteration = requiredInputFrameCount;
} }
@@ -64568,14 +64400,7 @@ static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_co
#if 1 #if 1
{ {
ma_uint64 requiredInputFrameCount; ma_uint64 requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;
result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount);
if (result != MA_SUCCESS) {
/* Fall back to a best guess. */
requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;
}
if (frameCountInThisIteration > requiredInputFrameCount) { if (frameCountInThisIteration > requiredInputFrameCount) {
frameCountInThisIteration = requiredInputFrameCount; frameCountInThisIteration = requiredInputFrameCount;
} }
@@ -64715,46 +64540,6 @@ MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* p
return 0; /* No latency without a resampler. */ return 0; /* No latency without a resampler. */
} }
MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
{
if (pInputFrameCount == NULL) {
return MA_INVALID_ARGS;
}
*pInputFrameCount = 0;
if (pConverter == NULL) {
return MA_INVALID_ARGS;
}
if (pConverter->hasResampler) {
return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount);
} else {
*pInputFrameCount = outputFrameCount; /* 1:1 */
return MA_SUCCESS;
}
}
MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
{
if (pOutputFrameCount == NULL) {
return MA_INVALID_ARGS;
}
*pOutputFrameCount = 0;
if (pConverter == NULL) {
return MA_INVALID_ARGS;
}
if (pConverter->hasResampler) {
return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount);
} else {
*pOutputFrameCount = inputFrameCount; /* 1:1 */
return MA_SUCCESS;
}
}
MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
{ {
if (pConverter == NULL || pChannelMap == NULL) { if (pConverter == NULL || pChannelMap == NULL) {
@@ -65792,23 +65577,22 @@ MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const
} }
if (pOut == NULL) { if (pOut == NULL) {
result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut); if (converter.sampleRateIn == converter.sampleRateOut) {
if (result != MA_SUCCESS) { frameCountOut = frameCountIn;
if (result == MA_NOT_IMPLEMENTED) { } else {
/* No way to calculate the number of frames, so we'll need to brute force it and loop. */ /* No way to calculate the number of frames, so we'll need to brute force it and loop. */
frameCountOut = 0; frameCountOut = 0;
while (frameCountIn > 0) { while (frameCountIn > 0) {
ma_uint64 framesProcessedIn = frameCountIn; ma_uint64 framesProcessedIn = frameCountIn;
ma_uint64 framesProcessedOut = 0xFFFFFFFF; ma_uint64 framesProcessedOut = 0xFFFFFFFF;
result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut); result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
break; break;
}
frameCountIn -= framesProcessedIn;
} }
frameCountIn -= framesProcessedIn;
} }
} }
} else { } else {
@@ -73593,34 +73377,24 @@ static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_
/* /*
Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll
need this if the data converter does not support calculation of the required input frame count. To need this if the data converter is performing resampling.
determine support for this we'll just run a test.
*/ */
{ if (pDecoder->converter.sampleRateIn != pDecoder->converter.sampleRateOut) {
ma_uint64 unused; ma_uint64 inputCacheCapSizeInBytes;
result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused); pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels);
if (result != MA_SUCCESS) {
/*
We were unable to calculate the required input frame count which means we'll need to use
a heap-allocated cache.
*/
ma_uint64 inputCacheCapSizeInBytes;
pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels); /* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */
inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels);
if (inputCacheCapSizeInBytes > MA_SIZE_MAX) {
ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks);
return MA_OUT_OF_MEMORY;
}
/* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */ pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks); /* Safe cast to size_t. */
inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels); if (pDecoder->pInputCache == NULL) {
if (inputCacheCapSizeInBytes > MA_SIZE_MAX) { ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks);
ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); return MA_OUT_OF_MEMORY;
return MA_OUT_OF_MEMORY;
}
pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks); /* Safe cast to size_t. */
if (pDecoder->pInputCache == NULL) {
ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks);
return MA_OUT_OF_MEMORY;
}
} }
} }
@@ -73629,8 +73403,6 @@ static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_
static ma_result ma_decoder_on_read(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) static ma_result ma_decoder_on_read(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
{ {
ma_decoder* pDecoder = (ma_decoder*)pUserData; ma_decoder* pDecoder = (ma_decoder*)pUserData;
@@ -74568,7 +74340,6 @@ MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO
ma_uint64 framesReadThisIterationIn; ma_uint64 framesReadThisIterationIn;
ma_uint64 framesToReadThisIterationOut; ma_uint64 framesToReadThisIterationOut;
ma_uint64 framesReadThisIterationOut; ma_uint64 framesReadThisIterationOut;
ma_uint64 requiredInputFrameCount;
framesToReadThisIterationOut = (frameCount - totalFramesReadOut); framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
framesToReadThisIterationIn = framesToReadThisIterationOut; framesToReadThisIterationIn = framesToReadThisIterationOut;
@@ -74576,12 +74347,7 @@ MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO
framesToReadThisIterationIn = intermediaryBufferCap; framesToReadThisIterationIn = intermediaryBufferCap;
} }
ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount); if (framesToReadThisIterationIn > 0) {
if (framesToReadThisIterationIn > requiredInputFrameCount) {
framesToReadThisIterationIn = requiredInputFrameCount;
}
if (requiredInputFrameCount > 0) {
result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn); result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn);
/* /*