mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-23 00:34:03 +02:00
Add support for reinitialization of low-pass and biquad filters.
This commit is contained in:
+55
-14
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
TODO:
|
||||
- Document passthrough behaviour of the biquad filter and how it doesn't update previous inputs and outputs.
|
||||
- Document how changing biquad constants requires reinitialization of the filter (due to issue above).
|
||||
- Document how changing biquad constants requires reinitialization of the filter (due to issue above). ma_biquad_reinit().
|
||||
- Document how ma_biquad_process() and ma_lpf_process() supports in-place filtering by passing in the same buffer for both the input and output.
|
||||
*/
|
||||
|
||||
@@ -34,6 +34,7 @@ typedef struct
|
||||
} ma_biquad;
|
||||
|
||||
ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ);
|
||||
ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ);
|
||||
ma_result ma_biquad_process(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
||||
|
||||
|
||||
@@ -54,6 +55,7 @@ typedef struct
|
||||
} ma_lpf;
|
||||
|
||||
ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF);
|
||||
ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF);
|
||||
ma_result ma_lpf_process(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
||||
|
||||
#endif /* ma_lpf_h */
|
||||
@@ -92,6 +94,15 @@ ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ)
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
return ma_biquad_reinit(pConfig, pBQ);
|
||||
}
|
||||
|
||||
ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ)
|
||||
{
|
||||
if (pBQ == NULL || pConfig == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
if (pConfig->a0 == 0) {
|
||||
return MA_INVALID_ARGS; /* Division by zero. */
|
||||
}
|
||||
@@ -206,9 +217,8 @@ ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32
|
||||
return config;
|
||||
}
|
||||
|
||||
ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF)
|
||||
static MA_INLINE ma_biquad_config ma_lpf__get_biquad_config(const ma_lpf_config* pConfig)
|
||||
{
|
||||
ma_result result;
|
||||
ma_biquad_config bqConfig;
|
||||
double q;
|
||||
double w;
|
||||
@@ -216,17 +226,7 @@ ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF)
|
||||
double c;
|
||||
double a;
|
||||
|
||||
if (pLPF == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
MA_ZERO_OBJECT(pLPF);
|
||||
|
||||
if (pConfig == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
pLPF->config = *pConfig;
|
||||
MA_ASSERT(pConfig != NULL);
|
||||
|
||||
q = 1 / sqrt(2);
|
||||
w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
|
||||
@@ -244,6 +244,27 @@ ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF)
|
||||
bqConfig.format = pConfig->format;
|
||||
bqConfig.channels = pConfig->channels;
|
||||
|
||||
return bqConfig;
|
||||
}
|
||||
|
||||
ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF)
|
||||
{
|
||||
ma_result result;
|
||||
ma_biquad_config bqConfig;
|
||||
|
||||
if (pLPF == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
MA_ZERO_OBJECT(pLPF);
|
||||
|
||||
if (pConfig == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
pLPF->config = *pConfig;
|
||||
|
||||
bqConfig = ma_lpf__get_biquad_config(pConfig);
|
||||
result = ma_biquad_init(&bqConfig, &pLPF->bq);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
@@ -252,6 +273,26 @@ ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF)
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF)
|
||||
{
|
||||
ma_result result;
|
||||
ma_biquad_config bqConfig;
|
||||
|
||||
if (pLPF == NULL || pConfig == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
pLPF->config = *pConfig;
|
||||
|
||||
bqConfig = ma_lpf__get_biquad_config(pConfig);
|
||||
result = ma_biquad_reinit(&bqConfig, &pLPF->bq);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
ma_result ma_lpf_process(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
||||
{
|
||||
if (pLPF == NULL) {
|
||||
|
||||
+14
-15
@@ -269,24 +269,18 @@ ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channel
|
||||
return config;
|
||||
}
|
||||
|
||||
static ma_result ma_resampler__init_lpf(ma_resampler* pResampler)
|
||||
static ma_lpf_config ma_resampler_create_lpf_config(ma_resampler* pResampler)
|
||||
{
|
||||
ma_result result;
|
||||
ma_lpf_config lpfConfig;
|
||||
ma_lpf_config config;
|
||||
|
||||
pResampler->state.linear.t = -1; /* This must be set to -1 for the linear backend. It's used to indicate that the first frame needs to be loaded. */
|
||||
MA_ASSERT(pResampler != NULL);
|
||||
|
||||
lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, pResampler->config.sampleRateOut, pResampler->config.linear.lpfCutoffFrequency);
|
||||
if (lpfConfig.cutoffFrequency == 0) {
|
||||
lpfConfig.cutoffFrequency = ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) / 2;
|
||||
config = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, pResampler->config.sampleRateOut, pResampler->config.linear.lpfCutoffFrequency);
|
||||
if (config.cutoffFrequency == 0) {
|
||||
config.cutoffFrequency = ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) / 2;
|
||||
}
|
||||
|
||||
result = ma_lpf_init(&lpfConfig, &pResampler->state.linear.lpf);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
return config;
|
||||
}
|
||||
|
||||
ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pResampler)
|
||||
@@ -317,7 +311,8 @@ ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pR
|
||||
pResampler->state.linear.t = -1;
|
||||
|
||||
if (pConfig->linear.enableLPF) {
|
||||
result = ma_resampler__init_lpf(pResampler);
|
||||
ma_lpf_config lpfConfig = ma_resampler_create_lpf_config(pResampler);
|
||||
result = ma_lpf_init(&lpfConfig, &pResampler->state.linear.lpf);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
@@ -802,7 +797,11 @@ ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn
|
||||
{
|
||||
/* If we are using low-pass filtering we need to reinitialize the filter since it depends on the sample rate. */
|
||||
if (pResampler->config.linear.enableLPF) {
|
||||
ma_resampler__init_lpf(pResampler);
|
||||
ma_lpf_config lpfConfig = ma_resampler_create_lpf_config(pResampler);
|
||||
ma_result result = ma_lpf_reinit(&lpfConfig, &pResampler->state.linear.lpf);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result; /* Failed to reinitialize the low-pass filter. */
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user