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.
762 lines
23 KiB
C++
762 lines
23 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 <sstream>
|
|
#include <QDrag>
|
|
#include <QElapsedTimer>
|
|
#include <QLabel>
|
|
#include <QMimeData>
|
|
#include <QPushButton>
|
|
#include <QStandardItemModel>
|
|
#include <QStringBuilder>
|
|
#include <QScrollArea>
|
|
#include <QFileInfo>
|
|
#include <QFileDialog>
|
|
#include <QMessageBox>
|
|
#include <QProcess>
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
|
|
#include "helpers/StringHelper.h"
|
|
#include "helpers/LogHelper.h"
|
|
#include "helpers/ChannelHelper.h"
|
|
#include "Editor/helpers/GUIChannelHelper.h"
|
|
#include "version.h"
|
|
#include "FilterTable.h"
|
|
#include "MainWindow.h"
|
|
#include "ui_MainWindow.h"
|
|
|
|
using namespace std;
|
|
|
|
MainWindow::MainWindow(QDir configDir, QWidget *parent) :
|
|
QMainWindow(parent), ui(new Ui::MainWindow), configDir(configDir)
|
|
{
|
|
outputDevices = QList<DeviceAPOInfo>::fromVector(QVector<DeviceAPOInfo>::fromStdVector(DeviceAPOInfo::loadAllInfos(false)));
|
|
inputDevices = QList<DeviceAPOInfo>::fromVector(QVector<DeviceAPOInfo>::fromStdVector(DeviceAPOInfo::loadAllInfos(true)));
|
|
|
|
defaultOutputDevice = NULL;
|
|
for(DeviceAPOInfo& apoInfo : outputDevices)
|
|
{
|
|
if(apoInfo.isDefaultDevice)
|
|
{
|
|
defaultOutputDevice = &apoInfo;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ui->setupUi(this);
|
|
|
|
LogHelper::set(stderr, true, false, false);
|
|
|
|
QString version = QString("%0.%1").arg(MAJOR).arg(MINOR);
|
|
if(REVISION != 0)
|
|
version += QString(".%0").arg(REVISION);
|
|
setWindowTitle(tr("Equalizer APO %0 Configuration Editor").arg(version));
|
|
|
|
QWidget* spacer = new QWidget;
|
|
spacer->setFixedWidth(10);
|
|
ui->mainToolBar->addWidget(spacer);
|
|
|
|
instantModeCheckBox = new QCheckBox(tr("Instant mode"));
|
|
instantModeCheckBox->setChecked(true);
|
|
instantModeCheckBox->setToolTip(tr("Changes are saved immediately"));
|
|
connect(instantModeCheckBox, SIGNAL(toggled(bool)), this, SLOT(instantModeEnabled(bool)));
|
|
ui->mainToolBar->addWidget(instantModeCheckBox);
|
|
|
|
spacer = new QWidget;
|
|
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
ui->mainToolBar->addWidget(spacer);
|
|
|
|
ui->mainToolBar->addWidget(new QLabel(tr("Device: ")));
|
|
|
|
deviceComboBox = new QComboBox;
|
|
connect(deviceComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(deviceSelected(int)));
|
|
ui->mainToolBar->addWidget(deviceComboBox);
|
|
|
|
spacer = new QWidget;
|
|
spacer->setFixedWidth(10);
|
|
ui->mainToolBar->addWidget(spacer);
|
|
|
|
ui->mainToolBar->addWidget(new QLabel(tr("Channel configuration: ")));
|
|
|
|
channelConfigurationComboBox = new QComboBox;
|
|
channelConfigurationComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
|
ui->mainToolBar->addWidget(channelConfigurationComboBox);
|
|
|
|
QStandardItemModel* model = qobject_cast<QStandardItemModel*>(deviceComboBox->model());
|
|
if(defaultOutputDevice != NULL)
|
|
deviceComboBox->addItem(tr("Default") + " (" + QString::fromStdWString(defaultOutputDevice->connectionName) + " - " + QString::fromStdWString(defaultOutputDevice->deviceName) + ")", NULL);
|
|
|
|
deviceComboBox->addItem(tr("Playback devices:"));
|
|
QStandardItem* item = model->item(model->rowCount() - 1);
|
|
QFont font = item->font();
|
|
font.setBold(true);
|
|
item->setFont(font);
|
|
item->setSelectable(false);
|
|
|
|
for(DeviceAPOInfo& apoInfo : outputDevices)
|
|
if(apoInfo.isInstalled)
|
|
deviceComboBox->addItem(QString::fromStdWString(apoInfo.connectionName) + " - " + QString::fromStdWString(apoInfo.deviceName), QVariant::fromValue(&apoInfo));
|
|
|
|
deviceComboBox->addItem(tr("Capture devices:"));
|
|
item = model->item(model->rowCount() - 1);
|
|
item->setFont(font);
|
|
item->setSelectable(false);
|
|
|
|
for(DeviceAPOInfo& apoInfo : inputDevices)
|
|
if(apoInfo.isInstalled)
|
|
deviceComboBox->addItem(QString::fromStdWString(apoInfo.connectionName) + " - " + QString::fromStdWString(apoInfo.deviceName), QVariant::fromValue(&apoInfo));
|
|
|
|
connect(channelConfigurationComboBox, SIGNAL(activated(int)), this, SLOT(channelConfigurationSelected(int)));
|
|
|
|
analysisPlotScene = new AnalysisPlotScene(ui->graphicsView);
|
|
ui->graphicsView->setScene(analysisPlotScene);
|
|
|
|
analysisThread = new AnalysisThread;
|
|
analysisThread->start();
|
|
connect(analysisThread, SIGNAL(analysisFinished()), this, SLOT(updateAnalysisPanel()));
|
|
}
|
|
|
|
MainWindow::~MainWindow()
|
|
{
|
|
delete ui;
|
|
|
|
delete analysisThread;
|
|
}
|
|
|
|
void MainWindow::doChecks()
|
|
{
|
|
if(!DeviceAPOInfo::checkProtectedAudioDG(false))
|
|
{
|
|
if(QMessageBox::warning(this, tr("Registry problem"), tr("A registry value that is required for the operation of Equalizer APO is not set correctly.\nDo you want to run the Configurator application to fix the problem?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
|
|
{
|
|
QProcess::startDetached(QCoreApplication::applicationDirPath() + "/Configurator.exe", QStringList());
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(!defaultOutputDevice->isInstalled)
|
|
{
|
|
if(QMessageBox::warning(this, tr("APO not installed to device"), tr("Equalizer APO has not been installed to the selected device.\nDo you want to run the Configurator application to fix the problem?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
|
|
{
|
|
QProcess::startDetached(QCoreApplication::applicationDirPath() + "/Configurator.exe", QStringList());
|
|
return;
|
|
}
|
|
}
|
|
|
|
DeviceAPOInfo* disabledApoInfo = NULL;
|
|
for(DeviceAPOInfo& apoInfo : outputDevices)
|
|
{
|
|
if(apoInfo.isInstalled && apoInfo.isEnhancementsDisabled)
|
|
{
|
|
disabledApoInfo = &apoInfo;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(disabledApoInfo == NULL)
|
|
{
|
|
for(DeviceAPOInfo& apoInfo : inputDevices)
|
|
{
|
|
if(apoInfo.isInstalled && apoInfo.isEnhancementsDisabled)
|
|
{
|
|
disabledApoInfo = &apoInfo;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(disabledApoInfo != NULL)
|
|
{
|
|
if(QMessageBox::warning(this, tr("Audio enhancements disabled"), tr("Audio enhancements are not enabled for the device\n%0 %1.\nDo you want to run the Configurator application to fix the problem?").arg(QString::fromStdWString(disabledApoInfo->connectionName)).arg(QString::fromStdWString(disabledApoInfo->deviceName)), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
|
|
{
|
|
QProcess::startDetached(QCoreApplication::applicationDirPath() + "/Configurator.exe", QStringList());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::load(QString path)
|
|
{
|
|
for(int i = 0; i < ui->tabWidget->count(); i++)
|
|
{
|
|
QScrollArea* scrollArea = qobject_cast<QScrollArea*>(ui->tabWidget->widget(i));
|
|
FilterTable* filterTable = qobject_cast<FilterTable*>(scrollArea->widget());
|
|
|
|
if(filterTable->getConfigPath() == path)
|
|
{
|
|
ui->tabWidget->setCurrentIndex(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
QElapsedTimer timer;
|
|
timer.start();
|
|
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
while(hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
hFile = CreateFile(path.toStdWString().c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if(hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD error = GetLastError();
|
|
if(error != ERROR_SHARING_VIOLATION)
|
|
{
|
|
QMessageBox::critical(this, tr("Error"), tr("Error while reading configuration file: %0").arg(QString::fromStdWString(StringHelper::getSystemErrorString(error))));
|
|
return;
|
|
}
|
|
|
|
// file is being written, so wait
|
|
Sleep(1);
|
|
}
|
|
}
|
|
|
|
stringstream inputStream;
|
|
|
|
char buf[8192];
|
|
unsigned long bytesRead = -1;
|
|
while(ReadFile(hFile, buf, sizeof(buf), &bytesRead, NULL) && bytesRead != 0)
|
|
{
|
|
inputStream.write(buf, bytesRead);
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
|
|
inputStream.seekg(0);
|
|
|
|
QList<QString> lines;
|
|
while(inputStream.good())
|
|
{
|
|
string encodedLine;
|
|
getline(inputStream, encodedLine);
|
|
if(encodedLine.size() > 0 && encodedLine[encodedLine.size() - 1] == '\r')
|
|
encodedLine.resize(encodedLine.size() - 1);
|
|
|
|
wstring line = StringHelper::toWString(encodedLine, CP_UTF8);
|
|
if(line.find(L'\uFFFD') != -1)
|
|
line = StringHelper::toWString(encodedLine, CP_ACP);
|
|
|
|
lines.append(QString::fromStdWString(line));
|
|
}
|
|
|
|
QFileInfo fileInfo(path);
|
|
FilterTable* filterTable = addTab(fileInfo.fileName(), QDir::toNativeSeparators(fileInfo.absoluteFilePath()));
|
|
|
|
filterTable->setLines(path, lines);
|
|
|
|
connect(filterTable, SIGNAL(linesChanged()), this, SLOT(linesChanged()));
|
|
|
|
qDebug("Loading took %.1f ms", timer.nsecsElapsed() / 1e6);
|
|
|
|
ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1);
|
|
}
|
|
|
|
void MainWindow::save(FilterTable* filterTable, QString path)
|
|
{
|
|
QElapsedTimer timer;
|
|
timer.start();
|
|
|
|
QList<QString> lines = filterTable->getLines();
|
|
|
|
bool first = true;
|
|
QByteArray byteArray;
|
|
for(QString line : lines)
|
|
{
|
|
if(first)
|
|
first = false;
|
|
else
|
|
byteArray.append("\r\n");
|
|
byteArray.append(line.toUtf8());
|
|
}
|
|
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
while(hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
hFile = CreateFile(path.toStdWString().c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if(hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD error = GetLastError();
|
|
if(error != ERROR_SHARING_VIOLATION)
|
|
{
|
|
QMessageBox::critical(this, tr("Error"), tr("Error while writing configuration file: %0").arg(QString::fromStdWString(StringHelper::getSystemErrorString(error))));
|
|
return;
|
|
}
|
|
|
|
// file is being written, so wait
|
|
Sleep(1);
|
|
}
|
|
}
|
|
|
|
unsigned long bytesWritten;
|
|
WriteFile(hFile, byteArray.constData(), byteArray.length(), &bytesWritten, NULL);
|
|
if(bytesWritten != byteArray.length())
|
|
{
|
|
// should never happen
|
|
QMessageBox::critical(this, tr("Error"), tr("Only %0/%1 bytes have been written!").arg(bytesWritten).arg(byteArray.length()));
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
|
|
qDebug("Saving took %.1f ms", timer.nsecsElapsed() / 1e6);
|
|
|
|
startAnalysis();
|
|
}
|
|
|
|
void MainWindow::closeEvent(QCloseEvent* event)
|
|
{
|
|
while(ui->tabWidget->count() > 0)
|
|
{
|
|
ui->tabWidget->setCurrentIndex(0);
|
|
|
|
if(!on_tabWidget_tabCloseRequested(0))
|
|
{
|
|
event->ignore();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::deviceSelected(int index)
|
|
{
|
|
DeviceAPOInfo* apoInfo = deviceComboBox->itemData(index).value<DeviceAPOInfo*>();
|
|
if(apoInfo == NULL)
|
|
apoInfo = defaultOutputDevice;
|
|
|
|
channelConfigurationComboBox->clear();
|
|
|
|
const QList<GUIChannelHelper::ChannelConfigurationInfo>& infos = GUIChannelHelper::getInstance()->getChannelConfigurationInfos();
|
|
|
|
if(apoInfo != NULL)
|
|
{
|
|
const GUIChannelHelper::ChannelConfigurationInfo* selectedInfo = NULL;
|
|
for(const GUIChannelHelper::ChannelConfigurationInfo& info : infos)
|
|
{
|
|
if(info.channelMask == apoInfo->channelMask)
|
|
{
|
|
selectedInfo = &info;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(selectedInfo != NULL)
|
|
channelConfigurationComboBox->addItem(tr("From device") + " (" + selectedInfo->name + ")", 0);
|
|
else
|
|
channelConfigurationComboBox->addItem(tr("From device") + " (" + apoInfo->channelCount + " channels)", 0);
|
|
}
|
|
|
|
for(const GUIChannelHelper::ChannelConfigurationInfo& info : infos)
|
|
channelConfigurationComboBox->addItem(info.name, info.channelMask);
|
|
|
|
channelConfigurationSelected(channelConfigurationComboBox->currentIndex());
|
|
}
|
|
|
|
void MainWindow::channelConfigurationSelected(int index)
|
|
{
|
|
DeviceAPOInfo* selectedDevice;
|
|
int channelMask;
|
|
getDeviceAndChannelMask(&selectedDevice, &channelMask);
|
|
|
|
for(int i = 0; i < ui->tabWidget->count(); i++)
|
|
{
|
|
QScrollArea* scrollArea = qobject_cast<QScrollArea*>(ui->tabWidget->widget(i));
|
|
FilterTable* filterTable = qobject_cast<FilterTable*>(scrollArea->widget());
|
|
filterTable->updateDeviceAndChannelMask(selectedDevice, channelMask);
|
|
}
|
|
|
|
ui->analysisChannelComboBox->clear();
|
|
|
|
if(selectedDevice != NULL)
|
|
{
|
|
unsigned channelCount = selectedDevice->channelCount;
|
|
if(channelMask != 0 && channelMask != selectedDevice->channelMask)
|
|
{
|
|
channelCount = 0;
|
|
for(int i = 0; i < 31; i++)
|
|
{
|
|
int channelPos = 1 << i;
|
|
if(channelMask & channelPos)
|
|
channelCount++;
|
|
}
|
|
}
|
|
|
|
vector<wstring> channelNames = ChannelHelper::getChannelNames(channelCount, channelMask);
|
|
for(wstring channelName : channelNames)
|
|
{
|
|
ui->analysisChannelComboBox->addItem(QString::fromStdWString(channelName));
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::linesChanged()
|
|
{
|
|
FilterTable* filterTable = qobject_cast<FilterTable*>(sender());
|
|
|
|
if(instantModeCheckBox->isChecked())
|
|
{
|
|
QString configPath = filterTable->getConfigPath();
|
|
if(configPath.length() > 0)
|
|
{
|
|
save(filterTable, configPath);
|
|
return;
|
|
}
|
|
}
|
|
|
|
int tabIndex = -1;
|
|
for(int i = 0; i < ui->tabWidget->count(); i++)
|
|
{
|
|
QScrollArea* scrollArea = qobject_cast<QScrollArea*>(ui->tabWidget->widget(i));
|
|
if(scrollArea->widget() == filterTable)
|
|
{
|
|
tabIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
QString tabText = ui->tabWidget->tabText(tabIndex);
|
|
if(!tabText.endsWith('*'))
|
|
{
|
|
tabText += '*';
|
|
ui->tabWidget->setTabText(tabIndex, tabText);
|
|
}
|
|
}
|
|
|
|
bool MainWindow::on_tabWidget_tabCloseRequested(int index)
|
|
{
|
|
if(ui->tabWidget->tabText(index).endsWith('*'))
|
|
{
|
|
ui->tabWidget->setCurrentIndex(index);
|
|
QString configPath = ui->tabWidget->tabToolTip(index);
|
|
QMessageBox messageBox;
|
|
messageBox.setWindowTitle(tr("Unsaved changes"));
|
|
messageBox.setText(tr("The configuration file %0 has unsaved changes.").arg(configPath));
|
|
messageBox.setInformativeText(tr("Do you want to save the changes before closing the file?"));
|
|
messageBox.setIcon(QMessageBox::Question);
|
|
messageBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
|
|
messageBox.setDefaultButton(QMessageBox::Save);
|
|
messageBox.setEscapeButton(QMessageBox::Cancel);
|
|
messageBox.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
|
|
int result = messageBox.exec();
|
|
|
|
switch(result)
|
|
{
|
|
case QMessageBox::Save:
|
|
ui->actionSave->trigger();
|
|
if(ui->tabWidget->tabText(index).endsWith('*'))
|
|
// saving was canceled
|
|
return false;
|
|
break;
|
|
case QMessageBox::Discard:
|
|
break;
|
|
case QMessageBox::Cancel:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ui->tabWidget->removeTab(index);
|
|
return true;
|
|
}
|
|
|
|
void MainWindow::on_actionOpen_triggered()
|
|
{
|
|
QScrollArea* scrollArea = qobject_cast<QScrollArea*>(ui->tabWidget->currentWidget());
|
|
QString path;
|
|
if(scrollArea != NULL)
|
|
{
|
|
FilterTable* filterTable = qobject_cast<FilterTable*>(scrollArea->widget());
|
|
if(filterTable->getConfigPath().length() > 0)
|
|
{
|
|
QFileInfo fileInfo(filterTable->getConfigPath());
|
|
path = fileInfo.absolutePath();
|
|
}
|
|
}
|
|
if(path.length() == 0)
|
|
path = configDir.absolutePath();
|
|
|
|
QFileDialog dialog(this, tr("Open file"), path, "*.txt");
|
|
dialog.setFileMode(QFileDialog::ExistingFiles);
|
|
dialog.setNameFilter(tr("E-APO configurations (*.txt)"));
|
|
|
|
if(dialog.exec() == QDialog::Accepted)
|
|
{
|
|
for(QString file : dialog.selectedFiles())
|
|
load(file);
|
|
}
|
|
}
|
|
|
|
void MainWindow::on_actionSave_triggered()
|
|
{
|
|
QScrollArea* scrollArea = qobject_cast<QScrollArea*>(ui->tabWidget->currentWidget());
|
|
if(scrollArea == NULL)
|
|
return;
|
|
|
|
FilterTable* filterTable = qobject_cast<FilterTable*>(scrollArea->widget());
|
|
|
|
if(filterTable->getConfigPath().length() == 0)
|
|
{
|
|
ui->actionSaveAs->trigger();
|
|
}
|
|
else
|
|
{
|
|
save(filterTable, filterTable->getConfigPath());
|
|
|
|
QString tabText = ui->tabWidget->tabText(ui->tabWidget->currentIndex());
|
|
if(tabText.endsWith('*'))
|
|
ui->tabWidget->setTabText(ui->tabWidget->currentIndex(), tabText.left(tabText.length() - 1));
|
|
}
|
|
}
|
|
|
|
void MainWindow::on_actionSaveAs_triggered()
|
|
{
|
|
QScrollArea* scrollArea = qobject_cast<QScrollArea*>(ui->tabWidget->currentWidget());
|
|
if(scrollArea == NULL)
|
|
return;
|
|
|
|
FilterTable* filterTable = qobject_cast<FilterTable*>(scrollArea->widget());
|
|
QString path;
|
|
QString filename;
|
|
if(filterTable->getConfigPath().length() == 0)
|
|
{
|
|
path = configDir.absolutePath();
|
|
}
|
|
else
|
|
{
|
|
QFileInfo fileInfo(filterTable->getConfigPath());
|
|
path = fileInfo.absolutePath();
|
|
filename = fileInfo.fileName();
|
|
}
|
|
QFileDialog dialog(this, tr("Save file as"), path, "*.txt");
|
|
dialog.setFileMode(QFileDialog::AnyFile);
|
|
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
|
dialog.setNameFilter(tr("E-APO configurations (*.txt)"));
|
|
dialog.setDefaultSuffix(".txt");
|
|
if(filename.length() > 0)
|
|
dialog.selectFile(filename);
|
|
|
|
if(dialog.exec() == QDialog::Accepted)
|
|
{
|
|
QString savePath = dialog.selectedFiles().first();
|
|
save(filterTable, savePath);
|
|
filterTable->setConfigPath(savePath);
|
|
|
|
QFileInfo fileInfo(savePath);
|
|
ui->tabWidget->setTabText(ui->tabWidget->currentIndex(), fileInfo.fileName());
|
|
ui->tabWidget->setTabToolTip(ui->tabWidget->currentIndex(), QDir::toNativeSeparators(fileInfo.absoluteFilePath()));
|
|
}
|
|
}
|
|
|
|
void MainWindow::on_actionNew_triggered()
|
|
{
|
|
FilterTable* filterTable = addTab(tr("Unsaved"), "");
|
|
filterTable->setLines("", QList<QString>());
|
|
|
|
connect(filterTable, SIGNAL(linesChanged()), this, SLOT(linesChanged()));
|
|
ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1);
|
|
}
|
|
|
|
void MainWindow::on_actionCut_triggered()
|
|
{
|
|
QScrollArea* scrollArea = qobject_cast<QScrollArea*>(ui->tabWidget->currentWidget());
|
|
if(scrollArea == NULL)
|
|
return;
|
|
|
|
FilterTable* filterTable = qobject_cast<FilterTable*>(scrollArea->widget());
|
|
filterTable->cut();
|
|
}
|
|
|
|
void MainWindow::on_actionCopy_triggered()
|
|
{
|
|
QScrollArea* scrollArea = qobject_cast<QScrollArea*>(ui->tabWidget->currentWidget());
|
|
if(scrollArea == NULL)
|
|
return;
|
|
|
|
FilterTable* filterTable = qobject_cast<FilterTable*>(scrollArea->widget());
|
|
filterTable->copy();
|
|
}
|
|
|
|
void MainWindow::on_actionPaste_triggered()
|
|
{
|
|
QScrollArea* scrollArea = qobject_cast<QScrollArea*>(ui->tabWidget->currentWidget());
|
|
if(scrollArea == NULL)
|
|
return;
|
|
|
|
FilterTable* filterTable = qobject_cast<FilterTable*>(scrollArea->widget());
|
|
filterTable->paste();
|
|
}
|
|
|
|
void MainWindow::on_actionDelete_triggered()
|
|
{
|
|
QScrollArea* scrollArea = qobject_cast<QScrollArea*>(ui->tabWidget->currentWidget());
|
|
if(scrollArea == NULL)
|
|
return;
|
|
|
|
FilterTable* filterTable = qobject_cast<FilterTable*>(scrollArea->widget());
|
|
filterTable->deleteSelectedLines();
|
|
}
|
|
|
|
void MainWindow::instantModeEnabled(bool enabled)
|
|
{
|
|
if(enabled)
|
|
{
|
|
for(int i = 0; i < ui->tabWidget->count(); i++)
|
|
{
|
|
QScrollArea* scrollArea = qobject_cast<QScrollArea*>(ui->tabWidget->widget(i));
|
|
FilterTable* filterTable = qobject_cast<FilterTable*>(scrollArea->widget());
|
|
|
|
if(filterTable->getConfigPath().length() > 0)
|
|
{
|
|
save(filterTable, filterTable->getConfigPath());
|
|
|
|
QString tabText = ui->tabWidget->tabText(i);
|
|
if(tabText.endsWith('*'))
|
|
ui->tabWidget->setTabText(i, tabText.left(tabText.length() - 1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::on_tabWidget_currentChanged(int index)
|
|
{
|
|
startAnalysis();
|
|
}
|
|
|
|
void MainWindow::on_startFromComboBox_activated(int index)
|
|
{
|
|
startAnalysis();
|
|
}
|
|
|
|
void MainWindow::on_analysisChannelComboBox_activated(int index)
|
|
{
|
|
startAnalysis();
|
|
}
|
|
|
|
void MainWindow::on_resolutionSpinBox_valueChanged(int value)
|
|
{
|
|
startAnalysis();
|
|
}
|
|
|
|
void MainWindow::updateAnalysisPanel()
|
|
{
|
|
analysisThread->beginGetResult();
|
|
int sampleRate = analysisThread->getFreqDataSampleRate();
|
|
int latency = analysisThread->getLatency();
|
|
analysisPlotScene->setFreqData(analysisThread->getFreqData(), analysisThread->getFreqDataLength(), sampleRate);
|
|
|
|
double peakGain = analysisThread->getPeakGain();
|
|
ui->peakGainValueLabel->setText(tr("%0 dB").arg(peakGain, 0, 'f', 1));
|
|
ui->peakGainValueLabel->setForegroundRole(peakGain > 0 ? QPalette::Dark : QPalette::WindowText);
|
|
|
|
ui->latencyValueLabel->setText(tr("%0 ms (%1 samples)").arg(latency * 1000.0 / sampleRate).arg(latency));
|
|
|
|
ui->initTimeValueLabel->setText(tr("%0 ms").arg(analysisThread->getInitializationTime(), 0, 'f', 1));
|
|
|
|
double cpuUsage = analysisThread->getProcessingTime() * 100.0 / (analysisThread->getProcessedFrames() * 1000.0 / sampleRate);
|
|
ui->cpuUsageValueLabel->setText(tr("%0 % (one core)").arg(cpuUsage, 0, 'f', 1));
|
|
ui->cpuUsageValueLabel->setForegroundRole(cpuUsage >= 20 ? (cpuUsage >= 50 ? QPalette::Dark : QPalette::Midlight) : QPalette::WindowText);
|
|
|
|
analysisThread->endGetResult();
|
|
}
|
|
|
|
void MainWindow::on_mainToolBar_visibilityChanged(bool visible)
|
|
{
|
|
ui->actionToolbar->setChecked(visible);
|
|
}
|
|
|
|
void MainWindow::on_analysisDockWidget_visibilityChanged(bool visible)
|
|
{
|
|
ui->actionAnalysisPanel->setChecked(visible);
|
|
}
|
|
|
|
void MainWindow::on_actionToolbar_triggered(bool checked)
|
|
{
|
|
ui->mainToolBar->setVisible(checked);
|
|
}
|
|
|
|
void MainWindow::on_actionAnalysisPanel_triggered(bool checked)
|
|
{
|
|
ui->analysisDockWidget->setVisible(checked);
|
|
}
|
|
|
|
FilterTable* MainWindow::addTab(QString title, QString tooltip)
|
|
{
|
|
QScrollArea* scrollArea = new QScrollArea(ui->tabWidget);
|
|
scrollArea->setWidgetResizable(true);
|
|
FilterTable* filterTable = new FilterTable(this);
|
|
scrollArea->setWidget(filterTable);
|
|
filterTable->setAcceptDrops(true);
|
|
filterTable->setFocusPolicy(Qt::WheelFocus);
|
|
|
|
int tabIndex = ui->tabWidget->addTab(scrollArea, title);
|
|
ui->tabWidget->setTabToolTip(tabIndex, tooltip);
|
|
|
|
DeviceAPOInfo* selectedDevice;
|
|
int channelMask;
|
|
getDeviceAndChannelMask(&selectedDevice, &channelMask);
|
|
filterTable->updateDeviceAndChannelMask(selectedDevice, channelMask);
|
|
filterTable->initialize(scrollArea, outputDevices, inputDevices);
|
|
|
|
return filterTable;
|
|
}
|
|
|
|
void MainWindow::getDeviceAndChannelMask(DeviceAPOInfo** selectedDevice, int* channelMask)
|
|
{
|
|
*selectedDevice = deviceComboBox->currentData().value<DeviceAPOInfo*>();
|
|
if(*selectedDevice == NULL)
|
|
*selectedDevice = defaultOutputDevice;
|
|
|
|
*channelMask = channelConfigurationComboBox->currentData().toInt();
|
|
if(*channelMask == 0 && selectedDevice != NULL)
|
|
{
|
|
*channelMask = (*selectedDevice)->channelMask;
|
|
|
|
if(*channelMask == 0)
|
|
*channelMask = ChannelHelper::getDefaultChannelMask((*selectedDevice)->channelCount);
|
|
}
|
|
}
|
|
|
|
void MainWindow::startAnalysis()
|
|
{
|
|
DeviceAPOInfo* selectedDevice;
|
|
|
|
int channelMask;
|
|
getDeviceAndChannelMask(&selectedDevice, &channelMask);
|
|
|
|
if(selectedDevice != NULL)
|
|
{
|
|
QString configPath;
|
|
|
|
if(ui->startFromComboBox->currentIndex() == 1)
|
|
{
|
|
QScrollArea* scrollArea = qobject_cast<QScrollArea*>(ui->tabWidget->currentWidget());
|
|
if(scrollArea != NULL)
|
|
{
|
|
FilterTable* filterTable = qobject_cast<FilterTable*>(scrollArea->widget());
|
|
|
|
if(filterTable->getConfigPath().length() > 0)
|
|
configPath = filterTable->getConfigPath();
|
|
}
|
|
}
|
|
|
|
if(configPath.isEmpty())
|
|
configPath = configDir.absoluteFilePath("config.txt");
|
|
configPath = QDir::toNativeSeparators(configPath);
|
|
|
|
analysisThread->setParameters(selectedDevice, channelMask, ui->analysisChannelComboBox->currentIndex(), configPath, ui->resolutionSpinBox->value());
|
|
}
|
|
}
|