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 "ui_mainwindow.h"
#include <QAudioInput>
#include <QAudioFormat>
#include <QAudioDeviceInfo>
// Qt includes
#include <QButtonGroup>
#include <QMessageBox>
#include <QStringBuilder>
#include <QRadioButton>
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<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_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<int>(&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<int>(&QSpinBox::valueChanged), m_ui.widget, &OsciWidget::setBlend);
m_ui.spinBoxGlow->setValue(m_ui.widget->glow());
connect(m_ui.spinBoxGlow, qOverload<int>(&QSpinBox::valueChanged), m_ui.widget, &OsciWidget::setGlow);
connect(m_ui->spinBoxGlow, qOverload<int>(&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<AudioDevice*>(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<QAudioInput>(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("▮▮");
}
}

View File

@@ -1,16 +1,15 @@
#pragma once
// Qt includes
#include <QMainWindow>
// system includes
#include <memory>
#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<QAudioInput> m_input;
Device m_device;
//FakeDevice m_fakeDevice;
const std::unique_ptr<Ui::MainWindow> m_ui;
const QList<QAudioDeviceInfo> m_audioDevices;
const std::unique_ptr<BaseDevice> m_input;
};

View File

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

View File

@@ -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

View File

@@ -5,17 +5,11 @@
#include <QLine>
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());

View File

@@ -12,7 +12,7 @@
#include <optional>
#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};