From c9ddad8e21f8e3504da63a565fb650b61419b514 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Sun, 25 Aug 2019 18:22:31 +0200 Subject: [PATCH] First try --- .gitignore | 73 ++++++++++++++++++++++++++++++++ device.cpp | 47 +++++++++++++++++++++ device.h | 43 +++++++++++++++++++ main.cpp | 40 ++++++++++++++++++ oscilloscope.pro | 15 +++++++ osciwidget.cpp | 108 +++++++++++++++++++++++++++++++++++++++++++++++ osciwidget.h | 40 ++++++++++++++++++ 7 files changed, 366 insertions(+) create mode 100644 .gitignore create mode 100644 device.cpp create mode 100644 device.h create mode 100644 main.cpp create mode 100644 oscilloscope.pro create mode 100644 osciwidget.cpp create mode 100644 osciwidget.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/device.cpp b/device.cpp new file mode 100644 index 0000000..a022925 --- /dev/null +++ b/device.cpp @@ -0,0 +1,47 @@ +#include "device.h" + +#include + +#include + +Device::Device(QObject *parent) : + QIODevice(parent) +{ + setOpenMode(QIODevice::WriteOnly); +} + +qint64 Device::readData(char *data, qint64 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)) +{ +} + +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 new file mode 100644 index 0000000..5698a6e --- /dev/null +++ b/device.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#define qvoid void + +struct SamplePair { + qint16 x, y; +}; + +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/main.cpp b/main.cpp new file mode 100644 index 0000000..5574b20 --- /dev/null +++ b/main.cpp @@ -0,0 +1,40 @@ +#include +#include + +#include "device.h" +#include "osciwidget.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + const auto info = []() -> const QAudioDeviceInfo { + for (const auto &info : QAudioDeviceInfo::availableDevices(QAudio::AudioInput)) + if (info.deviceName() == "alsa_output.pci-0000_00_1b.0.analog-stereo.monitor") + return info; + Q_UNREACHABLE(); + }(); + + QAudioFormat format; + format.setSampleRate(44100); + format.setChannelCount(2); + format.setSampleSize(16); + format.setSampleType(QAudioFormat::SignedInt); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + + QAudioInput input(info, format); + input.setBufferSize(44100/60*4); + + OsciWidget widget; + widget.show(); + + Device device; + input.start(&device); + QObject::connect(&device, &Device::samplesReceived, &widget, &OsciWidget::samplesReceived); + + //FakeDevice fake; + //QObject::connect(&fake, &FakeDevice::samplesReceived, &widget, &OsciWidget::samplesReceived); + + return a.exec(); +} diff --git a/oscilloscope.pro b/oscilloscope.pro new file mode 100644 index 0000000..76a90fa --- /dev/null +++ b/oscilloscope.pro @@ -0,0 +1,15 @@ +QT = core gui widgets multimedia + +CONFIG += c++17 +DEFINES += QT_DEPRECATED_WARNINGS QT_DISABLE_DEPRECATED_BEFORE=0x060000 + +SOURCES += \ + device.cpp \ + main.cpp \ + osciwidget.cpp + +HEADERS += \ + device.h \ + osciwidget.h + +STATECHARTS += diff --git a/osciwidget.cpp b/osciwidget.cpp new file mode 100644 index 0000000..0b1d4e7 --- /dev/null +++ b/osciwidget.cpp @@ -0,0 +1,108 @@ +#include "osciwidget.h" + +#include + +#include + +namespace { +constexpr auto framerate = 60; +constexpr auto blend = 190; +} + +template +auto pythagoras(const T &dx, const T &dy) +{ + return std::sqrt(dx * dx + dy * dy); +} + +auto pythagoras(const QLine &line) +{ + return pythagoras(line.dx(), line.dy()); +} + +OsciWidget::OsciWidget(QWidget *parent) : + QWidget(parent), m_timerId(startTimer(1000/framerate)) +{ + resizePixmap(); + m_timer.start(); +} + +void OsciWidget::samplesReceived(const SamplePair *begin, const SamplePair *end) +{ + QPainter painter; + painter.begin(&m_pixmap); + painter.setCompositionMode(QPainter::CompositionMode_Plus); + + QPen pen; + pen.setWidth(2); + + for (auto i = begin; i < end; i++) + { + const qint32 x = (float(i->x) / std::numeric_limits::max() / 2 * width() * 2) + (width() / 2); + const qint32 y = (float(-i->y) / std::numeric_limits::max() / 2 * height() * 2) + (height() / 2); + const QPoint p{x,y}; + + if (Q_LIKELY(m_lastPoint.has_value())) + { + auto dist = pythagoras(QLine(*m_lastPoint, p)); + if (dist < 1) + dist = 1; + + pen.setColor(QColor(0, 1./dist*255, 0)); + painter.setPen(pen); + + painter.drawLine(*m_lastPoint, p); + } + + m_lastPoint = p; + } + + painter.end(); + + if (m_timer.hasExpired(1000/framerate)) + { + repaint(); + m_timer.restart(); + } +} + +void OsciWidget::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + QPainter painter; + painter.begin(this); + painter.drawPixmap(0, 0, m_pixmap); + painter.end(); + + painter.begin(&m_pixmap); + painter.setCompositionMode(QPainter::CompositionMode_Multiply); + painter.drawPixmap(0, 0, m_fadeoutPixmap); + painter.end(); +} + +void OsciWidget::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == m_timerId) + { + //repaint(); + } + else + QWidget::timerEvent(event); +} + +void OsciWidget::resizeEvent(QResizeEvent *event) +{ + Q_UNUSED(event) + + resizePixmap(); +} + +void OsciWidget::resizePixmap() +{ + m_pixmap = QPixmap(size()); + m_pixmap.fill(QColor()); + + m_fadeoutPixmap = QPixmap(size()); + m_fadeoutPixmap.fill(QColor(blend, blend, blend)); +} diff --git a/osciwidget.h b/osciwidget.h new file mode 100644 index 0000000..8c5f59b --- /dev/null +++ b/osciwidget.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "device.h" + +class OsciWidget : public QWidget +{ + Q_OBJECT + +public: + explicit OsciWidget(QWidget *parent = nullptr); + +public slots: + void samplesReceived(const SamplePair *begin, const SamplePair *end); + +protected: + void paintEvent(QPaintEvent *event) override; + void timerEvent(QTimerEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + +private: + void resizePixmap(); + + const int m_timerId; + QPixmap m_pixmap; + QPixmap m_fadeoutPixmap; + + std::optional m_lastPoint; + + QElapsedTimer m_timer; +};