Files
oscilloscope/mainwindow.cpp
2019-09-23 21:56:51 +02:00

250 lines
8.2 KiB
C++

#include "mainwindow.h"
#include "ui_mainwindow.h"
// local includes
#include "audiodevice.h"
#include "debugtonegenerator.h"
// Qt includes
#include <QLabel>
#include <QWidgetAction>
#include <QFormLayout>
#include <QSpinBox>
#include <QShortcut>
#include <QDebug>
// system includes
#include <stdexcept>
namespace {
constexpr int samplerates[] = { 44100, 48000, 96000, 192000 };
constexpr int refreshrates[] = { 1, 15, 30, 50, 60 };
constexpr int zoomlevels[] = { 50, 75, 100, 200, 400, 800 };
template<typename T>
void setActionsEnabled(const T &actions, bool enabled)
{
for(auto action : actions)
action->setEnabled(enabled);
}
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow{parent},
m_ui{std::make_unique<Ui::MainWindow>()},
m_inputDevices{QAudioDeviceInfo::availableDevices(QAudio::AudioInput)},
m_outputDevices{QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)},
m_statusLabel{*new QLabel}
{
m_ui->setupUi(this);
m_ui->statusbar->addWidget(&m_statusLabel);
connect(m_ui->widget, &OsciWidget::statusUpdate, &m_statusLabel, &QLabel::setText);
// setting up menu File
connect(m_ui->actionStart, &QAction::triggered, this, &MainWindow::start);
connect(m_ui->actionStop, &QAction::triggered, this, &MainWindow::stop);
m_ui->actionQuit->setShortcut(QKeySequence::Quit);
// setting up menu InputDevices
for (const auto &device : m_inputDevices)
{
auto name = device.deviceName();
const auto action = m_ui->menuInputDevice->addAction(name);
action->setCheckable(true);
m_inputDeviceGroup.addAction(action);
// Select last element containing monitor if available
if(name.contains("monitor"))
action->setChecked(true);
}
// setting up menu OutputDevices
for (const auto &device : m_outputDevices)
{
auto name = device.deviceName();
const auto action = m_ui->menuOutputDevice->addAction(name);
action->setCheckable(true);
m_outputDeviceGroup.addAction(action);
// Select last element containing analog-stereo if available
if(name.contains("analog-stereo"))
action->setChecked(true);
}
// setting up menu Samplerates
for (const auto samplerate : samplerates)
{
auto action = m_ui->menuSamplerate->addAction(tr("%0").arg(samplerate));
action->setCheckable(true);
m_samplerateGroup.addAction(action);
}
m_samplerateGroup.actions().first()->setChecked(true);
// setting up menu Refreshrates
for (const auto refreshrate : refreshrates)
{
auto action = m_ui->menuRefreshrate->addAction(tr("%0FPS").arg(refreshrate));
action->setCheckable(true);
m_refreshrateGroup.addAction(action);
}
{
const auto index = std::find(std::begin(refreshrates), std::end(refreshrates), m_ui->widget->fps());
if (index != std::end(refreshrates))
m_refreshrateGroup.actions().at(std::distance(std::begin(refreshrates), index))->setChecked(true);
}
connect(&m_refreshrateGroup, &QActionGroup::triggered, this, &MainWindow::refreshRateChanged);
// setting up menu Zoom
for (const auto zoomlevel : zoomlevels)
{
auto action = m_ui->menuZoom->addAction(tr("%0%").arg(zoomlevel));
action->setCheckable(true);
m_zoomlevelsGroup.addAction(action);
}
{
const auto index = std::find(std::begin(zoomlevels), std::end(zoomlevels), m_ui->widget->factor()*100);
if (index != std::end(zoomlevels))
m_zoomlevelsGroup.actions().at(std::distance(std::begin(zoomlevels), index))->setChecked(true);
}
connect(&m_zoomlevelsGroup, &QActionGroup::triggered, this, &MainWindow::zoomChanged);
//setting up menu Debug
connect(m_ui->actionToneGenerator, &QAction::triggered, this, &MainWindow::startStopGenerator);
{
auto widgetAction = new QWidgetAction(this);
auto widget = new QWidget;
auto layout = new QFormLayout(widget);
{
auto input = new QDoubleSpinBox;
input->setRange(0, 10000.0);
input->setSingleStep(1.0);
input->setValue(m_ui->widget->afterglow());
connect(input, qOverload<double>(&QDoubleSpinBox::valueChanged), m_ui->widget, &OsciWidget::setAfterglow);
layout->addRow(tr("Afterglow:"), input);
}
{
auto input = new QSpinBox;
input->setRange(0, 255);
input->setValue(m_ui->widget->lightspeed());
connect(input, qOverload<int>(&QSpinBox::valueChanged), m_ui->widget, &OsciWidget::setLightspeed);
layout->addRow(tr("Lightspeed:"), input);
}
widgetAction->setDefaultWidget(widget);
m_ui->menuDebug->addAction(widgetAction);
}
// TODO cleanup when we have clean a strategy for implementing shortcuts and actions
auto toggleZen = new QShortcut(QKeySequence(tr("Z")), this);
connect(toggleZen, &QShortcut::activated, this, [=](){
setWindowFlags(windowFlags() ^ Qt::FramelessWindowHint);
setWindowState(windowState() ^ Qt::WindowFullScreen);
show();
menuBar()->setVisible(!menuBar()->isVisible());
statusBar()->setVisible(!statusBar()->isVisible());
QCursor *currentCursor = QGuiApplication::overrideCursor();
if(!currentCursor || *currentCursor != Qt::BlankCursor){
QGuiApplication::setOverrideCursor(Qt::BlankCursor);
}else{
QGuiApplication::restoreOverrideCursor();
}
});
// autostart
if (m_inputDevices.isEmpty())
{
m_ui->actionStart->setEnabled(false);
m_ui->actionStop->setEnabled(false);
}
else
start();
}
void MainWindow::start()
{
{
auto input = std::make_unique<AudioDevice>();
// setDevice is AudioDevice specific API
input->setDevice(m_inputDevices.at(m_inputDeviceGroup.actions().indexOf(m_inputDeviceGroup.checkedAction())));
m_input = std::move(input);
}
m_input->setSamplerate(samplerate());
connect(m_input.get(), &BaseDevice::samplesReceived, m_ui->widget, &OsciWidget::renderSamples);
m_input->start();
setActionsEnabled(m_inputDeviceGroup.actions(), false);
setActionsEnabled(m_samplerateGroup.actions(), false);
m_ui->actionStart->setEnabled(false);
m_ui->actionStop->setEnabled(true);
}
void MainWindow::stop()
{
m_input = nullptr;
setActionsEnabled(m_inputDeviceGroup.actions(), true);
setActionsEnabled(m_samplerateGroup.actions(), true);
m_ui->actionStart->setEnabled(true);
m_ui->actionStop->setEnabled(false);
}
void MainWindow::refreshRateChanged()
{
auto *checked = m_refreshrateGroup.checkedAction();
const auto index = m_refreshrateGroup.actions().indexOf(checked);
const auto refreshrate = refreshrates[index];
qDebug() << "refreshrate: checked =" << checked << "index =" << index << "value =" << refreshrate;
m_ui->widget->setFps(refreshrate);
}
void MainWindow::zoomChanged()
{
auto *checked = m_zoomlevelsGroup.checkedAction();
const auto index = m_zoomlevelsGroup.actions().indexOf(checked);
const auto zoomlevel = zoomlevels[index];
qDebug() << "zoomlevel: checked =" << checked << "index =" << index << "value =" << zoomlevel;
m_ui->widget->setFactor(zoomlevel/100.f);
}
void MainWindow::startStopGenerator()
{
if(m_generator){
m_generator = nullptr;
return;
}
m_generator = std::make_unique<DebugToneGenerator>();
m_generator->setDevice(m_outputDevices.at(m_outputDeviceGroup.actions().indexOf(m_outputDeviceGroup.checkedAction())));
m_generator->setSamplerate(samplerate());
m_generator->start();
}
int MainWindow::samplerate() const
{
auto *checked = m_samplerateGroup.checkedAction();
if (!checked)
throw std::runtime_error(tr("No samplerate selected!").toStdString());
const auto index = m_samplerateGroup.actions().indexOf(checked);
if (index < 0)
throw std::runtime_error(tr("Unknown samplerate selected!").toStdString());
if (index >= std::distance(std::begin(samplerates), std::end(samplerates)))
throw std::runtime_error(tr("Index out of range!").toStdString());
return samplerates[index];
}
MainWindow::~MainWindow() = default;