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 @@
/*
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) {
......
......@@ -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;
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_lpf_config config;
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;
}
MA_ASSERT(pResampler != NULL);
result = ma_lpf_init(&lpfConfig, &pResampler->state.linear.lpf);
if (result != MA_SUCCESS) {
return result;
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;
}
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;
......
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