Files
EqualizerAPO/filters/GraphicEQFilter.cpp
T
jthedering b8cdd47f71 Version 1.0
Added: Configuration Editor, which allows to edit configurations in a graphical user interface. Contains GUIs for most commands supported by E-APO, but as lines can also be edited directly, all commands can be used.
Added: Command "GraphicEQ", which can act as a regular, fixed-band graphic equalizer (via the GUI) but also supports variable bands so that any desired frequency response can be specified. Internally, it is implemented via convolution with a generated IR.
Added: Command "Convolution", which allows to convolve the signal with a user-provided impulse response to achieve e.g. equalization or reverberation effects.
Improved: Configurator performs checks for registry values that are needed for the operation of E-APO, which may be changed by driver installations, fixing the values if necessary.
Improved: Configurator shows which is the default device as a hint to the user.
2015-09-27 16:01:21 +00:00

187 lines
4.9 KiB
C++

/*
This file is part of EqualizerAPO, a system-wide equalizer.
Copyright (C) 2015 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 "stdafx.h"
#define _USE_MATH_DEFINES
#include <cmath>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1
#include <sndfile.h>
#include "helpers/LogHelper.h"
#include "helpers/MemoryHelper.h"
#include "GraphicEQFilter.h"
using namespace std;
GraphicEQFilter::GraphicEQFilter(const std::vector<FilterNode>& nodes, unsigned filterLength)
:nodes(nodes), filterLength(filterLength)
{
filters = NULL;
}
GraphicEQFilter::~GraphicEQFilter()
{
cleanup();
}
vector<wstring> GraphicEQFilter::initialize(float sampleRate, unsigned maxFrameCount, vector<wstring> channelNames)
{
cleanup();
channelCount = (unsigned)channelNames.size();
fftwf_complex* timeData = fftwf_alloc_complex(filterLength * 2);
fftwf_complex* freqData = fftwf_alloc_complex(filterLength * 2);
fftwf_plan planForward = fftwf_plan_dft_1d(filterLength * 2, timeData, freqData, FFTW_FORWARD, FFTW_ESTIMATE);
fftwf_plan planReverse = fftwf_plan_dft_1d(filterLength * 2, freqData, timeData, FFTW_BACKWARD, FFTW_ESTIMATE);
GainIterator gainIterator(nodes);
for(unsigned i=0; i<filterLength; i++)
{
double freq = i*1.0*sampleRate/(filterLength * 2);
double dbGain = gainIterator.gainAt(freq);
float gain = (float)pow(10.0, dbGain / 20.0);
freqData[i][0] = gain;
freqData[i][1] = 0;
freqData[2*filterLength-i-1][0] = gain;
freqData[2*filterLength-i-1][1] = 0;
}
mps(timeData, freqData, planForward, planReverse);
fftwf_execute(planReverse);
for(unsigned i=0; i<2*filterLength; i++)
{
timeData[i][0] /= 2*filterLength;
timeData[i][1] /= 2*filterLength;
}
for(unsigned i=0; i<filterLength; i++)
{
float factor=(float)(0.5*(1+cos(2*M_PI*i*1.0/(2*filterLength))));
timeData[i][0] *= factor;
timeData[i][1] *= factor;
}
float* buf = new float[filterLength];
for(unsigned i=0; i<filterLength; i++)
{
buf[i] = timeData[i][0];
}
fftwf_free(timeData);
fftwf_free(freqData);
fftwf_destroy_plan(planForward);
fftwf_destroy_plan(planReverse);
filters = (HConvSingle*)MemoryHelper::alloc(sizeof(HConvSingle) * channelCount);
for(unsigned i=0; i<channelCount; i++)
{
hcInitSingle(&filters[i], buf, filterLength, maxFrameCount, 1);
}
delete buf;
return channelNames;
}
#pragma AVRT_CODE_BEGIN
void GraphicEQFilter::process(float** output, float** input, unsigned frameCount)
{
if(filters == NULL)
return;
for(unsigned i=0; i<channelCount; i++)
{
float* inputChannel = input[i];
float* outputChannel = output[i];
HConvSingle* filter = &filters[i];
hcPutSingle(filter, inputChannel);
hcProcessSingle(filter);
hcGetSingle(filter, outputChannel);
}
}
#pragma AVRT_CODE_END
const std::vector<FilterNode>& GraphicEQFilter::getNodes()
{
return nodes;
}
void GraphicEQFilter::cleanup()
{
if(filters != NULL)
{
for(unsigned i=0; i<channelCount; i++)
hcCloseSingle(&filters[i]);
MemoryHelper::free(filters);
filters = NULL;
}
}
// Minimum phase spectrum from coefficients
void GraphicEQFilter::mps(fftwf_complex* timeData, fftwf_complex* freqData, fftwf_plan planForward, fftwf_plan planReverse)
{
double threshold=pow(10.0, -100.0/20.0);
float logThreshold=(float)log(threshold);
for(unsigned i=0; i<filterLength * 2; i++)
{
if(freqData[i][0] < threshold)
freqData[i][0] = logThreshold;
else
freqData[i][0] = log(freqData[i][0]);
freqData[i][1]=0;
}
fftwf_execute(planReverse);
for(unsigned i=0; i<filterLength * 2; i++)
{
timeData[i][0] /= filterLength * 2;
timeData[i][1] /= filterLength * 2;
}
for(unsigned i=1; i<filterLength; i++)
{
timeData[i][0] += timeData[filterLength*2 - i][0];
timeData[i][1] -= timeData[filterLength*2 - i][1];
timeData[filterLength*2 - i][0] = 0;
timeData[filterLength*2 - i][1] = 0;
}
timeData[filterLength][1] *= -1;
fftwf_execute(planForward);
for(unsigned i=0; i<filterLength * 2; i++)
{
double eR=exp(freqData[i][0]);
freqData[i][0]=float(eR*cos(freqData[i][1]));
freqData[i][1]=float(eR*sin(freqData[i][1]));
}
}