From e2d5e64f217b16a3fd9d3f7864e974e068620b62 Mon Sep 17 00:00:00 2001
From: jthedering <51533355+jthedering@users.noreply.github.com>
Date: Tue, 14 May 2013 19:22:04 +0000
Subject: [PATCH] Added: All remaining filter types (LP(Q), HP(Q), LS(6/12dB),
HS(6/12dB), NO, AP) of RoomEQWizard's "Generic" equalizer. Syntax is
compatible to REW's filter text file, of course.
---
Benchmark/Benchmark.vcxproj | 2 +
BiQuad.cpp | 130 ++++++++++++++++++++
BiQuad.h | 72 ++++++++++++
EqualizerAPO.vcxproj | 2 +
LogHelper.h | 8 +-
ParametricEQ.cpp | 229 +++++++++++++++++++++++++++++-------
ParametricEQ.h | 45 +------
7 files changed, 397 insertions(+), 91 deletions(-)
create mode 100644 BiQuad.cpp
create mode 100644 BiQuad.h
diff --git a/Benchmark/Benchmark.vcxproj b/Benchmark/Benchmark.vcxproj
index a38752d..dedaa40 100644
--- a/Benchmark/Benchmark.vcxproj
+++ b/Benchmark/Benchmark.vcxproj
@@ -149,6 +149,7 @@
+
@@ -156,6 +157,7 @@
+
diff --git a/BiQuad.cpp b/BiQuad.cpp
new file mode 100644
index 0000000..5ae330a
--- /dev/null
+++ b/BiQuad.cpp
@@ -0,0 +1,130 @@
+
+#define _USE_MATH_DEFINES
+#include
+#include
+#include "BiQuad.h"
+
+using namespace std;
+
+BiQuad::BiQuad(Type type, float dbGain, float freq, float srate, float bandwidthOrQOrS, bool isBandwidth)
+{
+ float A;
+ if(type == PEAKING || type == LOW_SHELF || type == HIGH_SHELF)
+ A = pow(10, dbGain / 40);
+ else
+ A = pow(10, dbGain / 20);
+ float omega = 2 * (float)M_PI * freq / srate;
+ float sn = sin(omega);
+ float cs = cos(omega);
+ float alpha;
+ float beta = -1;
+
+ if (type == LOW_SHELF || type == HIGH_SHELF) // S
+ {
+ alpha = sn/2 * sqrt((A + 1/A) * (1/bandwidthOrQOrS - 1) + 2);
+ beta = 2 * sqrt(A) * alpha;
+ }
+ else if(isBandwidth) // BW
+ alpha = sn * sinh((float)M_LN2/2 * bandwidthOrQOrS * omega / sn);
+ else // Q
+ alpha = sn / (2 * bandwidthOrQOrS);
+
+ float b0, b1, b2, a0, a1, a2;
+
+ switch(type)
+ {
+ case LOW_PASS:
+ b0 = (1 - cs) /2;
+ b1 = 1 - cs;
+ b2 = (1 - cs) /2;
+ a0 = 1 + alpha;
+ a1 = -2 * cs;
+ a2 = 1 - alpha;
+ break;
+ case HIGH_PASS:
+ b0 = (1 + cs) /2;
+ b1 = -(1 + cs);
+ b2 = (1 + cs) /2;
+ a0 = 1 + alpha;
+ a1 = -2 * cs;
+ a2 = 1 - alpha;
+ break;
+ case BAND_PASS:
+ b0 = alpha;
+ b1 = 0;
+ b2 = -alpha;
+ a0 = 1 + alpha;
+ a1 = -2 * cs;
+ a2 = 1 - alpha;
+ break;
+ case NOTCH:
+ b0 = 1;
+ b1 = -2 * cs;
+ b2 = 1;
+ a0 = 1 + alpha;
+ a1 = -2 * cs;
+ a2 = 1 - alpha;
+ break;
+ case ALL_PASS:
+ b0 = 1 - alpha;
+ b1 = -2 * cs;
+ b2 = 1 + alpha;
+ a0 = 1 + alpha;
+ a1 = -2 * cs;
+ a2 = 1 - alpha;
+ break;
+ case PEAKING:
+ b0 = 1 + (alpha * A);
+ b1 = -2 * cs;
+ b2 = 1 - (alpha * A);
+ a0 = 1 + (alpha / A);
+ a1 = -2 * cs;
+ a2 = 1 - (alpha / A);
+ break;
+ case LOW_SHELF:
+ b0 = A * ((A + 1) - (A - 1) * cs + beta);
+ b1 = 2 * A * ((A - 1) - (A + 1) * cs);
+ b2 = A * ((A + 1) - (A - 1) * cs - beta);
+ a0 = (A + 1) + (A - 1) * cs + beta;
+ a1 = -2 * ((A - 1) + (A + 1) * cs);
+ a2 = (A + 1) + (A - 1) * cs - beta;
+ break;
+ case HIGH_SHELF:
+ b0 = A * ((A + 1) + (A - 1) * cs + beta);
+ b1 = -2 * A * ((A - 1) + (A + 1) * cs);
+ b2 = A * ((A + 1) + (A - 1) * cs - beta);
+ a0 = (A + 1) - (A - 1) * cs + beta;
+ a1 = 2 * ((A - 1) - (A + 1) * cs);
+ a2 = (A + 1) - (A - 1) * cs - beta;
+ break;
+ }
+
+ this->a0 = b0 /a0;
+ this->a[0] = b1 /a0;
+ this->a[1] = b2 /a0;
+ this->a[2] = -a1 /a0;
+ this->a[3] = -a2 /a0;
+
+ x1 = 0;
+ x2 = 0;
+ y1 = 0;
+ y2 = 0;
+}
+
+float BiQuad::gainAt(float freq, float srate)
+{
+ float omega = 2 * (float)M_PI * freq / srate;
+ float sn = sin(omega/2.0f);
+ float phi = sn * sn;
+ float b0 = this->a0;
+ float b1 = this->a[0];
+ float b2 = this->a[1];
+ float a0 = 1.0f;
+ float a1 = -this->a[2];
+ float a2 = -this->a[3];
+
+ float dbGain = 10*log10( pow(b0+b1+b2, 2) - 4*(b0*b1 + 4*b0*b2 + b1*b2)*phi + 16*b0*b2*phi*phi )
+ -10*log10( pow(a0+a1+a2, 2) - 4*(a0*a1 + 4*a0*a2 + a1*a2)*phi + 16*a0*a2*phi*phi );
+
+ return dbGain;
+}
\ No newline at end of file
diff --git a/BiQuad.h b/BiQuad.h
new file mode 100644
index 0000000..3951fe7
--- /dev/null
+++ b/BiQuad.h
@@ -0,0 +1,72 @@
+/*
+ This file is part of EqualizerAPO, a system-wide equalizer.
+ Copyright (C) 2013 Jonas Thedering
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include
+
+#define IS_DENORMAL(f) (((*(unsigned int *)&(f))&0x7f800000) == 0)
+
+class BiQuad
+{
+public:
+ enum Type
+ {
+ LOW_PASS, HIGH_PASS, BAND_PASS, NOTCH, ALL_PASS, PEAKING, LOW_SHELF, HIGH_SHELF
+ };
+
+ BiQuad() {}
+ BiQuad(Type type, float dbGain, float freq, float srate, float bandwidthOrQOrS, bool isBandwidth);
+
+ __forceinline
+ void removeDenormals()
+ {
+ if(IS_DENORMAL(x1))
+ x1 = 0.0f;
+ if(IS_DENORMAL(x2))
+ x2 = 0.0f;
+ if(IS_DENORMAL(y1))
+ y1 = 0.0f;
+ if(IS_DENORMAL(y2))
+ y2 = 0.0f;
+ }
+
+ __forceinline
+ float process(float sample)
+ {
+ float result = a0 * sample + a[0] * x1 + a[1] * x2 +
+ a[2] * y1 + a[3] * y2;
+
+ x2 = x1;
+ x1 = sample;
+
+ y2 = y1;
+ y1 = result;
+
+ //Input for next stage
+ return result;
+ }
+
+ float gainAt(float freq, float srate);
+
+private:
+ __declspec(align(16)) float a[4];
+ float a0;
+
+ float x1, x2;
+ float y1, y2;
+};
diff --git a/EqualizerAPO.vcxproj b/EqualizerAPO.vcxproj
index e2b898a..aae2845 100644
--- a/EqualizerAPO.vcxproj
+++ b/EqualizerAPO.vcxproj
@@ -157,6 +157,7 @@
+
@@ -168,6 +169,7 @@
+
diff --git a/LogHelper.h b/LogHelper.h
index 3d891b1..9e75cd8 100644
--- a/LogHelper.h
+++ b/LogHelper.h
@@ -22,10 +22,10 @@
#include
#include
-#define TraceF(format, ...) LogHelper::log(__FILE__, __LINE__, this, true, format, __VA_ARGS__);
-#define TraceFStatic(format, ...) LogHelper::log(__FILE__, __LINE__, NULL, true, format, __VA_ARGS__);
-#define LogF(format, ...) LogHelper::log(__FILE__, __LINE__, this, false, format, __VA_ARGS__);
-#define LogFStatic(format, ...) LogHelper::log(__FILE__, __LINE__, NULL, false, format, __VA_ARGS__);
+#define TraceF(format, ...) LogHelper::log(__FILE__, __LINE__, this, true, format, __VA_ARGS__)
+#define TraceFStatic(format, ...) LogHelper::log(__FILE__, __LINE__, NULL, true, format, __VA_ARGS__)
+#define LogF(format, ...) LogHelper::log(__FILE__, __LINE__, this, false, format, __VA_ARGS__)
+#define LogFStatic(format, ...) LogHelper::log(__FILE__, __LINE__, NULL, false, format, __VA_ARGS__)
class LogHelper
{
diff --git a/ParametricEQ.cpp b/ParametricEQ.cpp
index 30f6e37..da207a4 100644
--- a/ParametricEQ.cpp
+++ b/ParametricEQ.cpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#define WIN32_LEAN_AND_MEAN
#include
#include
@@ -37,30 +38,12 @@
using namespace std;
using namespace stdext;
-BiQuad::BiQuad(float dbGain, float freq, float srate, float bandwidthOrQ, bool isQ)
-{
- float A = pow(10, dbGain / 40);
- float omega = 2 * (float)M_PI * freq / srate;
- float sn = sin(omega);
- float cs = cos(omega);
- float alpha;
- if(isQ)
- alpha = sn / (2 * bandwidthOrQ);
- else
- alpha = sn * sinh((float)M_LN2/2 * bandwidthOrQ * omega / sn);
-
- float temp = 1 + (alpha /A);
- a0 = (1 + (alpha * A)) / temp;
- a[0] = (-2 * cs) / temp;
- a[1] = (1 - (alpha * A)) / temp;
- a[2] = - (-2 * cs) / temp;
- a[3] = - (1 - (alpha /A)) / temp;
-
- x1 = 0;
- x2 = 0;
- y1 = 0;
- y2 = 0;
-}
+static wregex regexType(L"^\\s*ON\\s+([A-Za-z]+)");
+static wregex regexFreq(L"\\s+Fc\\s*([0-9.]+)\\s*H\\s*z");
+static wregex regexGain(L"\\s+Gain\\s*([-+0-9.]+)\\s*dB");
+static wregex regexQ(L"\\s+Q\\s*([0-9.]+)");
+static wregex regexBW(L"\\s+BW\\s+Oct\\s*([0-9.]+)");
+static wregex regexSlope(L"^\\s*([0-9.]+)\\s*dB");
ChannelData::ChannelData()
{
@@ -87,6 +70,28 @@ ParametricEQ::ParametricEQ()
for(hash_map::iterator it=channelNameToPosMap.begin(); it!=channelNameToPosMap.end(); it++)
channelPosToNameMap[it->second] = it->first;
+
+ filterNameToTypeMap[L"PK"] = BiQuad::PEAKING;
+ filterNameToTypeMap[L"PEQ"] = BiQuad::PEAKING;
+ filterNameToTypeMap[L"Modal"] = BiQuad::PEAKING;
+ filterNameToTypeMap[L"LP"] = BiQuad::LOW_PASS;
+ filterNameToTypeMap[L"HP"] = BiQuad::HIGH_PASS;
+ filterNameToTypeMap[L"LPQ"] = BiQuad::LOW_PASS;
+ filterNameToTypeMap[L"HPQ"] = BiQuad::HIGH_PASS;
+ filterNameToTypeMap[L"BP"] = BiQuad::BAND_PASS;
+ filterNameToTypeMap[L"LS"] = BiQuad::LOW_SHELF;
+ filterNameToTypeMap[L"HS"] = BiQuad::HIGH_SHELF;
+ filterNameToTypeMap[L"NO"] = BiQuad::NOTCH;
+ filterNameToTypeMap[L"AP"] = BiQuad::ALL_PASS;
+
+ filterTypeToDescriptionMap[BiQuad::PEAKING] = L"peaking";
+ filterTypeToDescriptionMap[BiQuad::LOW_PASS] = L"low-pass";
+ filterTypeToDescriptionMap[BiQuad::HIGH_PASS] = L"high-pass";
+ filterTypeToDescriptionMap[BiQuad::BAND_PASS] = L"band-pass";
+ filterTypeToDescriptionMap[BiQuad::LOW_SHELF] = L"low-shelf";
+ filterTypeToDescriptionMap[BiQuad::HIGH_SHELF] = L"high-shelf";
+ filterTypeToDescriptionMap[BiQuad::NOTCH] = L"notch";
+ filterTypeToDescriptionMap[BiQuad::ALL_PASS] = L"all-pass";
}
ParametricEQ::~ParametricEQ()
@@ -211,7 +216,26 @@ void ParametricEQ::loadConfig()
loadFilterCount += channelData[c].loadFilterCount;
}
- TraceF(L"%d filters loaded", loadFilterCount);
+ wstringstream channelFilterCounts;
+ unsigned c=0;
+ for(int i=0; i<31; i++)
+ {
+ int channelPos = 1< 0)
+ channelFilterCounts << L" ";
+ if(channelPosToNameMap.find(channelPos) != channelPosToNameMap.end())
+ channelFilterCounts << channelPosToNameMap[channelPos];
+ else
+ channelFilterCounts << c;
+
+ channelFilterCounts << ":" << channelData[c-1].loadFilterCount;
+ }
+ }
+
+ TraceF(L"%d filters loaded: %s", loadFilterCount, channelFilterCounts.str().c_str());
}
void ParametricEQ::loadConfig(const wstring& path, vector selectedChannels)
@@ -373,35 +397,150 @@ void ParametricEQ::loadConfig(const wstring& path, vector selectedChannels
//Conversion to period as decimal mark, if needed
value = StringHelper::replaceCharacters(value, L",", L'.');
- wchar_t freqString[10];
- float freq, gain, bandwidth;
+ wsmatch match;
+ wstring typeString;
- int matched = swscanf_s(value.c_str(), L" ON PEQ Fc %9s Hz Gain %f dB BW Oct %f", &freqString, 10, &gain, &bandwidth);
- if(matched == 3 && (freq = getFreq(freqString)) != -1.0f)
+ bool found = regex_search(value, match, regexType);
+ if(found)
{
- for(unsigned c=0; c
#include
-#define IS_DENORMAL(f) (((*(unsigned int *)&(f))&0x7f800000) == 0)
-
-struct BiQuad
-{
- BiQuad() {}
- BiQuad(float dbGain, float freq, float srate, float bandwidthOrQ, bool isQ);
-
- __forceinline
- void removeDenormals()
- {
- if(IS_DENORMAL(x1))
- x1 = 0.0f;
- if(IS_DENORMAL(x2))
- x2 = 0.0f;
- if(IS_DENORMAL(y1))
- y1 = 0.0f;
- if(IS_DENORMAL(y2))
- y2 = 0.0f;
- }
-
- __forceinline
- float process(float sample)
- {
- float result = a0 * sample + a[0] * x1 + a[1] * x2 +
- a[2] * y1 + a[3] * y2;
-
- x2 = x1;
- x1 = sample;
-
- y2 = y1;
- y1 = result;
-
- //Input for next stage
- return result;
- }
-
- __declspec(align(16)) float a[4];
- float a0;
-
- float x1, x2;
- float y1, y2;
-};
+#include "BiQuad.h"
struct ChannelData
{
@@ -109,4 +68,6 @@ private:
void* shutdownEvent;
stdext::hash_map channelNameToPosMap;
stdext::hash_map channelPosToNameMap;
+ stdext::hash_map filterNameToTypeMap;
+ stdext::hash_map filterTypeToDescriptionMap;
};