mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
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:
+21
-65
@@ -5636,26 +5636,20 @@ typedef struct
|
||||
float* f32;
|
||||
ma_int16* s16;
|
||||
} 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
|
||||
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:
|
||||
|
||||
+-------------------+------------+
|
||||
| Biquad b1 | 1 |
|
||||
| Biquad b2 | 1 |
|
||||
| Biquad a1 | 1 |
|
||||
| Biquad a2 | 1 |
|
||||
| Biquad register 1 | `channels` |
|
||||
| Biquad register 2 | `channels` |
|
||||
+-------------------+------------+
|
||||
|
||||
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.
|
||||
@@ -5664,7 +5658,7 @@ typedef struct
|
||||
{
|
||||
float* f32;
|
||||
ma_int32* s32;
|
||||
} heap;
|
||||
} lpf;
|
||||
|
||||
/* Memory management. */
|
||||
void* _pHeap;
|
||||
@@ -58957,8 +58951,6 @@ typedef struct
|
||||
size_t x0Offset;
|
||||
size_t x1Offset;
|
||||
size_t lpfOffset;
|
||||
size_t cachedSamplesOffset;
|
||||
size_t lpfStateOffset;
|
||||
} 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. */
|
||||
#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_S32(pResampler, lpfIndex) pResampler->heap.s32 + 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->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 lpfSampleRate;
|
||||
double lpfCutoffFrequency;
|
||||
ma_lpf_config lpfConfig;
|
||||
ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */
|
||||
ma_uint32 minSampleRate;
|
||||
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->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);
|
||||
|
||||
|
||||
/* LPF */
|
||||
/* LPF. */
|
||||
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)));
|
||||
}
|
||||
@@ -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->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. */
|
||||
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) {
|
||||
return result;
|
||||
}
|
||||
@@ -59315,8 +59264,6 @@ MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma
|
||||
return;
|
||||
}
|
||||
|
||||
ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks);
|
||||
|
||||
if (pResampler->_ownsHeap) {
|
||||
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)
|
||||
{
|
||||
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)
|
||||
@@ -61307,7 +61254,7 @@ MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler
|
||||
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)
|
||||
@@ -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_uint32 iChannel;
|
||||
ma_uint32 iLPF;
|
||||
|
||||
if (pResampler == NULL) {
|
||||
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. */
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user