Commit a90131d8 authored by David Reid's avatar David Reid

Improvements to high order low- and high-pass filters.

This changes the Q value of the chain of second order low-pass filters
making up the high order filters. Previously the Q value was always set
to 0.707107 for the entire chain, but this is incorrect for Butterworth
filters when the order is greater than 2.

This should make a small improvement to the quality of the linear
resampler.
parent 163286be
...@@ -1072,13 +1072,13 @@ Low-Pass Filtering ...@@ -1072,13 +1072,13 @@ Low-Pass Filtering
------------------ ------------------
Low-pass filter is achieved with the following APIs: Low-pass filter is achieved with the following APIs:
|---------|-----------------------------------------| |---------|------------------------------------------|
| API | Description | | API | Description |
|---------|-----------------------------------------| |---------|------------------------------------------|
| ma_lpf1 | First order low-pass filter | | ma_lpf1 | First order low-pass filter |
| ma_lpf2 | Second order low-pass filter | | ma_lpf2 | Second order low-pass filter |
| ma_lpf | Low-pass filter with configurable order | | ma_lpf | High order low-pass filter (Butterworth) |
|---------|-----------------------------------------| |---------|------------------------------------------|
Low-pass filter example: Low-pass filter example:
...@@ -1103,11 +1103,11 @@ Filtering can be applied in-place by passing in the same pointer for both the in ...@@ -1103,11 +1103,11 @@ Filtering can be applied in-place by passing in the same pointer for both the in
ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount); ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount);
``` ```
The maximum filter order is limited to MA_MAX_FILTER_ORDER which is set to 8. If you need more, you can chain filters together. The maximum filter order is limited to MA_MAX_FILTER_ORDER which is set to 8. If you need more, you can chain first and second order filters together.
```c ```c
for (iFilter = 0; iFilter < filterCount; iFilter += 1) { for (iFilter = 0; iFilter < filterCount; iFilter += 1) {
ma_lpf_process_pcm_frames(&lpf[iFilter], pMyData, pMyData, frameCount); ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount);
} }
``` ```
...@@ -1126,13 +1126,13 @@ High-Pass Filtering ...@@ -1126,13 +1126,13 @@ High-Pass Filtering
------------------- -------------------
High-pass filtering is achieved with the following APIs: High-pass filtering is achieved with the following APIs:
|---------|------------------------------------------| |---------|-------------------------------------------|
| API | Description | | API | Description |
|---------|------------------------------------------| |---------|-------------------------------------------|
| ma_hpf1 | First order high-pass filter | | ma_hpf1 | First order high-pass filter |
| ma_hpf2 | Second order high-pass filter | | ma_hpf2 | Second order high-pass filter |
| ma_hpf | High-pass filter with configurable order | | ma_hpf | High order high-pass filter (Butterworth) |
|---------|------------------------------------------| |---------|-------------------------------------------|
High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, `ma_hpf2` and `ma_hpf`. See example code for low-pass filters High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, `ma_hpf2` and `ma_hpf`. See example code for low-pass filters
for example usage. for example usage.
...@@ -1142,12 +1142,12 @@ Band-Pass Filtering ...@@ -1142,12 +1142,12 @@ Band-Pass Filtering
------------------- -------------------
Band-pass filtering is achieved with the following APIs: Band-pass filtering is achieved with the following APIs:
|---------|------------------------------------------| |---------|-------------------------------|
| API | Description | | API | Description |
|---------|------------------------------------------| |---------|-------------------------------|
| ma_bpf2 | Second order band-pass filter | | ma_bpf2 | Second order band-pass filter |
| ma_bpf | Band-pass filter with configurable order | | ma_bpf | High order band-pass filter |
|---------|------------------------------------------| |---------|-------------------------------|
Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and `ma_hpf`. See example code for low-pass filters for example Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and `ma_hpf`. See example code for low-pass filters for example
usage. Note that the order for band-pass filters must be an even number which means there is no first order band-pass filter, unlike low-pass and high-pass usage. Note that the order for band-pass filters must be an even number which means there is no first order band-pass filter, unlike low-pass and high-pass
...@@ -30262,10 +30262,16 @@ static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, ma_lpf* p ...@@ -30262,10 +30262,16 @@ static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, ma_lpf* p
for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) {
ma_lpf2_config lpf2Config; ma_lpf2_config lpf2Config;
double q; double q;
double a;
/* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */
if (lpf1Count == 1) {
a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */
} else {
a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */
}
q = 1 / (2*ma_cos(a));
/* TODO: Calculate Q. */
q = 0;
lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
if (isNew) { if (isNew) {
...@@ -30758,9 +30764,15 @@ static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, ma_hpf* p ...@@ -30758,9 +30764,15 @@ static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, ma_hpf* p
for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) {
ma_hpf2_config hpf2Config; ma_hpf2_config hpf2Config;
double q; double q;
double a;
/* TODO: Calculate Q. */ /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */
q = 0; if (hpf1Count == 1) {
a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */
} else {
a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */
}
q = 1 / (2*ma_cos(a));
hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
...@@ -31078,8 +31090,8 @@ static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, ma_bpf* p ...@@ -31078,8 +31090,8 @@ static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, ma_bpf* p
ma_bpf2_config bpf2Config; ma_bpf2_config bpf2Config;
double q; double q;
/* TODO: Calculate Q. */ /* TODO: Calculate Q to make this a proper Butterworth filter. */
q = 0; q = 0.707107;
bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
...@@ -38318,11 +38330,12 @@ ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outpu ...@@ -38318,11 +38330,12 @@ ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outpu
config.format = outputFormat; config.format = outputFormat;
config.channels = outputChannels; config.channels = outputChannels;
config.sampleRate = outputSampleRate; config.sampleRate = outputSampleRate;
ma_get_standard_channel_map(ma_standard_channel_map_default, config.channels, config.channelMap);
config.resampling.algorithm = ma_resample_algorithm_linear; config.resampling.algorithm = ma_resample_algorithm_linear;
config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
config.resampling.speex.quality = 3; config.resampling.speex.quality = 3;
/* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */
return config; return config;
} }
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