Created base class for sample sources

This commit is contained in:
2019-08-30 20:31:04 +02:00
parent 4dd39e1ba3
commit c86e85920b
14 changed files with 302 additions and 227 deletions

74
audiodevice.cpp Normal file
View File

@@ -0,0 +1,74 @@
#include "audiodevice.h"
// Qt includes
#include <QIODevice>
#include <QAudioInput>
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<AudioDeviceHelper>(*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<QAudioInput>(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<const SamplePair*>(data),
reinterpret_cast<const SamplePair*>(data + (len/sizeof(SamplePair))));
return len;
}
}

45
audiodevice.h Normal file
View File

@@ -0,0 +1,45 @@
#pragma once
#include "basedevice.h"
// Qt includes
#include <QAudioDeviceInfo>
// system includes
#include <memory>
// 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<AudioDeviceHelper> m_helper;
std::unique_ptr<QAudioInput> m_input;
int m_samplerate;
int m_framerate;
QAudioDeviceInfo m_device;
};

1
basedevice.cpp Normal file
View File

@@ -0,0 +1 @@
#include "basedevice.h"

31
basedevice.h Normal file
View File

@@ -0,0 +1,31 @@
#pragma once
#include <QObject>
template<typename T>
struct SamplePairT {
T x, y;
};
typedef SamplePairT<qint16> 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);
};

View File

@@ -1,49 +0,0 @@
#include "device.h"
#include <cmath>
#include <QTimerEvent>
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<const SamplePair*>(data),
reinterpret_cast<const SamplePair*>(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<SamplePair, 44100/60> samples;
for (SamplePair &pair : samples)
{
pair.x = std::sin(m_dingsDesHaltHochZaehlt) * std::numeric_limits<qint16>::max();
pair.y = std::cos(m_dingsDesHaltHochZaehlt) * std::numeric_limits<qint16>::max();
m_dingsDesHaltHochZaehlt += 0.05;
}
emit samplesReceived(samples.begin(), samples.end());
}
else
QObject::timerEvent(event);
}

View File

@@ -1,45 +0,0 @@
#pragma once
#include <QIODevice>
#include <QDebug>
#define qvoid void
template<typename T> struct SamplePairT {
T x, y;
};
typedef SamplePairT<qint16> 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;
};

46
fakedevice.cpp Normal file
View File

@@ -0,0 +1,46 @@
#include "fakedevice.h"
// Qt includes
#include <QTimerEvent>
// system includes
#include <cmath>
void FakeDevice::start()
{
Q_ASSERT(!running());
m_bufferSize = m_samplerate/m_framerate;
m_buffer = std::make_unique<SamplePair[]>(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<qint16>::max();
pair->y = std::cos(m_dingsDesHaltHochZaehlt) * std::numeric_limits<qint16>::max();
m_dingsDesHaltHochZaehlt += 0.05;
}
emit samplesReceived(m_buffer.get(), m_buffer.get() + m_bufferSize);
}
else
QObject::timerEvent(event);
}

37
fakedevice.h Normal file
View File

@@ -0,0 +1,37 @@
#pragma once
#include "basedevice.h"
// system includes
#include <memory>
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<SamplePair[]> m_buffer;
std::size_t m_bufferSize;
int m_samplerate{44100};
int m_framerate{15};
};

View File

