Resampler: For LPF orders to be a multiple of 2.

This is in preparation for an implementation simplification for the
purpose of some upcoming optimizations.
This commit is contained in:
David Reid
2026-02-08 19:21:10 +10:00
parent a04f300821
commit 20180b0ae5
+19 -5
View File
@@ -3017,7 +3017,9 @@ The linear resampler performs low-pass filtering before or after downsampling or
depending on the sample rates you're converting between. When decreasing the sample rate, the depending on the sample rates you're converting between. When decreasing the sample rate, the
low-pass filter will be applied before downsampling. When increasing the rate it will be performed low-pass filter will be applied before downsampling. When increasing the rate it will be performed
after upsampling. By default a fourth order low-pass filter will be applied. This can be configured after upsampling. By default a fourth order low-pass filter will be applied. This can be configured
via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering. via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering. It should be
set to a multiple of 2, such as 2, 4, 6, 8, etc. There are diminishing returns the higher you go.
The maximum is `MA_MAX_FILTER_ORDER` which is 8 by default (it can be configured at compile time).
The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of
the input and output sample rates (Nyquist Frequency). the input and output sample rates (Nyquist Frequency).
@@ -5580,7 +5582,7 @@ typedef struct
ma_uint32 channels; ma_uint32 channels;
ma_uint32 sampleRateIn; ma_uint32 sampleRateIn;
ma_uint32 sampleRateOut; ma_uint32 sampleRateOut;
ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */ ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. Must be a multiple of 2. */
float lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */ float lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */
} ma_linear_resampler_config; } ma_linear_resampler_config;
@@ -5667,7 +5669,7 @@ struct ma_resampler_config
void* pBackendUserData; void* pBackendUserData;
struct struct
{ {
ma_uint32 lpfOrder; ma_uint32 lpfOrder; /* Must be a multiple of 2. */
} linear; } linear;
}; };
@@ -8718,7 +8720,7 @@ then be set directly on the structure. Below are the members of the `ma_device_c
resampling.linear.lpfOrder resampling.linear.lpfOrder
The linear resampler applies a low-pass filter as part of its processing for anti-aliasing. This setting controls the order of the filter. The higher The linear resampler applies a low-pass filter as part of its processing for anti-aliasing. This setting controls the order of the filter. The higher
the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is
`MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`. `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`. It must be a multiple of 2.
playback.pDeviceID playback.pDeviceID
A pointer to a `ma_device_id` structure containing the ID of the playback device to initialize. Setting this NULL (default) will use the system's A pointer to a `ma_device_id` structure containing the ID of the playback device to initialize. Setting this NULL (default) will use the system's
@@ -58986,6 +58988,8 @@ static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pRes
static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout) static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout)
{ {
ma_uint32 lpfOrder;
MA_ASSERT(pHeapLayout != NULL); MA_ASSERT(pHeapLayout != NULL);
MA_ZERO_OBJECT(pHeapLayout); MA_ZERO_OBJECT(pHeapLayout);
@@ -59002,6 +59006,11 @@ static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_c
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
lpfOrder = pConfig->lpfOrder;
if ((lpfOrder & 0x1) != 0) {
lpfOrder += 1; /* Round up to even. */
}
pHeapLayout->sizeInBytes = 0; pHeapLayout->sizeInBytes = 0;
/* x0 */ /* x0 */
@@ -59025,7 +59034,7 @@ static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_c
{ {
ma_result result; ma_result result;
size_t lpfHeapSizeInBytes; size_t lpfHeapSizeInBytes;
ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder); /* Sample rate and cutoff frequency do not matter. */ 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); result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
@@ -59085,6 +59094,11 @@ MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler
pResampler->lpfOrder = pConfig->lpfOrder; pResampler->lpfOrder = pConfig->lpfOrder;
pResampler->lpfNyquistFactor = pConfig->lpfNyquistFactor; pResampler->lpfNyquistFactor = pConfig->lpfNyquistFactor;
/* The order needs to be even. */
if ((pResampler->lpfOrder & 0x1) != 0) {
pResampler->lpfOrder += 1; /* Round up to even. */
}
pResampler->_pHeap = pHeap; pResampler->_pHeap = pHeap;
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);