From 811e438d1b1873bb49b531d6e7f093b4d4278d02 Mon Sep 17 00:00:00 2001 From: David Reid Date: Mon, 20 Jan 2020 19:01:42 +1000 Subject: [PATCH] Add support for reinitialization of low-pass and biquad filters. --- research/ma_lpf.h | 69 ++++++++++++++++++++++++++++++++--------- research/ma_resampler.h | 29 +++++++++-------- 2 files changed, 69 insertions(+), 29 deletions(-) diff --git a/research/ma_lpf.h b/research/ma_lpf.h index 73da294c..4337522f 100644 --- a/research/ma_lpf.h +++ b/research/ma_lpf.h @@ -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) { diff --git a/research/ma_resampler.h b/research/ma_resampler.h index df210082..8db0af31 100644 --- a/research/ma_resampler.h +++ b/research/ma_resampler.h @@ -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;