From af43c1166e8ace50954dd1c6ac86769810104b53 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Sat, 6 Sep 2025 16:57:40 +1000 Subject: plugin: abstract audioprovider --- plugin/src/Caelestia/CMakeLists.txt | 3 +- plugin/src/Caelestia/audioprovider.cpp | 78 ++++++++++++++++++++++++++++++++++ plugin/src/Caelestia/audioprovider.hpp | 36 ++++++++++++++++ plugin/src/Caelestia/beattracker.cpp | 74 +++++--------------------------- plugin/src/Caelestia/beattracker.hpp | 16 ++----- 5 files changed, 131 insertions(+), 76 deletions(-) create mode 100644 plugin/src/Caelestia/audioprovider.cpp create mode 100644 plugin/src/Caelestia/audioprovider.hpp (limited to 'plugin/src') diff --git a/plugin/src/Caelestia/CMakeLists.txt b/plugin/src/Caelestia/CMakeLists.txt index 464ed98..8a6152a 100644 --- a/plugin/src/Caelestia/CMakeLists.txt +++ b/plugin/src/Caelestia/CMakeLists.txt @@ -4,7 +4,7 @@ pkg_check_modules(AUBIO REQUIRED aubio) qt_add_qml_module(caelestia URI Caelestia - VERSION 0.1 + VERSION ${VERSION} SOURCES cutils.hpp cutils.cpp cachingimagemanager.hpp cachingimagemanager.cpp @@ -13,6 +13,7 @@ qt_add_qml_module(caelestia beattracker.hpp beattracker.cpp service.hpp service.cpp serviceref.hpp serviceref.cpp + audioprovider.hpp audioprovider.cpp ) qt_query_qml_module(caelestia diff --git a/plugin/src/Caelestia/audioprovider.cpp b/plugin/src/Caelestia/audioprovider.cpp new file mode 100644 index 0000000..0c6f4fd --- /dev/null +++ b/plugin/src/Caelestia/audioprovider.cpp @@ -0,0 +1,78 @@ +#include "audioprovider.hpp" + +#include "service.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace caelestia { + +AudioProvider::AudioProvider(int sampleRate, int hopSize, QObject* parent) + : Service(parent) + , m_hopSize(hopSize) { + QAudioFormat format; + format.setSampleRate(sampleRate); + format.setChannelCount(1); + format.setSampleFormat(QAudioFormat::Int16); + + m_source = new QAudioSource(QMediaDevices::defaultAudioInput(), format, this); + connect(m_source, &QAudioSource::stateChanged, this, &AudioProvider::handleStateChanged); +}; + +AudioProvider::~AudioProvider() { + m_source->stop(); + delete m_source; +} + +void AudioProvider::start() { + m_device = m_source->start(); + connect(m_device, &QIODevice::readyRead, this, &AudioProvider::processData); +} + +void AudioProvider::stop() { + m_source->stop(); + m_device = nullptr; +} + +template void AudioProvider::process(T* outBuf) { + const QByteArray data = m_device->readAll(); + const int16_t* samples = reinterpret_cast(data.constData()); + const size_t count = static_cast(data.size()) / sizeof(int16_t); + const size_t hopSize = static_cast(m_hopSize); + + for (size_t i = 0; i < count; ++i) { + outBuf[i % hopSize] = static_cast(samples[i] / 32768.0); + if ((i + 1) % hopSize == 0) { + consumeData(); + } + } +} +template void AudioProvider::process(float* outBuf); +template void AudioProvider::process(double* outBuf); + +void AudioProvider::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"; + break; + case QtAudio::IOError: + qWarning() << "AudioProvider: 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"; + break; + case QtAudio::FatalError: + qCritical() << "AudioProvider: fatal error in audio device"; + break; + default: + break; + } + } +} + +} // namespace caelestia diff --git a/plugin/src/Caelestia/audioprovider.hpp b/plugin/src/Caelestia/audioprovider.hpp new file mode 100644 index 0000000..5564dc5 --- /dev/null +++ b/plugin/src/Caelestia/audioprovider.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "service.hpp" +#include +#include +#include +#include + +namespace caelestia { + +class AudioProvider : public Service { + Q_OBJECT + +public: + explicit AudioProvider(int sampleRate = 44100, int hopSize = 512, QObject* parent = nullptr); + ~AudioProvider(); + +protected: + int m_hopSize; + + template void process(T* outBuf); + +private: + QAudioSource* m_source; + QIODevice* m_device; + + void start() override; + void stop() override; + + void handleStateChanged(QtAudio::State state) const; + + virtual void processData() = 0; + virtual void consumeData() = 0; +}; + +} // namespace caelestia diff --git a/plugin/src/Caelestia/beattracker.cpp b/plugin/src/Caelestia/beattracker.cpp index aeca64b..a915c08 100644 --- a/plugin/src/Caelestia/beattracker.cpp +++ b/plugin/src/Caelestia/beattracker.cpp @@ -1,90 +1,38 @@ #include "beattracker.hpp" -#include "service.hpp" -#include -#include -#include -#include +#include "audioprovider.hpp" #include #include namespace caelestia { BeatTracker::BeatTracker(uint_t sampleRate, uint_t hopSize, QObject* parent) - : Service(parent) + : AudioProvider(static_cast(sampleRate), static_cast(hopSize), parent) , m_tempo(new_aubio_tempo("default", 1024, hopSize, sampleRate)) , m_in(new_fvec(hopSize)) , m_out(new_fvec(2)) - , m_hopSize(hopSize) - , m_bpm(120) { - QAudioFormat format; - format.setSampleRate(static_cast(sampleRate)); - format.setChannelCount(1); - format.setSampleFormat(QAudioFormat::Int16); - - m_source = new QAudioSource(QMediaDevices::defaultAudioInput(), format, this); - connect(m_source, &QAudioSource::stateChanged, this, &BeatTracker::handleStateChanged); -}; + , m_bpm(120) {}; BeatTracker::~BeatTracker() { del_aubio_tempo(m_tempo); del_fvec(m_in); del_fvec(m_out); - - m_source->stop(); - delete m_source; } smpl_t BeatTracker::bpm() const { return m_bpm; } -void BeatTracker::start() { - m_device = m_source->start(); - connect(m_device, &QIODevice::readyRead, this, &BeatTracker::process); -} - -void BeatTracker::stop() { - m_source->stop(); - m_device = nullptr; -} - -void BeatTracker::process() { - const QByteArray data = m_device->readAll(); - const int16_t* samples = reinterpret_cast(data.constData()); - const size_t count = static_cast(data.size()) / sizeof(int16_t); - - for (size_t i = 0; i < count; ++i) { - m_in->data[i % m_hopSize] = samples[i] / 32768.0f; - if ((i + 1) % m_hopSize == 0) { - 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 bpmChanged(); - emit beat(m_bpm); - } - } - } +void BeatTracker::processData() { + process(m_in->data); } -void BeatTracker::handleStateChanged(QtAudio::State state) const { - if (state == QtAudio::StoppedState && m_source->error() != QtAudio::NoError) { - switch (m_source->error()) { - case QtAudio::OpenError: - qWarning() << "BeatTracker: failed to open audio device"; - break; - case QtAudio::IOError: - qWarning() << "BeatTracker: an error occurred during read/write of audio device"; - break; - case QtAudio::UnderrunError: - qWarning() << "BeatTracker: audio data is not being fed to audio device fast enough"; - break; - case QtAudio::FatalError: - qCritical() << "BeatTracker: fatal error in audio device"; - break; - default: - break; - } +void BeatTracker::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 bpmChanged(); + emit beat(m_bpm); } } diff --git a/plugin/src/Caelestia/beattracker.hpp b/plugin/src/Caelestia/beattracker.hpp index 7c7fada..b4cb559 100644 --- a/plugin/src/Caelestia/beattracker.hpp +++ b/plugin/src/Caelestia/beattracker.hpp @@ -1,15 +1,13 @@ #pragma once -#include "service.hpp" -#include -#include +#include "audioprovider.hpp" #include #include #include namespace caelestia { -class BeatTracker : public Service { +class BeatTracker : public AudioProvider { Q_OBJECT QML_ELEMENT QML_SINGLETON @@ -27,20 +25,14 @@ signals: void beat(smpl_t bpm); private: - QAudioSource* m_source; - QIODevice* m_device; - aubio_tempo_t* m_tempo; fvec_t* m_in; fvec_t* m_out; - uint_t m_hopSize; smpl_t m_bpm; - void start() override; - void stop() override; - void process(); - void handleStateChanged(QtAudio::State state) const; + void processData() override; + void consumeData() override; }; } // namespace caelestia -- cgit v1.2.3-freya