Added: Analysis panel which shows the calculated frequency response of the filters for the selected device and channel.

Improved: GUIs of GraphicEQ and Copy command can now be resized
Improved: GraphicEQ GUI can now be zoomed separately in X and Y direction using mouse wheel on rulers. Also now shows values at mouse position.
Fixed: Original APO was not released when E-APO was shutting down, leading to a memory leak.
This commit is contained in:
jthedering committed 2015-11-07 19:00:49 +00:00
1 parent 04d22adb07
commit 0895d84055
49 files changed
+2228 -267

No files matched your search

+278
View File
@@ -0,0 +1,278 @@
/*
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 <QElapsedTimer>
#include "FilterEngine.h"
#include "AnalysisThread.h"
AnalysisThread::AnalysisThread()
{
}
AnalysisThread::~AnalysisThread()
{
mutex.lock();
quit = true;
condition.wakeAll();
mutex.unlock();
wait();
if(resultFreqData != NULL)
fftwf_free(resultFreqData);
if(buf != NULL)
delete buf;
if(buf2 != NULL)
delete buf2;
if(timeData != NULL)
fftwf_free(timeData);
if(freqData != NULL)
fftwf_free(freqData);
if(planForward != NULL)
fftwf_destroy_plan(planForward);
}
void AnalysisThread::setParameters(DeviceAPOInfo* device, int channelMask, int channelIndex, QString configPath, int frameCount)
{
QMutexLocker mutexLocker(&mutex);
this->device = *device;
this->channelMask = channelMask;
this->channelIndex = channelIndex;
this->configPath = configPath;
this->frameCount = frameCount;
condition.wakeAll();
}
void AnalysisThread::beginGetResult()
{
mutex.lock();
}
void AnalysisThread::endGetResult()
{
mutex.unlock();
}
fftwf_complex* AnalysisThread::getFreqData() const
{
return resultFreqData;
}
int AnalysisThread::getFreqDataLength() const
{
return freqDataLength;
}
int AnalysisThread::getFreqDataSampleRate() const
{
return freqDataSampleRate;
}
double AnalysisThread::getPeakGain() const
{
return peakGain;
}
int AnalysisThread::getLatency() const
{
return latency;
}
double AnalysisThread::getInitializationTime() const
{
return initializationTime;
}
double AnalysisThread::getProcessingTime() const
{
return processingTime;
}
int AnalysisThread::getProcessedFrames() const
{
return processedFrames;
}
void AnalysisThread::run()
{
while(true)
{
mutex.lock();
if(!quit && this->frameCount == 0)
condition.wait(&mutex);
if(quit)
{
mutex.unlock();
break;
}
DeviceAPOInfo device = this->device;
int channelMask = this->channelMask;
int channelIndex = this->channelIndex;
QString configPath = this->configPath;
int frameCount = this->frameCount;
this->frameCount = 0;
mutex.unlock();
QElapsedTimer timer;
timer.start();
unsigned channelCount = device.channelCount;
if(channelMask != 0 && channelMask != device.channelMask)
{
channelCount = 0;
for(int i = 0; i < 31; i++)
{
int channelPos = 1 << i;
if(channelMask & channelPos)
channelCount++;
}
}
unsigned sampleRate = device.sampleRate;
qint64 startTime = timer.nsecsElapsed();
FilterEngine engine;
engine.setDeviceInfo(device.isInput, true, device.deviceName, device.connectionName, device.deviceGuid);
engine.initialize(sampleRate, channelCount, channelCount, channelCount, channelMask, frameCount, configPath.toStdWString());
double initializationTime = (timer.nsecsElapsed() - startTime) / 1e6;
if(frameCount != lastFrameCount || channelCount != lastChannelCount)
{
if(buf != NULL)
delete buf;
buf = new float[frameCount * channelCount];
memset(buf, 0, frameCount * channelCount * sizeof(float));
if(buf2 != NULL)
delete buf2;
buf2 = new float[frameCount * channelCount];
}
for(unsigned i = 0; i < channelCount; i++)
buf[i] = 1.0f;
if(frameCount != lastFrameCount)
{
if(timeData != NULL)
fftwf_free(timeData);
timeData = fftwf_alloc_real(frameCount);
if(freqData != NULL)
fftwf_free(freqData);
freqData = fftwf_alloc_complex(frameCount);
if(planForward != NULL)
fftwf_destroy_plan(planForward);
planForward = fftwf_plan_dft_r2c_1d(frameCount, timeData, freqData, FFTW_ESTIMATE);
}
lastFrameCount = frameCount;
lastChannelCount = channelCount;
int latency = 0;
int startFrame = -1;
double processingTime = 0.0;
int processedFrames = 0;
while(true)
{
qint64 startTime = timer.nsecsElapsed();
engine.process(buf2, buf, frameCount);
processingTime += (timer.nsecsElapsed() - startTime) / 1e6;
processedFrames += frameCount;
if(startFrame != -1)
{
for(int i = 0; i < startFrame; i++)
{
timeData[frameCount - startFrame + i] = buf2[i * channelCount + channelIndex];
}
break;
}
for(int i = 0; i < frameCount; i++)
{
float s = buf2[i * channelCount + channelIndex];
if(abs(s) > 1e-5f)
{
startFrame = i;
break;
}
}
if(startFrame != -1)
{
for(int i = 0; i < frameCount - startFrame; i++)
{
timeData[i] = buf2[(startFrame + i) * channelCount + channelIndex];
}
if(startFrame == 0)
break;
}
if(latency == 0)
{
for(unsigned i = 0; i < channelCount; i++)
buf[i] = 0.0f;
}
if(startFrame == -1)
latency += frameCount;
}
latency += startFrame;
fftwf_execute(planForward);
double peakGain = -DBL_MAX;
for(int i = 0; i < frameCount; i++)
{
float sqrGain = freqData[i][0] * freqData[i][0] + freqData[i][1] * freqData[i][1];
if(sqrGain > peakGain)
peakGain = sqrGain;
}
peakGain = sqrt(peakGain);
peakGain = log10(peakGain) * 20.0;
mutex.lock();
if(this->freqDataLength != frameCount)
{
if(resultFreqData != NULL)
fftwf_free(resultFreqData);
resultFreqData = fftwf_alloc_complex(frameCount);
}
memcpy(resultFreqData, freqData, frameCount * sizeof(fftwf_complex));
this->freqDataLength = frameCount;
this->freqDataSampleRate = sampleRate;
this->latency = latency;
this->peakGain = peakGain;
this->initializationTime = initializationTime;
this->processingTime = processingTime;
this->processedFrames = processedFrames;
mutex.unlock();
qDebug("Analysis took %.1f ms", timer.nsecsElapsed() / 1e6);
emit analysisFinished();
}
}