summaryrefslogtreecommitdiff
path: root/plugin
diff options
context:
space:
mode:
author2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-09-06 20:43:17 +1000
committer2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-09-06 20:43:17 +1000
commit929d8fa40e371a2adbe1fe0f93f0ebf11bcb0981 (patch)
tree965f95279a862cf5853eb6af8ac60938a784a685 /plugin
parentplugin: fix cmake version (diff)
downloadcaelestia-shell-929d8fa40e371a2adbe1fe0f93f0ebf11bcb0981.tar.gz
caelestia-shell-929d8fa40e371a2adbe1fe0f93f0ebf11bcb0981.tar.bz2
caelestia-shell-929d8fa40e371a2adbe1fe0f93f0ebf11bcb0981.zip
plugin: async audio processing
Diffstat (limited to 'plugin')
-rw-r--r--plugin/src/Caelestia/audioprovider.cpp84
-rw-r--r--plugin/src/Caelestia/audioprovider.hpp33
-rw-r--r--plugin/src/Caelestia/beattracker.cpp39
-rw-r--r--plugin/src/Caelestia/beattracker.hpp27
4 files changed, 141 insertions, 42 deletions
diff --git a/plugin/src/Caelestia/audioprovider.cpp b/plugin/src/Caelestia/audioprovider.cpp
index 0c6f4fd..2d76a5d 100644
--- a/plugin/src/Caelestia/audioprovider.cpp
+++ b/plugin/src/Caelestia/audioprovider.cpp
@@ -6,39 +6,49 @@
#include <QIODevice>
#include <QMediaDevices>
#include <QObject>
+#include <QThread>
#include <cstddef>
#include <cstdint>
namespace caelestia {
-AudioProvider::AudioProvider(int sampleRate, int hopSize, QObject* parent)
- : Service(parent)
- , m_hopSize(hopSize) {
+AudioWorker::AudioWorker(int sampleRate, int hopSize, QObject* parent)
+ : QObject(parent)
+ , m_sampleRate(sampleRate)
+ , m_hopSize(hopSize)
+ , m_source(nullptr)
+ , m_device(nullptr) {}
+
+void AudioWorker::init() {
QAudioFormat format;
- format.setSampleRate(sampleRate);
+ format.setSampleRate(m_sampleRate);
format.setChannelCount(1);
format.setSampleFormat(QAudioFormat::Int16);
m_source = new QAudioSource(QMediaDevices::defaultAudioInput(), format, this);
- connect(m_source, &QAudioSource::stateChanged, this, &AudioProvider::handleStateChanged);
+ connect(m_source, &QAudioSource::stateChanged, this, &AudioWorker::handleStateChanged);
};
-AudioProvider::~AudioProvider() {
+AudioWorker::~AudioWorker() {
m_source->stop();
delete m_source;
}
-void AudioProvider::start() {
+void AudioWorker::start() {
+ if (!m_source) {
+ return;
+ }
+
m_device = m_source->start();
- connect(m_device, &QIODevice::readyRead, this, &AudioProvider::processData);
+ connect(m_device, &QIODevice::readyRead, this, &AudioWorker::processData);
}
-void AudioProvider::stop() {
+void AudioWorker::stop() {
m_source->stop();
m_device = nullptr;
}
-template <typename T> void AudioProvider::process(T* outBuf) {
+template <typename T> void AudioWorker::process(T* outBuf) {
const QByteArray data = m_device->readAll();
const int16_t* samples = reinterpret_cast<const int16_t*>(data.constData());
const size_t count = static_cast<size_t>(data.size()) / sizeof(int16_t);
@@ -51,23 +61,23 @@ template <typename T> void AudioProvider::process(T* outBuf) {
}
}
}
-template void AudioProvider::process(float* outBuf);
-template void AudioProvider::process(double* outBuf);
+template void AudioWorker::process(float* outBuf);
+template void AudioWorker::process(double* outBuf);
-void AudioProvider::handleStateChanged(QtAudio::State state) const {
+void AudioWorker::handleStateChanged(QtAudio::State state) const {
if (state == QtAudio::StoppedState && m_source->error() != QtAudio::NoError) {
switch (m_source->error()) {
case QtAudio::OpenError:
- qWarning() << "AudioProvider: failed to open audio device";
+ qWarning() << "AudioWorker: failed to open audio device";
break;
case QtAudio::IOError:
- qWarning() << "AudioProvider: an error occurred during read/write of audio device";
+ qWarning() << "AudioWorker: an error occurred during read/write of audio device";
break;
case QtAudio::UnderrunError:
- qWarning() << "AudioProvider: audio data is not being fed to audio device fast enough";
+ qWarning() << "AudioWorker: audio data is not being fed to audio device fast enough";
break;
case QtAudio::FatalError:
- qCritical() << "AudioProvider: fatal error in audio device";
+ qCritical() << "AudioWorker: fatal error in audio device";
break;
default:
break;
@@ -75,4 +85,44 @@ void AudioProvider::handleStateChanged(QtAudio::State state) const {
}
}
+AudioProvider::AudioProvider(QObject* parent)
+ : Service(parent)
+ , m_worker(nullptr)
+ , m_thread(nullptr) {}
+
+AudioProvider::~AudioProvider() {
+ if (m_thread) {
+ m_thread->quit();
+ m_thread->wait();
+ }
+}
+
+void AudioProvider::init() {
+ if (!m_worker) {
+ qWarning() << "AudioProvider::init: attempted to init with no worker set";
+ return;
+ }
+
+ m_thread = new QThread(this);
+ m_worker->moveToThread(m_thread);
+
+ connect(m_thread, &QThread::started, m_worker, &AudioWorker::init);
+ connect(m_thread, &QThread::finished, m_worker, &AudioWorker::deleteLater);
+ connect(m_thread, &QThread::finished, m_thread, &QThread::deleteLater);
+
+ m_thread->start();
+}
+
+void AudioProvider::start() {
+ if (m_worker) {
+ QMetaObject::invokeMethod(m_worker, "start", Qt::QueuedConnection);
+ }
+}
+
+void AudioProvider::stop() {
+ if (m_worker) {
+ QMetaObject::invokeMethod(m_worker, "stop", Qt::QueuedConnection);
+ }
+}
+
} // namespace caelestia
diff --git a/plugin/src/Caelestia/audioprovider.hpp b/plugin/src/Caelestia/audioprovider.hpp
index 5564dc5..205be2c 100644
--- a/plugin/src/Caelestia/audioprovider.hpp
+++ b/plugin/src/Caelestia/audioprovider.hpp
@@ -4,18 +4,22 @@
#include <QAudioSource>
#include <QIODevice>
#include <QObject>
+#include <QThread>
#include <qqmlintegration.h>
namespace caelestia {
-class AudioProvider : public Service {
+class AudioWorker : public QObject {
Q_OBJECT
public:
- explicit AudioProvider(int sampleRate = 44100, int hopSize = 512, QObject* parent = nullptr);
- ~AudioProvider();
+ explicit AudioWorker(int sampleRate = 44100, int hopSize = 512, QObject* parent = nullptr);
+ ~AudioWorker();
+
+ void init();
protected:
+ int m_sampleRate;
int m_hopSize;
template <typename T> void process(T* outBuf);
@@ -24,8 +28,8 @@ private:
QAudioSource* m_source;
QIODevice* m_device;
- void start() override;
- void stop() override;
+ Q_INVOKABLE void start();
+ Q_INVOKABLE void stop();
void handleStateChanged(QtAudio::State state) const;
@@ -33,4 +37,23 @@ private:
virtual void consumeData() = 0;
};
+class AudioProvider : public Service {
+ Q_OBJECT
+
+public:
+ explicit AudioProvider(QObject* parent = nullptr);
+ ~AudioProvider();
+
+protected:
+ AudioWorker* m_worker;
+
+ void init();
+
+private:
+ QThread* m_thread;
+
+ void start() override;
+ void stop() override;
+};
+
} // namespace caelestia
diff --git a/plugin/src/Caelestia/beattracker.cpp b/plugin/src/Caelestia/beattracker.cpp
index a915c08..b59abd7 100644
--- a/plugin/src/Caelestia/beattracker.cpp
+++ b/plugin/src/Caelestia/beattracker.cpp
@@ -6,33 +6,46 @@
namespace caelestia {
-BeatTracker::BeatTracker(uint_t sampleRate, uint_t hopSize, QObject* parent)
- : AudioProvider(static_cast<int>(sampleRate), static_cast<int>(hopSize), parent)
+BeatWorker::BeatWorker(uint_t sampleRate, uint_t hopSize, QObject* parent)
+ : AudioWorker(static_cast<int>(sampleRate), static_cast<int>(hopSize), parent)
, m_tempo(new_aubio_tempo("default", 1024, hopSize, sampleRate))
, m_in(new_fvec(hopSize))
- , m_out(new_fvec(2))
- , m_bpm(120) {};
+ , m_out(new_fvec(2)) {};
-BeatTracker::~BeatTracker() {
+BeatWorker::~BeatWorker() {
del_aubio_tempo(m_tempo);
del_fvec(m_in);
del_fvec(m_out);
}
-smpl_t BeatTracker::bpm() const {
- return m_bpm;
-}
-
-void BeatTracker::processData() {
+void BeatWorker::processData() {
process(m_in->data);
}
-void BeatTracker::consumeData() {
+void BeatWorker::consumeData() {
aubio_tempo_do(m_tempo, m_in, m_out);
if (m_out->data[0] != 0.0f) {
- m_bpm = aubio_tempo_get_bpm(m_tempo);
+ emit beat(aubio_tempo_get_bpm(m_tempo));
+ }
+}
+
+BeatTracker::BeatTracker(uint_t sampleRate, uint_t hopSize, QObject* parent)
+ : AudioProvider(parent)
+ , m_bpm(120) {
+ m_worker = new BeatWorker(sampleRate, hopSize, this);
+ init();
+
+ connect(static_cast<BeatWorker*>(m_worker), &BeatWorker::beat, this, &BeatTracker::updateBpm);
+}
+
+smpl_t BeatTracker::bpm() const {
+ return m_bpm;
+}
+
+void BeatTracker::updateBpm(smpl_t bpm) {
+ if (!qFuzzyCompare(bpm + 1.0f, m_bpm + 1.0f)) {
+ m_bpm = bpm;
emit bpmChanged();
- emit beat(m_bpm);
}
}
diff --git a/plugin/src/Caelestia/beattracker.hpp b/plugin/src/Caelestia/beattracker.hpp
index b4cb559..edb9c0c 100644
--- a/plugin/src/Caelestia/beattracker.hpp
+++ b/plugin/src/Caelestia/beattracker.hpp
@@ -7,6 +7,25 @@
namespace caelestia {
+class BeatWorker : public AudioWorker {
+ Q_OBJECT
+
+public:
+ explicit BeatWorker(uint_t sampleRate = 44100, uint_t hopSize = 512, QObject* parent = nullptr);
+ ~BeatWorker();
+
+signals:
+ void beat(smpl_t bpm);
+
+private:
+ aubio_tempo_t* m_tempo;
+ fvec_t* m_in;
+ fvec_t* m_out;
+
+ void processData() override;
+ void consumeData() override;
+};
+
class BeatTracker : public AudioProvider {
Q_OBJECT
QML_ELEMENT
@@ -16,7 +35,6 @@ class BeatTracker : public AudioProvider {
public:
explicit BeatTracker(uint_t sampleRate = 44100, uint_t hopSize = 512, QObject* parent = nullptr);
- ~BeatTracker();
[[nodiscard]] smpl_t bpm() const;
@@ -25,14 +43,9 @@ signals:
void beat(smpl_t bpm);
private:
- aubio_tempo_t* m_tempo;
- fvec_t* m_in;
- fvec_t* m_out;
-
smpl_t m_bpm;
- void processData() override;
- void consumeData() override;
+ void updateBpm(smpl_t bpm);
};
} // namespace caelestia