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; };