279 lines
6.5 KiB
C++
279 lines
6.5 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 <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();
|
|
}
|
|
}
|