Commit 825cd23a authored by David Reid's avatar David Reid

Add second order peaking EQ filter.

This API is called ma_peak.
parent b4e024a4
...@@ -1027,7 +1027,7 @@ The maximum number of poles is limited to MA_MAX_FILTER_POLES which is set to 8. ...@@ -1027,7 +1027,7 @@ The maximum number of poles is limited to MA_MAX_FILTER_POLES which is set to 8.
} }
``` ```
If you need to change the configuration of the filter, but need to maintain the state of internal registers you can do so with `ma_lpf2_reinit()`. This may be If you need to change the configuration of the filter, but need to maintain the state of internal registers you can do so with `ma_lpf_reinit()`. This may be
useful if you need to change the sample rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the format or channel useful if you need to change the sample rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the format or channel
count after initialization is invalid and will result in an error. count after initialization is invalid and will result in an error.
...@@ -1843,6 +1843,35 @@ ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* ...@@ -1843,6 +1843,35 @@ ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void*
ma_uint32 ma_bpf_get_latency(ma_bpf* pBPF); ma_uint32 ma_bpf_get_latency(ma_bpf* pBPF);
/**************************************************************************************************************************************************************
Peaking EQ Filter
**************************************************************************************************************************************************************/
typedef struct
{
ma_format format;
ma_uint32 channels;
ma_uint32 sampleRate;
double gainDB;
double q;
double frequency;
} ma_peak2_config;
ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
typedef struct
{
ma_biquad bq;
} ma_peak2;
ma_result ma_peak2_init(const ma_peak2_config* pConfig, ma_peak2* pFilter);
ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter);
ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
ma_uint32 ma_peak2_get_latency(ma_peak2* pFilter);
/************************************************************************************************************************************************************ /************************************************************************************************************************************************************
************************************************************************************************************************************************************* *************************************************************************************************************************************************************
...@@ -30834,6 +30863,138 @@ ma_uint32 ma_bpf_get_latency(ma_bpf* pBPF) ...@@ -30834,6 +30863,138 @@ ma_uint32 ma_bpf_get_latency(ma_bpf* pBPF)
} }
/**************************************************************************************************************************************************************
Peaking EQ Filter
**************************************************************************************************************************************************************/
ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
{
ma_peak2_config config;
MA_ZERO_OBJECT(&config);
config.format = format;
config.channels = channels;
config.sampleRate = sampleRate;
config.gainDB = gainDB;
config.q = q;
config.frequency = frequency;
if (config.gainDB == 0) {
config.gainDB = 6;
}
if (config.q == 0) {
config.q = 0.707107;
}
return config;
}
static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig)
{
ma_biquad_config bqConfig;
double q;
double w;
double s;
double c;
double a;
double A;
MA_ASSERT(pConfig != NULL);
q = pConfig->q;
w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
s = ma_sin(w);
c = ma_cos(w);
a = s / (2*q);
A = ma_pow(10, (pConfig->gainDB / 40));
bqConfig.b0 = 1 + (a * A);
bqConfig.b1 = -2 * c;
bqConfig.b2 = 1 - (a * A);
bqConfig.a0 = 1 + (a / A);
bqConfig.a1 = -2 * c;
bqConfig.a2 = 1 - (a / A);
bqConfig.format = pConfig->format;
bqConfig.channels = pConfig->channels;
return bqConfig;
}
ma_result ma_peak2_init(const ma_peak2_config* pConfig, ma_peak2* pFilter)
{
ma_result result;
ma_biquad_config bqConfig;
if (pFilter == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pFilter);
if (pConfig == NULL) {
return MA_INVALID_ARGS;
}
bqConfig = ma_peak2__get_biquad_config(pConfig);
result = ma_biquad_init(&bqConfig, &pFilter->bq);
if (result != MA_SUCCESS) {
return result;
}
return MA_SUCCESS;
}
ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter)
{
ma_result result;
ma_biquad_config bqConfig;
if (pFilter == NULL || pConfig == NULL) {
return MA_INVALID_ARGS;
}
bqConfig = ma_peak2__get_biquad_config(pConfig);
result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
if (result != MA_SUCCESS) {
return result;
}
return MA_SUCCESS;
}
static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
{
ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
}
static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn)
{
ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
}
ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
{
if (pFilter == NULL) {
return MA_INVALID_ARGS;
}
return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
}
ma_uint32 ma_peak2_get_latency(ma_peak2* pFilter)
{
if (pFilter == NULL) {
return 0;
}
return ma_biquad_get_latency(&pFilter->bq);
}
/************************************************************************************************************************************************************** /**************************************************************************************************************************************************************
...@@ -26,6 +26,7 @@ ma_result filtering_init_decoder_and_encoder(const char* pInputFilePath, const c ...@@ -26,6 +26,7 @@ ma_result filtering_init_decoder_and_encoder(const char* pInputFilePath, const c
#include "ma_test_filtering_lpf.c" #include "ma_test_filtering_lpf.c"
#include "ma_test_filtering_hpf.c" #include "ma_test_filtering_hpf.c"
#include "ma_test_filtering_bpf.c" #include "ma_test_filtering_bpf.c"
#include "ma_test_filtering_peak.c"
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
...@@ -36,6 +37,7 @@ int main(int argc, char** argv) ...@@ -36,6 +37,7 @@ int main(int argc, char** argv)
(void)argc; (void)argc;
(void)argv; (void)argv;
#if 0
result = ma_register_test("Dithering", test_entry__dithering); result = ma_register_test("Dithering", test_entry__dithering);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
hasError = MA_TRUE; hasError = MA_TRUE;
...@@ -55,6 +57,12 @@ int main(int argc, char** argv) ...@@ -55,6 +57,12 @@ int main(int argc, char** argv)
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
hasError = MA_TRUE; hasError = MA_TRUE;
} }
#endif
result = ma_register_test("Peaking EQ Filtering", test_entry__peak);
if (result != MA_SUCCESS) {
hasError = MA_TRUE;
}
for (iTest = 0; iTest < g_Tests.count; iTest += 1) { for (iTest = 0; iTest < g_Tests.count; iTest += 1) {
printf("=== BEGIN %s ===\n", g_Tests.pTests[iTest].pName); printf("=== BEGIN %s ===\n", g_Tests.pTests[iTest].pName);
......
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