Files
EqualizerAPO/filters/GraphicEQFilter.cpp

188 lines
5.1 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_make_planner_thread_safe();
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]));
}
}