@@ -1,109 +1,98 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QAudioInput> // Qt includes
#include <QAudioFormat>
#include <QAudioDeviceInfo>
#include <QButtonGroup> #include <QButtonGroup>
#include <QMessageBox> #include <QMessageBox>
#include <QStringBuilder> #include <QStringBuilder>
#include <QRadioButton> #include <QRadioButton>
MainWindow::MainWindow(QWidget *parent) // local includes
: QMainWindow(parent) #include "audiodevice.h"
, m_audioDevices(QAudioDeviceInfo::availableDevices(QAudio::AudioInput)) #include "fakedevice.h"
, m_ui()
MainWindow::MainWindow(QWidget *parent) :
QMainWindow{parent},
m_ui{std::make_unique<Ui::MainWindow>()},
m_audioDevices{QAudioDeviceInfo::availableDevices(QAudio::AudioInput)},
m_input{std::make_unique<AudioDevice>()}
//m_input{std::make_unique<FakeDevice>()}
{ {
m_ui.setupUi(this); m_ui->setupUi(this);
connect(&m_device, &Device::samplesReceived, m_ui.widget, &OsciWidget::renderSamples); connect(m_input.get(), &BaseDevice::samplesReceived, m_ui->widget, &OsciWidget::renderSamples);
//connect(&m_fakeDevice, &FakeDevice::samplesReceived, m_ui.widget, &OsciWidget::samplesReceived);
for (const auto &device : m_audioDevices) for (const auto &device : m_audioDevices)
{ {
auto name = device.deviceName(); auto name = device.deviceName();
m_ui.comboBoxDevices->addItem(name); m_ui->comboBoxDevices->addItem(name);
// Select last element containing monitor if available // Select last element containing monitor if available
if(name.contains("monitor")) 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 }) 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}) 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](){ connect(m_ui->spinBoxBlend, qOverload<int>(&QSpinBox::valueChanged), m_ui->widget, &OsciWidget::setBlend);
widget.setFramerate(combobox.currentData().toInt());
});
m_ui.spinBoxBlend->setValue(m_ui.widget->blend()); m_ui->spinBoxGlow->setValue(m_ui->widget->glow());
connect(m_ui.spinBoxBlend, qOverload<int>(&QSpinBox::valueChanged), m_ui.widget, &OsciWidget::setBlend); connect(m_ui->spinBoxGlow, qOverload<int>(&QSpinBox::valueChanged), m_ui->widget, &OsciWidget::setGlow);
m_ui.spinBoxGlow->setValue(m_ui.widget->glow());
connect(m_ui.spinBoxGlow, qOverload<int>(&QSpinBox::valueChanged), m_ui.widget, &OsciWidget::setGlow);
auto buttonGroup = new QButtonGroup; auto buttonGroup = new QButtonGroup;
buttonGroup->setExclusive(true); buttonGroup->setExclusive(true);
for (auto factor : { .5f, 1.f, 2.f, 4.f, 8.f }) for (auto factor : { .5f, 1.f, 2.f, 4.f, 8.f })
{ {
auto radioButton = new QRadioButton(QString::number(factor)); 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); widget.setFactor(factor);
}); });
m_ui.horizontalLayout->addWidget(radioButton); m_ui->horizontalLayout->addWidget(radioButton);
} }
if (m_ui.comboBoxDevices->count()) if (m_ui->comboBoxDevices->count())
toggle(); toggle();
} }
void MainWindow::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!")); QMessageBox::warning(this, tr("Failed to start!"), tr("Failed to start!") % "\n\n" % tr("No audio devices available!"));
return; return;
} }
if (m_input) if (m_input->running())
{ {
m_input = nullptr; m_input->stop();
m_ui.comboBoxDevices->setEnabled(true); m_ui->comboBoxDevices->setEnabled(true);
m_ui.comboBoxSamplerate->setEnabled(true); m_ui->comboBoxSamplerate->setEnabled(true);
m_ui.pushButtonToggle->setText(""); m_ui->comboBoxFps->setEnabled(true);
m_ui->pushButtonToggle->setText("");
} }
else else
{ {
QAudioFormat format; m_input->setSamplerate(m_ui->comboBoxSamplerate->currentData().toInt());
format.setSampleRate(m_ui.comboBoxSamplerate->currentData().toInt()); m_input->setFramerate(m_ui->comboBoxFps->currentData().toInt());
format.setChannelCount(2); if (auto audioDevice = dynamic_cast<AudioDevice*>(m_input.get()))
format.setSampleSize(16); audioDevice->setDevice(m_audioDevices.at(m_ui->comboBoxDevices->currentIndex()));
format.setSampleType(QAudioFormat::SignedInt); m_input->start();
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
if(m_audioDevices.empty()){ m_ui->comboBoxDevices->setEnabled(false);
qFatal("No audio devices found"); m_ui->comboBoxSamplerate->setEnabled(false);
} m_ui->comboBoxFps->setEnabled(false);
m_input = std::make_unique<QAudioInput>(m_audioDevices.at(m_ui.comboBoxDevices->currentIndex()), format); m_ui->pushButtonToggle->setText("▮▮");
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("▮▮");
} }
} }

View File

@@ -1,16 +1,15 @@
#pragma once #pragma once
// Qt includes
#include <QMainWindow> #include <QMainWindow>
// system includes
#include <memory> #include <memory>
#include "device.h" // forward declares
#include "ui_mainwindow.h"
class QAudioInput;
class QAudioFormat;
class QAudioDeviceInfo; class QAudioDeviceInfo;
namespace Ui { class MainWindow; } namespace Ui { class MainWindow; }
class BaseDevice;
class MainWindow : public QMainWindow class MainWindow : public QMainWindow
{ {
@@ -24,12 +23,9 @@ private slots:
void toggle(); void toggle();
private: private:
Ui::MainWindow m_ui; const std::unique_ptr<Ui::MainWindow> m_ui;
std::unique_ptr<QAudioInput> m_input;
Device m_device;
//FakeDevice m_fakeDevice;
const QList<QAudioDeviceInfo> m_audioDevices; const QList<QAudioDeviceInfo> m_audioDevices;
const std::unique_ptr<BaseDevice> m_input;
}; };

View File

