Resampler: Remove dependency on ma_lpf.

This makes the resampler a bit more self-contained and allows us to do
some resampler-specific optimizations to the filtering process. It also
reduces the size of the `ma_linear_resampler` struct.
This commit is contained in:
David Reid
2026-02-13 18:50:42 +10:00
parent cbbe317adc
commit db1bc8c4b7
+21 -65
View File
@@ -5636,26 +5636,20 @@ typedef struct
float* f32; float* f32;
ma_int16* s16; ma_int16* s16;
} x1; /* The next input frame. */ } x1; /* The next input frame. */
ma_lpf lpf;
/* /*
We have some heap allocated data for the sample cache and the LPF state. This is an array of
either floats of int32s depending on whether or not `format` is f32 or s16. Below is the
structure:
| Cached Samples | `channels` |
| LPF State | (`lpfOrder` / 2) * (4 + (`channels` * 2)) |
The low-pass filter is achieved with a series of 2nd-order biquads. This means there is one The low-pass filter is achieved with a series of 2nd-order biquads. This means there is one
LPF state for each `lpfOrder`, divided by two. So if `lpfOrder` is 4, there will be 2 LPF LPF state for each `lpfOrder`, divided by two. So if `lpfOrder` is 4, there will be 2 LPF
states in the array. The structure of each LPF state is as follows: states in the array. The structure of each LPF state is as follows:
+-------------------+------------+
| Biquad b1 | 1 | | Biquad b1 | 1 |
| Biquad b2 | 1 | | Biquad b2 | 1 |
| Biquad a1 | 1 | | Biquad a1 | 1 |
| Biquad a2 | 1 | | Biquad a2 | 1 |
| Biquad register 1 | `channels` | | Biquad register 1 | `channels` |
| Biquad register 2 | `channels` | | Biquad register 2 | `channels` |
+-------------------+------------+
If you are familiar with biquads, you'll note that b0 and a0 are missing. This is because b0 If you are familiar with biquads, you'll note that b0 and a0 are missing. This is because b0
is set the same value as b2, so we just reuse b2, and a0 is just not used. is set the same value as b2, so we just reuse b2, and a0 is just not used.
@@ -5664,7 +5658,7 @@ typedef struct
{ {
float* f32; float* f32;
ma_int32* s32; ma_int32* s32;
} heap; } lpf;
/* Memory management. */ /* Memory management. */
void* _pHeap; void* _pHeap;
@@ -58957,8 +58951,6 @@ typedef struct
size_t x0Offset; size_t x0Offset;
size_t x1Offset; size_t x1Offset;
size_t lpfOffset; size_t lpfOffset;
size_t cachedSamplesOffset;
size_t lpfStateOffset;
} ma_linear_resampler_heap_layout; } ma_linear_resampler_heap_layout;
@@ -58981,16 +58973,14 @@ static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* p
} }
/* A cache of samples unrelated to the LPF comes first and needs to be skipped. */ /* A cache of samples unrelated to the LPF comes first and needs to be skipped. */
#define MA_RESAMPLER_GET_LPF_HEAP_F32(pResampler, lpfIndex) pResampler->heap.f32 + pResampler->channels + (lpfIndex * (4 + (pResampler->channels*2))) #define MA_RESAMPLER_GET_LPF_HEAP_F32(pResampler, lpfIndex) pResampler->lpf.f32 + (lpfIndex * (4 + (pResampler->channels*2)))
#define MA_RESAMPLER_GET_LPF_HEAP_S32(pResampler, lpfIndex) pResampler->heap.s32 + pResampler->channels + (lpfIndex * (4 + (pResampler->channels*2))) #define MA_RESAMPLER_GET_LPF_HEAP_S32(pResampler, lpfIndex) pResampler->lpf.s32 + (lpfIndex * (4 + (pResampler->channels*2)))
static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, void* pHeap, ma_linear_resampler_heap_layout* pHeapLayout, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized) static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized)
{ {
ma_result result;
ma_uint32 gcf; ma_uint32 gcf;
ma_uint32 lpfSampleRate; ma_uint32 lpfSampleRate;
double lpfCutoffFrequency; double lpfCutoffFrequency;
ma_lpf_config lpfConfig;
ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */ ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */
ma_uint32 minSampleRate; ma_uint32 minSampleRate;
ma_uint32 maxSampleRate; ma_uint32 maxSampleRate;
@@ -59085,25 +59075,6 @@ static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pRes
} }
} }
/* Old LPF. Will be removed later. */
lpfConfig = ma_lpf_config_init(pResampler->format, pResampler->channels, lpfSampleRate, lpfCutoffFrequency, pResampler->lpfOrder);
/*
If the resampler is already initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames
getting cleared. Instead we re-initialize the filter which will maintain any cached frames.
*/
if (isResamplerAlreadyInitialized) {
result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf);
} else {
result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf);
}
if (result != MA_SUCCESS) {
return result;
}
pResampler->inAdvanceInt = pResampler->sampleRateIn / pResampler->sampleRateOut; pResampler->inAdvanceInt = pResampler->sampleRateIn / pResampler->sampleRateOut;
pResampler->inAdvanceFrac = pResampler->sampleRateIn % pResampler->sampleRateOut; pResampler->inAdvanceFrac = pResampler->sampleRateIn % pResampler->sampleRateOut;
@@ -59159,30 +59130,8 @@ static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_c
pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
/* LPF */ /* LPF. */
pHeapLayout->lpfOffset = pHeapLayout->sizeInBytes; pHeapLayout->lpfOffset = pHeapLayout->sizeInBytes;
{
ma_result result;
size_t lpfHeapSizeInBytes;
ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, lpfOrder); /* Sample rate and cutoff frequency do not matter. */
result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes);
if (result != MA_SUCCESS) {
return result;
}
pHeapLayout->sizeInBytes += lpfHeapSizeInBytes;
}
/* Cached samples. These are always stored as either f32 or s32, so either way it's 4 bytes per sample, even when the format is s16. */
pHeapLayout->cachedSamplesOffset = pHeapLayout->sizeInBytes;
{
pHeapLayout->sizeInBytes += sizeof(ma_int32) * pConfig->channels;
}
/* LPF state. */
pHeapLayout->lpfStateOffset = pHeapLayout->sizeInBytes;
{ {
pHeapLayout->sizeInBytes += sizeof(ma_int32) * ((lpfOrder / 2) * (4 + (pConfig->channels * 2))); pHeapLayout->sizeInBytes += sizeof(ma_int32) * ((lpfOrder / 2) * (4 + (pConfig->channels * 2)));
} }
@@ -59259,10 +59208,10 @@ MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler
pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset); pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset);
} }
pResampler->heap.s32 = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.cachedSamplesOffset); pResampler->lpf.s32 = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.lpfOffset);
/* Setting the rate will set up the filter and time advances for us. */ /* Setting the rate will set up the filter and time advances for us. */
result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE); result = ma_linear_resampler_set_rate_internal(pResampler, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
} }
@@ -59315,8 +59264,6 @@ MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma
return; return;
} }
ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks);
if (pResampler->_ownsHeap) { if (pResampler->_ownsHeap) {
ma_free(pResampler->_pHeap, pAllocationCallbacks); ma_free(pResampler->_pHeap, pAllocationCallbacks);
} }
@@ -61273,7 +61220,7 @@ MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pRe
MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
{ {
return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE); return ma_linear_resampler_set_rate_internal(pResampler, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE);
} }
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)
@@ -61307,7 +61254,7 @@ MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler
return 0; return 0;
} }
return 1 + ma_lpf_get_latency(&pResampler->lpf); return 1 + pResampler->lpfOrder; /*pResampler->lpfCount*2;*/
} }
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)
@@ -61427,6 +61374,7 @@ MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_li
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;
ma_uint32 iLPF;
if (pResampler == NULL) { if (pResampler == NULL) {
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
@@ -61450,7 +61398,15 @@ MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler)
} }
/* The low pass filter needs to have its cache reset. */ /* The low pass filter needs to have its cache reset. */
ma_lpf_clear_cache(&pResampler->lpf); for (iLPF = 0; iLPF < pResampler->lpfOrder/2; iLPF += 1) {
if (pResampler->format == ma_format_f32) {
float* pLPF = MA_RESAMPLER_GET_LPF_HEAP_F32(pResampler, iLPF);
MA_ZERO_MEMORY(pLPF + 4, sizeof(float) * pResampler->channels * 2);
} else {
ma_int32* pLPF = MA_RESAMPLER_GET_LPF_HEAP_S32(pResampler, iLPF);
MA_ZERO_MEMORY(pLPF + 4, sizeof(ma_int32) * pResampler->channels * 2);
}
}
return MA_SUCCESS; return MA_SUCCESS;
} }