2021-04-29 19:26:48 +02:00
|
|
|
#include "mainwindow.h"
|
|
|
|
|
|
|
|
// system includes
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
// Qt includes
|
|
|
|
#include <QMetaEnum>
|
|
|
|
#include <QJsonObject>
|
|
|
|
#include <QJsonArray>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QAudioInput>
|
|
|
|
#include <QJsonDocument>
|
2021-06-24 20:00:06 +02:00
|
|
|
#include <QDir>
|
|
|
|
#include <QMessageBox>
|
2021-04-29 19:26:48 +02:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
template<typename QEnum>
|
|
|
|
QString enumToString(const QEnum value)
|
|
|
|
{
|
|
|
|
return QMetaEnum::fromType<QEnum>().valueToKey(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-24 20:00:06 +02:00
|
|
|
MainWindow::MainWindow(R3CtlSettings &settings, QWidget *parent) :
|
|
|
|
QMainWindow{parent},
|
|
|
|
m_settings{settings}
|
2021-04-29 19:26:48 +02:00
|
|
|
{
|
|
|
|
m_ui.setupUi(this);
|
|
|
|
|
|
|
|
connect(m_ui.connectButton, &QAbstractButton::pressed, this, &MainWindow::connectPressed);
|
|
|
|
connect(m_ui.disconnectButton, &QAbstractButton::pressed, this, &MainWindow::disconnectPressed);
|
|
|
|
|
|
|
|
connect(m_ui.lamp1OnButton, &QAbstractButton::pressed, this, [&](){ lightCmd("basiclight1", "on"); });
|
|
|
|
connect(m_ui.lamp1OffButton, &QAbstractButton::pressed, this, [&](){ lightCmd("basiclight1", "off"); });
|
|
|
|
connect(m_ui.lamp2OnButton, &QAbstractButton::pressed, this, [&](){ lightCmd("basiclight2", "on"); });
|
|
|
|
connect(m_ui.lamp2OffButton, &QAbstractButton::pressed, this, [&](){ lightCmd("basiclight2", "off"); });
|
|
|
|
connect(m_ui.lamp3OnButton, &QAbstractButton::pressed, this, [&](){ lightCmd("basiclight3", "on"); });
|
|
|
|
connect(m_ui.lamp3OffButton, &QAbstractButton::pressed, this, [&](){ lightCmd("basiclight3", "off"); });
|
|
|
|
connect(m_ui.lamp4OnButton, &QAbstractButton::pressed, this, [&](){ lightCmd("basiclight4", "on"); });
|
|
|
|
connect(m_ui.lamp4OffButton, &QAbstractButton::pressed, this, [&](){ lightCmd("basiclight4", "off"); });
|
|
|
|
connect(m_ui.lamp5OnButton, &QAbstractButton::pressed, this, [&](){ lightCmd("basiclight5", "on"); });
|
|
|
|
connect(m_ui.lamp5OffButton, &QAbstractButton::pressed, this, [&](){ lightCmd("basiclight5", "off"); });
|
|
|
|
connect(m_ui.lamp6OnButton, &QAbstractButton::pressed, this, [&](){ lightCmd("basiclight6", "on"); });
|
|
|
|
connect(m_ui.lamp6OffButton, &QAbstractButton::pressed, this, [&](){ lightCmd("basiclight6", "off"); });
|
|
|
|
|
|
|
|
connect(m_ui.toggleAllButton, &QAbstractButton::pressed, this, &MainWindow::toggleAllPressed);
|
|
|
|
connect(m_ui.discoLeftButton, &QAbstractButton::pressed, this, &MainWindow::discoLeftPressed);
|
|
|
|
connect(m_ui.discoRightButton, &QAbstractButton::pressed, this, &MainWindow::discoRightPressed);
|
|
|
|
|
|
|
|
connect(m_ui.bpmButton, &QAbstractButton::pressed, this, &MainWindow::bpmPressed);
|
|
|
|
|
|
|
|
connect(m_ui.openAudioDeviceButton, &QAbstractButton::pressed, this, &MainWindow::openAudioDevicePressed);
|
|
|
|
connect(m_ui.closeAudioDeviceButton, &QAbstractButton::pressed, this, &MainWindow::closeAudioDevicePressed);
|
|
|
|
|
|
|
|
connect(&m_client, &R3Client::connected, this, &MainWindow::connected);
|
|
|
|
connect(&m_client, &R3Client::disconnected, this, &MainWindow::disconnected);
|
|
|
|
connect(&m_client, &R3Client::error, this, &MainWindow::error);
|
|
|
|
connect(&m_client, &R3Client::statusReceived, this, &MainWindow::statusReceived);
|
|
|
|
|
|
|
|
updateAudioDevices();
|
|
|
|
|
|
|
|
m_timer.setTimerType(Qt::PreciseTimer);
|
|
|
|
connect(&m_timer, &QTimer::timeout, this, &MainWindow::bpmTimeout);
|
|
|
|
|
|
|
|
connect(m_ui.spinBox, &QSpinBox::valueChanged, &m_timer, qOverload<int>(&QTimer::setInterval));
|
|
|
|
|
|
|
|
connect(&m_detector, &BpmDetector::receivedFrames, this, &MainWindow::receivedFrames);
|
|
|
|
|
|
|
|
connect(&m_timer2, &QTimer::timeout, this, [&](){
|
|
|
|
const sample_t min = -m_min * 100;
|
|
|
|
m_ui.verticalSlider->setValue(min);
|
|
|
|
m_min = 0;
|
|
|
|
|
|
|
|
const sample_t max = m_max * 100;
|
|
|
|
m_ui.verticalSlider_2->setValue(max);
|
|
|
|
m_max = 0;
|
|
|
|
|
|
|
|
const sample_t avg = m_distanceSum / m_count * 100.;
|
|
|
|
m_ui.verticalSlider_3->setValue(avg);
|
|
|
|
m_distanceSum = 0.;
|
|
|
|
m_count = 0;
|
|
|
|
|
|
|
|
const auto selectedWert = [&](){
|
|
|
|
if (m_ui.radioButton->isChecked())
|
|
|
|
return min;
|
|
|
|
else if (m_ui.radioButton_2->isChecked())
|
|
|
|
return max;
|
|
|
|
else if (m_ui.radioButton_3->isChecked())
|
|
|
|
return avg;
|
|
|
|
}();
|
|
|
|
|
|
|
|
if (selectedWert > m_ui.verticalSlider_4->value())
|
|
|
|
switch (m_ui.comboBox->currentIndex())
|
|
|
|
{
|
|
|
|
case 0: discoLeftPressed(); break;
|
|
|
|
case 1: discoRightPressed(); break;
|
|
|
|
case 2: toggleAllPressed(); break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
m_timer2.start(1000/20);
|
2021-06-24 20:00:06 +02:00
|
|
|
|
|
|
|
m_ui.themeComboBox->addItem(tr("Default"), QString());
|
|
|
|
|
|
|
|
for(const auto &entry : QDir{QDir{QCoreApplication::applicationDirPath()}.absoluteFilePath(QStringLiteral("themes"))}.entryInfoList(QStringList { QStringLiteral("*.qss") }, QDir::Files))
|
|
|
|
m_ui.themeComboBox->addItem(entry.baseName(), entry.baseName());
|
|
|
|
|
|
|
|
if(!m_settings.theme().isEmpty())
|
|
|
|
{
|
|
|
|
auto index = m_ui.themeComboBox->findData(m_settings.theme());
|
|
|
|
if(index == -1)
|
|
|
|
QMessageBox::warning(this, tr("Invalid settings!"), tr("Invalid settings!") + "\n\n" + tr("Unknown theme!"));
|
|
|
|
m_ui.themeComboBox->setCurrentIndex(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
connect(m_ui.themeComboBox, &QComboBox::currentIndexChanged, this, &MainWindow::themeSelected);
|
2021-04-29 19:26:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
MainWindow::~MainWindow() = default;
|
|
|
|
|
|
|
|
void MainWindow::connectPressed()
|
|
|
|
{
|
|
|
|
log("Connecting...");
|
|
|
|
m_client.open();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::disconnectPressed()
|
|
|
|
{
|
|
|
|
log("Disconnecting...");
|
|
|
|
m_client.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::connected()
|
|
|
|
{
|
|
|
|
log("Connected successfully!");
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::disconnected()
|
|
|
|
{
|
|
|
|
log("Disconnected!");
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::error(QAbstractSocket::SocketError error)
|
|
|
|
{
|
|
|
|
log(QString{"Error: %0"}.arg(enumToString(error)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::statusReceived(const QString &ctx, const QJsonValue &jsonValue)
|
|
|
|
{
|
|
|
|
qDebug() << jsonValue;
|
|
|
|
log(QString{"ctx=%0"}.arg(ctx));
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::toggleAllPressed()
|
|
|
|
{
|
|
|
|
lightCmd("basiclight1", m_toggleState ? "on" : "off");
|
|
|
|
lightCmd("basiclight2", m_toggleState ? "on" : "off");
|
|
|
|
lightCmd("basiclight3", m_toggleState ? "on" : "off");
|
|
|
|
lightCmd("basiclight4", m_toggleState ? "on" : "off");
|
|
|
|
lightCmd("basiclight5", m_toggleState ? "on" : "off");
|
|
|
|
lightCmd("basiclight6", m_toggleState ? "on" : "off");
|
|
|
|
m_toggleState = !m_toggleState;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::discoLeftPressed()
|
|
|
|
{
|
|
|
|
if (m_discoState == 0)
|
|
|
|
m_discoState = 5;
|
|
|
|
else
|
|
|
|
m_discoState--;
|
|
|
|
updateDisco();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::discoRightPressed()
|
|
|
|
{
|
|
|
|
if (m_discoState == 5)
|
|
|
|
m_discoState = 0;
|
|
|
|
else
|
|
|
|
m_discoState++;
|
|
|
|
updateDisco();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::bpmPressed()
|
|
|
|
{
|
|
|
|
discoRightPressed();
|
|
|
|
|
|
|
|
if (m_bpmTap)
|
|
|
|
{
|
|
|
|
m_bpmTap->count++;
|
|
|
|
const auto elapsed = m_bpmTap->begin.msecsTo(QDateTime::currentDateTime());
|
|
|
|
m_bpmTap->msPerBeat = elapsed / m_bpmTap->count;
|
|
|
|
m_bpmTap->bpm = 60000 / m_bpmTap->msPerBeat;
|
|
|
|
m_ui.bpmButton->setText(QString{"%0 %1 BPM"}.arg(m_bpmTap->count).arg(m_bpmTap->bpm));
|
|
|
|
m_ui.spinBox->setValue(m_bpmTap->msPerBeat);
|
|
|
|
m_timer.start(m_bpmTap->msPerBeat);
|
|
|
|
m_bpmTap->flag = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_timer.start(2000);
|
|
|
|
m_bpmTap = BpmTap{};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::openAudioDevicePressed()
|
|
|
|
{
|
|
|
|
const auto index = m_ui.audioDeviceSelection->currentIndex();
|
|
|
|
if (index < 0 || index >= m_devices.count())
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_input = std::make_unique<QAudioInput>(m_devices.at(index), makeFormat());
|
|
|
|
m_input->start(&m_detector);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::closeAudioDevicePressed()
|
|
|
|
{
|
|
|
|
m_input = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::bpmTimeout()
|
|
|
|
{
|
|
|
|
if (m_bpmTap)
|
|
|
|
{
|
|
|
|
if (!m_bpmTap->flag)
|
|
|
|
{
|
|
|
|
m_bpmTap->flag = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_bpmTap->count)
|
|
|
|
{
|
|
|
|
qDebug() << "starting normal";
|
|
|
|
discoRightPressed();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_ui.bpmButton->setText(tr("BPM tap"));
|
|
|
|
qDebug() << "aborted";
|
|
|
|
m_timer.stop();
|
|
|
|
}
|
|
|
|
m_bpmTap = std::nullopt;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
qDebug() << "disco timer";
|
|
|
|
discoRightPressed();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::receivedFrames(const QVector<frame_t> &frames)
|
|
|
|
{
|
|
|
|
for (const auto &frame : frames)
|
|
|
|
{
|
|
|
|
if (frame[0] < m_min)
|
|
|
|
m_min = frame[0];
|
|
|
|
|
|
|
|
if (frame[0] > m_max)
|
|
|
|
m_max = frame[0];
|
|
|
|
|
|
|
|
m_distanceSum += std::abs(frame[0]);
|
|
|
|
m_count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-24 20:00:06 +02:00
|
|
|
void MainWindow::themeSelected()
|
|
|
|
{
|
|
|
|
if (m_ui.comboBox->currentIndex() == -1)
|
|
|
|
{
|
|
|
|
qWarning() << "invalid theme selected";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto theme = m_ui.themeComboBox->currentData().toString();
|
|
|
|
if (theme == m_settings.theme())
|
|
|
|
return;
|
|
|
|
|
|
|
|
QString styleSheet;
|
|
|
|
if(!theme.isEmpty())
|
|
|
|
{
|
|
|
|
auto themePath = QDir{QDir{QCoreApplication::applicationDirPath()}.absoluteFilePath(QStringLiteral("themes"))}.absoluteFilePath(theme);
|
|
|
|
|
|
|
|
QFile file{themePath + ".qss"};
|
|
|
|
|
|
|
|
if(!file.exists())
|
|
|
|
{
|
|
|
|
QMessageBox::warning(this, tr("Could not load theme!"), tr("Could not load theme!") + "\n\n" + tr("Theme file does not exist!"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
|
|
|
{
|
|
|
|
QMessageBox::warning(this, tr("Could not load theme!"), tr("Could not load theme!") + "\n\n" + file.errorString());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QTextStream textStream(&file);
|
|
|
|
styleSheet = textStream.readAll().replace(QStringLiteral("@THEME_RESOURCES@"), themePath);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!m_settings.setTheme(theme))
|
|
|
|
QMessageBox::warning(this, tr("Could not save theme!"), tr("Could not save theme!"));
|
|
|
|
|
|
|
|
qApp->setStyleSheet(styleSheet);
|
|
|
|
}
|
|
|
|
|
2021-04-29 19:26:48 +02:00
|
|
|
void MainWindow::log(const QString &msg)
|
|
|
|
{
|
|
|
|
m_ui.logView->appendPlainText(QString{"%0 %1"}.arg(QDateTime::currentDateTime().toString(), msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::lightCmd(const QString &light, const QString &status)
|
|
|
|
{
|
|
|
|
m_client.sendMQTT("action/GoLightCtrl/" + light, QJsonObject{ {"Action", status }});
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::updateDisco()
|
|
|
|
{
|
|
|
|
lightCmd("basiclight1", m_discoState == 0 ? "on" : "off");
|
|
|
|
lightCmd("basiclight2", m_discoState == 1 ? "on" : "off");
|
|
|
|
lightCmd("basiclight3", m_discoState == 2 ? "on" : "off");
|
|
|
|
lightCmd("basiclight4", m_discoState == 3 ? "on" : "off");
|
|
|
|
lightCmd("basiclight5", m_discoState == 4 ? "on" : "off");
|
|
|
|
lightCmd("basiclight6", m_discoState == 5 ? "on" : "off");
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::updateAudioDevices()
|
|
|
|
{
|
|
|
|
m_ui.audioDeviceSelection->clear();
|
|
|
|
m_devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
|
|
|
|
for (const auto &x : m_devices)
|
|
|
|
m_ui.audioDeviceSelection->addItem(x.deviceName());
|
|
|
|
}
|