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};