From c86e85920b790c64795273c0220ee97bbd843bf4 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Fri, 30 Aug 2019 20:31:04 +0200 Subject: [PATCH] Created base class for sample sources --- audiodevice.cpp | 74 ++++++++++++++++++++++++++++++++++++ audiodevice.h | 45 ++++++++++++++++++++++ basedevice.cpp | 1 + basedevice.h | 31 ++++++++++++++++ device.cpp | 49 ------------------------ device.h | 45 ---------------------- fakedevice.cpp | 46 +++++++++++++++++++++++ fakedevice.h | 37 ++++++++++++++++++ mainwindow.cpp | 97 +++++++++++++++++++++--------------------------- mainwindow.h | 18 ++++----- mainwindow.ui | 8 ++-- oscilloscope.pro | 8 +++- osciwidget.cpp | 59 ++++------------------------- osciwidget.h | 11 +----- 14 files changed, 302 insertions(+), 227 deletions(-) create mode 100644 audiodevice.cpp create mode 100644 audiodevice.h create mode 100644 basedevice.cpp create mode 100644 basedevice.h delete mode 100644 device.cpp delete mode 100644 device.h create mode 100644 fakedevice.cpp create mode 100644 fakedevice.h diff --git a/audiodevice.cpp b/audiodevice.cpp new file mode 100644 index 0000000..a2f4d4f --- /dev/null +++ b/audiodevice.cpp @@ -0,0 +1,74 @@ +#include "audiodevice.h" + +// Qt includes +#include +#include + +namespace { +//! private helper to allow QAudioInput to write to a io device +class AudioDeviceHelper : public QIODevice +{ +public: + explicit AudioDeviceHelper(AudioDevice &audioDevice); + + qint64 readData(char *data, qint64 maxlen) override; + qint64 writeData(const char *data, qint64 len) override; + +private: + AudioDevice &m_audioDevice; +}; +} + +AudioDevice::AudioDevice(QObject *parent) : + BaseDevice{parent}, + m_helper(std::make_unique(*this)) +{ +} + +AudioDevice::~AudioDevice() = default; + +void AudioDevice::start() +{ + QAudioFormat format; + format.setSampleRate(m_samplerate); + format.setChannelCount(2); + format.setSampleSize(16); + format.setSampleType(QAudioFormat::SignedInt); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + + m_input = std::make_unique(m_device, format); + m_input->start(m_helper.get()); + m_input->setBufferSize(m_samplerate/m_framerate*sizeof(qint16)*2); +} + +void AudioDevice::stop() +{ + m_input = nullptr; +} + + + +namespace { +AudioDeviceHelper::AudioDeviceHelper(AudioDevice &audioDevice) : + QIODevice(&audioDevice), + m_audioDevice(audioDevice) +{ + setOpenMode(QIODevice::WriteOnly); +} + +qint64 AudioDeviceHelper::readData(char *data, qint64 maxlen) +{ + Q_UNUSED(data) + Q_UNUSED(maxlen) + qFatal("reading is not allowed!"); +} + +qint64 AudioDeviceHelper::writeData(const char *data, qint64 len) +{ + Q_ASSERT(len % sizeof(SamplePair) == 0); + emit m_audioDevice.samplesReceived(reinterpret_cast(data), + reinterpret_cast(data + (len/sizeof(SamplePair)))); + return len; +} +} diff --git a/audiodevice.h b/audiodevice.h new file mode 100644 index 0000000..0c559dc --- /dev/null +++ b/audiodevice.h @@ -0,0 +1,45 @@ +#pragma once + +#include "basedevice.h" + +// Qt includes +#include + +// system includes +#include + +// forward declares +namespace { class AudioDeviceHelper; } +class QAudioInput; + +class AudioDevice : public BaseDevice +{ + Q_OBJECT + +public: + explicit AudioDevice(QObject *parent = nullptr); + ~AudioDevice() override; + + void start() override; + void stop() override; + bool running() const override { return m_input != nullptr; } + + int samplerate() const override { return m_samplerate; } + void setSamplerate(int samplerate) override { Q_ASSERT(!running()); m_samplerate = samplerate; } + + int framerate() const override { return m_framerate; } + void setFramerate(int framerate) override { Q_ASSERT(!running()); m_framerate = framerate; } + + const auto &device() const { return m_device; } + void setDevice(const QAudioDeviceInfo &device) { Q_ASSERT(!running()); m_device = device; } + +private: + const std::unique_ptr m_helper; + + std::unique_ptr m_input; + + int m_samplerate; + int m_framerate; + + QAudioDeviceInfo m_device; +}; diff --git a/basedevice.cpp b/basedevice.cpp new file mode 100644 index 0000000..8fc2a2a --- /dev/null +++ b/basedevice.cpp @@ -0,0 +1 @@ +#include "basedevice.h" diff --git a/basedevice.h b/basedevice.h new file mode 100644 index 0000000..bb63e31 --- /dev/null +++ b/basedevice.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +template +struct SamplePairT { + T x, y; +}; + +typedef SamplePairT SamplePair; + +class BaseDevice : public QObject +{ + Q_OBJECT + +public: + using QObject::QObject; + + virtual void start() = 0; + virtual void stop() = 0; + virtual bool running() const = 0; + + virtual int samplerate() const = 0; + virtual void setSamplerate(int samplerate) = 0; + + virtual int framerate() const = 0; + virtual void setFramerate(int framerate) = 0; + +signals: + void samplesReceived(const SamplePair *begin, const SamplePair *end); +}; diff --git a/device.cpp b/device.cpp deleted file mode 100644 index 9c4fbfb..0000000 --- a/device.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "device.h" - -#include - -#include - -Device::Device(QObject *parent) : - QIODevice(parent) -{ - setOpenMode(QIODevice::WriteOnly); -} - -qint64 Device::readData(char *data, qint64 maxlen) -{ - Q_UNUSED(data) - Q_UNUSED(maxlen) - qFatal("oida"); -} - -qint64 Device::writeData(const char *data, qint64 len) -{ - Q_ASSERT(len % sizeof(SamplePair) == 0); - emit samplesReceived(reinterpret_cast(data), - reinterpret_cast(data + (len/sizeof(SamplePair)))); - return len; -} - -FakeDevice::FakeDevice(QObject *parent) : - QObject(parent), m_timerId(startTimer(1000/60)), m_dingsDesHaltHochZaehlt(0) -{ -} - -void FakeDevice::timerEvent(QTimerEvent *event) -{ - if (event->timerId() == m_timerId) - { - std::array samples; - for (SamplePair &pair : samples) - { - pair.x = std::sin(m_dingsDesHaltHochZaehlt) * std::numeric_limits::max(); - pair.y = std::cos(m_dingsDesHaltHochZaehlt) * std::numeric_limits::max(); - m_dingsDesHaltHochZaehlt += 0.05; - } - - emit samplesReceived(samples.begin(), samples.end()); - } - else - QObject::timerEvent(event); -} diff --git a/device.h b/device.h deleted file mode 100644 index e90e9e4..0000000 --- a/device.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include -#include - -#define qvoid void - -template struct SamplePairT { - T x, y; -}; - -typedef SamplePairT SamplePair; - -class Device : public QIODevice -{ - Q_OBJECT - -public: - explicit Device(QObject *parent = nullptr); - -signals: - void samplesReceived(const SamplePair *begin, const SamplePair *end); - -protected: - qint64 readData(char *data, qint64 maxlen) override; - qint64 writeData(const char *data, qint64 len) override; -}; - -class FakeDevice : public QObject -{ - Q_OBJECT - -public: - explicit FakeDevice(QObject *parent = nullptr); - -signals: - void samplesReceived(const SamplePair *begin, const SamplePair *end); - -protected: - qvoid timerEvent(QTimerEvent *event) override; - -private: - double m_dingsDesHaltHochZaehlt; - const int m_timerId; -}; diff --git a/fakedevice.cpp b/fakedevice.cpp new file mode 100644 index 0000000..a7b448d --- /dev/null +++ b/fakedevice.cpp @@ -0,0 +1,46 @@ +#include "fakedevice.h" + +// Qt includes +#include + +// system includes +#include + +void FakeDevice::start() +{ + Q_ASSERT(!running()); + m_bufferSize = m_samplerate/m_framerate; + m_buffer = std::make_unique(m_bufferSize); + m_timerId = startTimer(1000/m_framerate); +} + +void FakeDevice::stop() +{ + Q_ASSERT(running()); + killTimer(m_timerId); + m_buffer = nullptr; +} + +bool FakeDevice::running() const +{ + return m_timerId != -1; +} + +void FakeDevice::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == m_timerId) + { + for (SamplePair *pair = m_buffer.get(); + pair != m_buffer.get() + m_bufferSize; + pair++) + { + pair->x = std::sin(m_dingsDesHaltHochZaehlt) * std::numeric_limits::max(); + pair->y = std::cos(m_dingsDesHaltHochZaehlt) * std::numeric_limits::max(); + m_dingsDesHaltHochZaehlt += 0.05; + } + + emit samplesReceived(m_buffer.get(), m_buffer.get() + m_bufferSize); + } + else + QObject::timerEvent(event); +} diff --git a/fakedevice.h b/fakedevice.h new file mode 100644 index 0000000..f120096 --- /dev/null +++ b/fakedevice.h @@ -0,0 +1,37 @@ +#pragma once + +#include "basedevice.h" + +// system includes +#include + +class FakeDevice : public BaseDevice +{ + Q_OBJECT + +public: + using BaseDevice::BaseDevice; + + void start() override; + void stop() override; + bool running() const override; + + int samplerate() const override { return m_samplerate; } + void setSamplerate(int samplerate) override { Q_ASSERT(!running()); m_samplerate = samplerate; } + + int framerate() const override { return m_framerate; } + void setFramerate(int framerate) override { Q_ASSERT(!running()); m_framerate = framerate; } + +protected: + void timerEvent(QTimerEvent *event) override; + +private: + double m_dingsDesHaltHochZaehlt{0.}; + int m_timerId{-1}; + + std::unique_ptr m_buffer; + std::size_t m_bufferSize; + + int m_samplerate{44100}; + int m_framerate{15}; +}; diff --git a/mainwindow.cpp b/mainwindow.cpp index 60c299a..da3bdd7 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,109 +1,98 @@ #include "mainwindow.h" +#include "ui_mainwindow.h" -#include -#include -#include +// Qt includes #include #include #include #include -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent) - , m_audioDevices(QAudioDeviceInfo::availableDevices(QAudio::AudioInput)) - , m_ui() +// local includes +#include "audiodevice.h" +#include "fakedevice.h" + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow{parent}, + m_ui{std::make_unique()}, + m_audioDevices{QAudioDeviceInfo::availableDevices(QAudio::AudioInput)}, + m_input{std::make_unique()} + //m_input{std::make_unique()} { - m_ui.setupUi(this); + m_ui->setupUi(this); - connect(&m_device, &Device::samplesReceived, m_ui.widget, &OsciWidget::renderSamples); - - //connect(&m_fakeDevice, &FakeDevice::samplesReceived, m_ui.widget, &OsciWidget::samplesReceived); + connect(m_input.get(), &BaseDevice::samplesReceived, m_ui->widget, &OsciWidget::renderSamples); for (const auto &device : m_audioDevices) { auto name = device.deviceName(); - m_ui.comboBoxDevices->addItem(name); + m_ui->comboBoxDevices->addItem(name); + // Select last element containing monitor if available if(name.contains("monitor")) { - m_ui.comboBoxDevices->setCurrentIndex(m_ui.comboBoxDevices->count()-1); + m_ui->comboBoxDevices->setCurrentIndex(m_ui->comboBoxDevices->count()-1); } } - if (m_ui.comboBoxDevices->count()) - m_ui.comboBoxDevices->setCurrentIndex(m_audioDevices.count()-1); - for (const auto samplerate : { 44100, 48000, 96000, 192000 }) - m_ui.comboBoxSamplerate->addItem(tr("%0").arg(samplerate), samplerate); + m_ui->comboBoxSamplerate->addItem(tr("%0").arg(samplerate), samplerate); - connect(m_ui.pushButtonToggle, &QAbstractButton::pressed, this, &MainWindow::toggle); + connect(m_ui->pushButtonToggle, &QAbstractButton::pressed, this, &MainWindow::toggle); for (const auto framerate : {15, 30, 50, 60}) - m_ui.comboBoxFps->addItem(tr("%0 FPS").arg(framerate), framerate); + m_ui->comboBoxFps->addItem(tr("%0 FPS").arg(framerate), framerate); - m_ui.comboBoxFps->setCurrentIndex(m_ui.comboBoxFps->findData(m_ui.widget->framerate())); + m_ui->spinBoxBlend->setValue(m_ui->widget->blend()); - connect(m_ui.comboBoxFps, &QComboBox::currentIndexChanged, this, [&combobox=*m_ui.comboBoxFps,&widget=*m_ui.widget](){ - widget.setFramerate(combobox.currentData().toInt()); - }); + connect(m_ui->spinBoxBlend, qOverload(&QSpinBox::valueChanged), m_ui->widget, &OsciWidget::setBlend); - m_ui.spinBoxBlend->setValue(m_ui.widget->blend()); + m_ui->spinBoxGlow->setValue(m_ui->widget->glow()); - connect(m_ui.spinBoxBlend, qOverload(&QSpinBox::valueChanged), m_ui.widget, &OsciWidget::setBlend); - - m_ui.spinBoxGlow->setValue(m_ui.widget->glow()); - - connect(m_ui.spinBoxGlow, qOverload(&QSpinBox::valueChanged), m_ui.widget, &OsciWidget::setGlow); + connect(m_ui->spinBoxGlow, qOverload(&QSpinBox::valueChanged), m_ui->widget, &OsciWidget::setGlow); auto buttonGroup = new QButtonGroup; buttonGroup->setExclusive(true); for (auto factor : { .5f, 1.f, 2.f, 4.f, 8.f }) { auto radioButton = new QRadioButton(QString::number(factor)); - connect(radioButton, &QRadioButton::pressed, this, [factor,&widget=*m_ui.widget](){ + connect(radioButton, &QRadioButton::pressed, this, [factor,&widget=*m_ui->widget](){ widget.setFactor(factor); }); - m_ui.horizontalLayout->addWidget(radioButton); + m_ui->horizontalLayout->addWidget(radioButton); } - if (m_ui.comboBoxDevices->count()) + if (m_ui->comboBoxDevices->count()) toggle(); } void MainWindow::toggle() { - if (!m_ui.comboBoxDevices->count()) + if (!m_ui->comboBoxDevices->count()) { QMessageBox::warning(this, tr("Failed to start!"), tr("Failed to start!") % "\n\n" % tr("No audio devices available!")); return; } - if (m_input) + if (m_input->running()) { - m_input = nullptr; - m_ui.comboBoxDevices->setEnabled(true); - m_ui.comboBoxSamplerate->setEnabled(true); - m_ui.pushButtonToggle->setText("▶"); + m_input->stop(); + m_ui->comboBoxDevices->setEnabled(true); + m_ui->comboBoxSamplerate->setEnabled(true); + m_ui->comboBoxFps->setEnabled(true); + m_ui->pushButtonToggle->setText("▶"); } else { - QAudioFormat format; - format.setSampleRate(m_ui.comboBoxSamplerate->currentData().toInt()); - format.setChannelCount(2); - format.setSampleSize(16); - format.setSampleType(QAudioFormat::SignedInt); - format.setCodec("audio/pcm"); - format.setByteOrder(QAudioFormat::LittleEndian); + m_input->setSamplerate(m_ui->comboBoxSamplerate->currentData().toInt()); + m_input->setFramerate(m_ui->comboBoxFps->currentData().toInt()); + if (auto audioDevice = dynamic_cast(m_input.get())) + audioDevice->setDevice(m_audioDevices.at(m_ui->comboBoxDevices->currentIndex())); + m_input->start(); - if(m_audioDevices.empty()){ - qFatal("No audio devices found"); - } - m_input = std::make_unique(m_audioDevices.at(m_ui.comboBoxDevices->currentIndex()), format); - m_input->start(&m_device); - m_input->setBufferSize(format.sampleRate()/60*sizeof(qint16)*2); - m_ui.comboBoxDevices->setEnabled(false); - m_ui.comboBoxSamplerate->setEnabled(false); - m_ui.pushButtonToggle->setText("▮▮"); + m_ui->comboBoxDevices->setEnabled(false); + m_ui->comboBoxSamplerate->setEnabled(false); + m_ui->comboBoxFps->setEnabled(false); + m_ui->pushButtonToggle->setText("▮▮"); } } diff --git a/mainwindow.h b/mainwindow.h index b05d94b..838b856 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -1,16 +1,15 @@ #pragma once +// Qt includes #include +// system includes #include -#include "device.h" -#include "ui_mainwindow.h" - -class QAudioInput; -class QAudioFormat; +// forward declares class QAudioDeviceInfo; namespace Ui { class MainWindow; } +class BaseDevice; class MainWindow : public QMainWindow { @@ -24,12 +23,9 @@ private slots: void toggle(); private: - Ui::MainWindow m_ui; - - std::unique_ptr m_input; - - Device m_device; - //FakeDevice m_fakeDevice; + const std::unique_ptr m_ui; const QList m_audioDevices; + + const std::unique_ptr m_input; }; diff --git a/mainwindow.ui b/mainwindow.ui index cc07513..461ef30 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -7,7 +7,7 @@ 0 0 800 - 600 + 860 @@ -42,6 +42,9 @@ + + + @@ -62,9 +65,6 @@ - - - diff --git a/oscilloscope.pro b/oscilloscope.pro index a812fec..e484e74 100644 --- a/oscilloscope.pro +++ b/oscilloscope.pro @@ -4,13 +4,17 @@ CONFIG += c++17 DEFINES += QT_DEPRECATED_WARNINGS QT_DISABLE_DEPRECATED_BEFORE=0x060000 SOURCES += \ - device.cpp \ + audiodevice.cpp \ + basedevice.cpp \ + fakedevice.cpp \ main.cpp \ mainwindow.cpp \ osciwidget.cpp HEADERS += \ - device.h \ + audiodevice.h \ + basedevice.h \ + fakedevice.h \ mainwindow.h \ osciwidget.h diff --git a/osciwidget.cpp b/osciwidget.cpp index 23652d1..f9f90ce 100644 --- a/osciwidget.cpp +++ b/osciwidget.cpp @@ -5,17 +5,11 @@ #include OsciWidget::OsciWidget(QWidget *parent) : - QOpenGLWidget(parent) + QOpenGLWidget{parent} { - restartTimer(); resizePixmap(); } -int OsciWidget::framerate() const -{ - return m_framerate; -} - int OsciWidget::blend() const { return m_blend; @@ -31,18 +25,6 @@ float OsciWidget::glow() const return m_glow; } -void OsciWidget::setFramerate(int framerate) -{ - if (framerate == m_framerate) - return; - - qDebug() << framerate; - - m_framerate = framerate; - - restartTimer(); -} - void OsciWidget::setBlend(int blend) { if (blend == m_blend) @@ -110,7 +92,13 @@ void OsciWidget::renderSamples(const SamplePair *begin, const SamplePair *end) m_lastPoint = p; } + painter.resetTransform(); + painter.setCompositionMode(QPainter::CompositionMode_Multiply); + painter.fillRect(m_pixmap.rect(), QColor(m_blend,m_blend,m_blend)); + painter.end(); + + repaint(); } void OsciWidget::paintEvent(QPaintEvent *event) @@ -121,22 +109,6 @@ void OsciWidget::paintEvent(QPaintEvent *event) painter.begin(this); painter.drawPixmap(0, 0, m_pixmap); painter.end(); - // Fade pixmap by multiplying all pixels by m_blend - - painter.begin(&m_pixmap); - painter.setCompositionMode(QPainter::CompositionMode_Multiply); - painter.fillRect(m_pixmap.rect(), QColor(m_blend,m_blend,m_blend)); - painter.end(); -} - -void OsciWidget::timerEvent(QTimerEvent *event) -{ - if (event->timerId() == m_timerId) - { - repaint(); - } - else - QWidget::timerEvent(event); } void OsciWidget::resizeEvent(QResizeEvent *event) @@ -146,23 +118,6 @@ void OsciWidget::resizeEvent(QResizeEvent *event) resizePixmap(); } -void OsciWidget::stop() -{ - if (m_timerId != -1) - killTimer(m_timerId); -} - -void OsciWidget::start() -{ - m_timerId = startTimer(1000/m_framerate); -} - -void OsciWidget::restartTimer() -{ - stop(); - start(); -} - void OsciWidget::resizePixmap() { m_pixmap = QPixmap(size()); diff --git a/osciwidget.h b/osciwidget.h index c064e07..7f1b916 100644 --- a/osciwidget.h +++ b/osciwidget.h @@ -12,7 +12,7 @@ #include -#include "device.h" +#include "audiodevice.h" class OsciWidget : public QOpenGLWidget { @@ -21,16 +21,11 @@ class OsciWidget : public QOpenGLWidget public: explicit OsciWidget(QWidget *parent = nullptr); - int framerate() const; int blend() const; float factor() const; float glow() const; - - void start(); - void stop(); public slots: - void setFramerate(int framerate); void setBlend(int blend); void setFactor(float factor); void setGlow(float glow); @@ -39,16 +34,12 @@ public slots: protected: void paintEvent(QPaintEvent *event) override; - void timerEvent(QTimerEvent *event) override; void resizeEvent(QResizeEvent *event) override; private: - void restartTimer(); void resizePixmap(); void createBlendPixmap(); - int m_timerId{-1}; - int m_framerate{15}; int m_blend{150}; float m_factor{4.f}; float m_glow{512.f};