@@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>800</width> <width>800</width>
<height>600</height> <height>860</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -42,6 +42,9 @@
<item> <item>
<widget class="QComboBox" name="comboBoxSamplerate"/> <widget class="QComboBox" name="comboBoxSamplerate"/>
</item> </item>
<item>
<widget class="QComboBox" name="comboBoxFps"/>
</item>
<item> <item>
<widget class="QPushButton" name="pushButtonToggle"> <widget class="QPushButton" name="pushButtonToggle">
<property name="text"> <property name="text">
@@ -62,9 +65,6 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QComboBox" name="comboBoxFps"/>
</item>
<item> <item>
<widget class="QSpinBox" name="spinBoxBlend"> <widget class="QSpinBox" name="spinBoxBlend">
<property name="suffix"> <property name="suffix">

View File

@@ -4,13 +4,17 @@ CONFIG += c++17
DEFINES += QT_DEPRECATED_WARNINGS QT_DISABLE_DEPRECATED_BEFORE=0x060000 DEFINES += QT_DEPRECATED_WARNINGS QT_DISABLE_DEPRECATED_BEFORE=0x060000
SOURCES += \ SOURCES += \
device.cpp \ audiodevice.cpp \
basedevice.cpp \
fakedevice.cpp \
main.cpp \ main.cpp \
mainwindow.cpp \ mainwindow.cpp \
osciwidget.cpp osciwidget.cpp
HEADERS += \ HEADERS += \
device.h \ audiodevice.h \
basedevice.h \
fakedevice.h \
mainwindow.h \ mainwindow.h \
osciwidget.h osciwidget.h

View File

@@ -5,17 +5,11 @@
#include <QLine> #include <QLine>
OsciWidget::OsciWidget(QWidget *parent) : OsciWidget::OsciWidget(QWidget *parent) :
QOpenGLWidget(parent) QOpenGLWidget{parent}
{ {
restartTimer();
resizePixmap(); resizePixmap();
} }
int OsciWidget::framerate() const
{
return m_framerate;
}
int OsciWidget::blend() const int OsciWidget::blend() const
{ {
return m_blend; return m_blend;
@@ -31,18 +25,6 @@ float OsciWidget::glow() const
return m_glow; return m_glow;
} }
void OsciWidget::setFramerate(int framerate)
{
if (framerate == m_framerate)
return;
qDebug() << framerate;
m_framerate = framerate;
restartTimer();
}
void OsciWidget::setBlend(int blend) void OsciWidget::setBlend(int blend)
{ {
if (blend == m_blend) if (blend == m_blend)
@@ -110,7 +92,13 @@ void OsciWidget::renderSamples(const SamplePair *begin, const SamplePair *end)
m_lastPoint = p; m_lastPoint = p;
} }
painter.resetTransform();
painter.setCompositionMode(QPainter::CompositionMode_Multiply);
painter.fillRect(m_pixmap.rect(), QColor(m_blend,m_blend,m_blend));
painter.end(); painter.end();
repaint();
} }
void OsciWidget::paintEvent(QPaintEvent *event) void OsciWidget::paintEvent(QPaintEvent *event)
@@ -121,22 +109,6 @@ void OsciWidget::paintEvent(QPaintEvent *event)
painter.begin(this); painter.begin(this);
painter.drawPixmap(0, 0, m_pixmap); painter.drawPixmap(0, 0, m_pixmap);
painter.end(); 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) void OsciWidget::resizeEvent(QResizeEvent *event)
@@ -146,23 +118,6 @@ void OsciWidget::resizeEvent(QResizeEvent *event)
resizePixmap(); 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() void OsciWidget::resizePixmap()
{ {
m_pixmap = QPixmap(size()); m_pixmap = QPixmap(size());

View File

@@ -12,7 +12,7 @@
#include <optional> #include <optional>
#include "device.h" #include "audiodevice.h"
class OsciWidget : public QOpenGLWidget class OsciWidget : public QOpenGLWidget
{ {
@@ -21,16 +21,11 @@ class OsciWidget : public QOpenGLWidget
public: public:
explicit OsciWidget(QWidget *parent = nullptr); explicit OsciWidget(QWidget *parent = nullptr);
int framerate() const;
int blend() const; int blend() const;
float factor() const; float factor() const;
float glow() const; float glow() const;
void start();
void stop();
public slots: public slots:
void setFramerate(int framerate);
void setBlend(int blend); void setBlend(int blend);
void setFactor(float factor); void setFactor(float factor);
void setGlow(float glow); void setGlow(float glow);
@@ -39,16 +34,12 @@ public slots:
protected: protected:
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
void timerEvent(QTimerEvent *event) override;
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
private: private:
void restartTimer();
void resizePixmap(); void resizePixmap();
void createBlendPixmap(); void createBlendPixmap();
int m_timerId{-1};
int m_framerate{15};
int m_blend{150}; int m_blend{150};
float m_factor{4.f}; float m_factor{4.f};
float m_glow{512.f}; float m_glow{512.f};