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:
1 parent
04d22adb07
commit
0895d84055
49 files changed
+2228
-267
No files matched your search
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user