/* 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 #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(); } }