diff options
| author | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-09-06 20:43:17 +1000 |
|---|---|---|
| committer | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-09-06 20:43:17 +1000 |
| commit | 929d8fa40e371a2adbe1fe0f93f0ebf11bcb0981 (patch) | |
| tree | 965f95279a862cf5853eb6af8ac60938a784a685 /plugin/src | |
| parent | plugin: fix cmake version (diff) | |
| download | caelestia-shell-929d8fa40e371a2adbe1fe0f93f0ebf11bcb0981.tar.gz caelestia-shell-929d8fa40e371a2adbe1fe0f93f0ebf11bcb0981.tar.bz2 caelestia-shell-929d8fa40e371a2adbe1fe0f93f0ebf11bcb0981.zip | |
plugin: async audio processing
Diffstat (limited to 'plugin/src')
| -rw-r--r-- | plugin/src/Caelestia/audioprovider.cpp | 84 | ||||
| -rw-r--r-- | plugin/src/Caelestia/audioprovider.hpp | 33 | ||||
| -rw-r--r-- | plugin/src/Caelestia/beattracker.cpp | 39 | ||||
| -rw-r--r-- | plugin/src/Caelestia/beattracker.hpp | 27 |
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 |