Commit 811e438d authored by David Reid's avatar David Reid

Add support for reinitialization of low-pass and biquad filters.

parent 5d0f2144
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
/* /*
TODO: TODO:
- Document passthrough behaviour of the biquad filter and how it doesn't update previous inputs and outputs. - 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. - 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 ...@@ -34,6 +34,7 @@ typedef struct
} ma_biquad; } ma_biquad;
ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ); 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); ma_result ma_biquad_process(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
...@@ -54,6 +55,7 @@ typedef struct ...@@ -54,6 +55,7 @@ typedef struct
} ma_lpf; } ma_lpf;
ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF); 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); ma_result ma_lpf_process(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
#endif /* ma_lpf_h */ #endif /* ma_lpf_h */
...@@ -92,6 +94,15 @@ ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ) ...@@ -92,6 +94,15 @@ ma_result ma_biquad_init(const ma_biquad_config* pConfig, ma_biquad* pBQ)
return MA_INVALID_ARGS; 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) { if (pConfig->a0 == 0) {
return MA_INVALID_ARGS; /* Division by zero. */ 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 ...@@ -206,9 +217,8 @@ ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32
return config; 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; ma_biquad_config bqConfig;
double q; double q;
double w; double w;
...@@ -216,17 +226,7 @@ ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF) ...@@ -216,17 +226,7 @@ ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF)
double c; double c;
double a; double a;
if (pLPF == NULL) { MA_ASSERT(pConfig != NULL);
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pLPF);
if (pConfig == NULL) {
return MA_INVALID_ARGS;
}
pLPF->config = *pConfig;
q = 1 / sqrt(2); q = 1 / sqrt(2);
w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; 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) ...@@ -244,6 +244,27 @@ ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF)
bqConfig.format = pConfig->format; bqConfig.format = pConfig->format;
bqConfig.channels = pConfig->channels; 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); result = ma_biquad_init(&bqConfig, &pLPF->bq);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
...@@ -252,6 +273,26 @@ ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF) ...@@ -252,6 +273,26 @@ ma_result ma_lpf_init(const ma_lpf_config* pConfig, ma_lpf* pLPF)
return MA_SUCCESS; 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) ma_result ma_lpf_process(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
{ {
if (pLPF == NULL) { if (pLPF == NULL) {
......
...@@ -269,24 +269,18 @@ ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channel ...@@ -269,24 +269,18 @@ ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channel
return config; 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 config;
ma_lpf_config lpfConfig;
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. */
lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, pResampler->config.sampleRateOut, pResampler->config.linear.lpfCutoffFrequency); MA_ASSERT(pResampler != NULL);
if (lpfConfig.cutoffFrequency == 0) {
lpfConfig.cutoffFrequency = ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) / 2;
}
result = ma_lpf_init(&lpfConfig, &pResampler->state.linear.lpf); config = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, pResampler->config.sampleRateOut, pResampler->config.linear.lpfCutoffFrequency);
if (result != MA_SUCCESS) { if (config.cutoffFrequency == 0) {
return result; config.cutoffFrequency = ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) / 2;
} }
return MA_SUCCESS; return config;
} }
ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pResampler) 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 ...@@ -317,7 +311,8 @@ ma_result ma_resampler_init(const ma_resampler_config* pConfig, ma_resampler* pR
pResampler->state.linear.t = -1; pResampler->state.linear.t = -1;
if (pConfig->linear.enableLPF) { 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) { if (result != MA_SUCCESS) {
return result; return result;
} }
...@@ -802,7 +797,11 @@ ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn ...@@ -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 we are using low-pass filtering we need to reinitialize the filter since it depends on the sample rate. */
if (pResampler->config.linear.enableLPF) { 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; } break;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment