mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-23 08:44:04 +02:00
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.
This commit is contained in:
+43
-30
@@ -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
|
|||||||
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
|
|||||||
-------------------
|
-------------------
|
||||||
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
|
|||||||
-------------------
|
-------------------
|
||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user