diff options
| author | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-09-07 15:18:23 +1000 |
|---|---|---|
| committer | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-09-07 15:18:23 +1000 |
| commit | 07bee231269399546e81539bb58149dd11ac4aa9 (patch) | |
| tree | 1d6c9ad880a9299d0f3f275c353ebb8257ef9ae2 /plugin | |
| parent | plugin/ap: properly buffer data (diff) | |
| download | caelestia-shell-07bee231269399546e81539bb58149dd11ac4aa9.tar.gz caelestia-shell-07bee231269399546e81539bb58149dd11ac4aa9.tar.bz2 caelestia-shell-07bee231269399546e81539bb58149dd11ac4aa9.zip | |
plugin: add cava provider
Diffstat (limited to 'plugin')
| -rw-r--r-- | plugin/src/Caelestia/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | plugin/src/Caelestia/cavaprovider.cpp | 140 | ||||
| -rw-r--r-- | plugin/src/Caelestia/cavaprovider.hpp | 64 |
3 files changed, 210 insertions, 2 deletions
diff --git a/plugin/src/Caelestia/CMakeLists.txt b/plugin/src/Caelestia/CMakeLists.txt index 5ce0800..c1f3d15 100644 --- a/plugin/src/Caelestia/CMakeLists.txt +++ b/plugin/src/Caelestia/CMakeLists.txt @@ -1,6 +1,7 @@ find_package(PkgConfig REQUIRED) pkg_check_modules(QALCULATE REQUIRED libqalculate) pkg_check_modules(AUBIO REQUIRED aubio) +pkg_check_modules(CAVA REQUIRED cava) qt_add_qml_module(caelestia URI Caelestia @@ -14,6 +15,7 @@ qt_add_qml_module(caelestia service.hpp service.cpp serviceref.hpp serviceref.cpp audioprovider.hpp audioprovider.cpp + cavaprovider.hpp cavaprovider.cpp ) qt_query_qml_module(caelestia @@ -33,8 +35,10 @@ install(TARGETS "${module_plugin_target}" LIBRARY DESTINATION "${module_dir}" RU install(FILES "${module_qmldir}" DESTINATION "${module_dir}") install(FILES "${module_typeinfo}" DESTINATION "${module_dir}") -target_include_directories(caelestia SYSTEM PRIVATE ${QALCULATE_INCLUDE_DIRS} ${AUBIO_INCLUDE_DIRS}) +target_include_directories(caelestia SYSTEM PRIVATE + ${QALCULATE_INCLUDE_DIRS} ${AUBIO_INCLUDE_DIRS} ${CAVA_INCLUDE_DIRS} +) target_link_libraries(caelestia PRIVATE Qt::Core Qt::Qml Qt::Gui Qt::Concurrent Qt::Multimedia - ${QALCULATE_LIBRARIES} ${AUBIO_LIBRARIES} + ${QALCULATE_LIBRARIES} ${AUBIO_LIBRARIES} ${CAVA_LIBRARIES} ) diff --git a/plugin/src/Caelestia/cavaprovider.cpp b/plugin/src/Caelestia/cavaprovider.cpp new file mode 100644 index 0000000..ffc98a6 --- /dev/null +++ b/plugin/src/Caelestia/cavaprovider.cpp @@ -0,0 +1,140 @@ +#include "cavaprovider.hpp" + +#include "audioprovider.hpp" +#include <QDebug> +#include <QObject> +#include <cava/cavacore.h> +#include <cmath> +#include <cstddef> + +namespace caelestia { + +CavaProcessor::CavaProcessor(AudioProvider* provider, QObject* parent) + : AudioProcessor(provider, parent) + , m_plan(nullptr) + , m_in(new double[static_cast<size_t>(m_chunkSize)]) + , m_out(nullptr) + , m_bars(0) {}; + +CavaProcessor::~CavaProcessor() { + cleanup(); + delete[] m_in; +} + +void CavaProcessor::setBars(int bars) { + if (bars < 0) { + qWarning() << "CavaProcessor::setBars: bars must be greater than 0. Setting to 0."; + bars = 0; + } + + if (m_bars != bars) { + m_bars = bars; + reload(); + } +} + +void CavaProcessor::reload() { + cleanup(); + initCava(); +} + +void CavaProcessor::cleanup() { + if (!m_plan) { + return; + } + + cava_destroy(m_plan); + m_plan = nullptr; + + if (m_out) { + delete[] m_out; + m_out = nullptr; + } +} + +void CavaProcessor::initCava() { + if (m_plan || m_bars == 0) { + return; + } + + m_plan = cava_init(m_bars, static_cast<unsigned int>(m_sampleRate), 1, 1, 0.85, 50, 10000); + + if (m_plan->status == -1) { + qWarning() << "CavaProcessor::initCava: failed to initialise cava plan"; + cleanup(); + return; + } + + m_out = new double[static_cast<size_t>(m_bars)]; +} + +void CavaProcessor::processChunk(const QVector<double>& chunk) { + if (!m_plan || m_bars == 0) { + return; + } + + std::copy(chunk.constBegin(), chunk.constEnd(), m_in); + + // Process in data via cava + cava_execute(m_in, m_chunkSize, m_out, m_plan); + + // Apply monstercat filter + for (int i = 0; i < m_bars; i++) { + for (int j = i - 1; j >= 0; j--) { + m_out[j] = std::max(m_out[i] / std::pow(1.5, i - j), m_out[j]); + } + for (int j = i + 1; j < m_bars; j++) { + m_out[j] = std::max(m_out[i] / std::pow(1.5, j - i), m_out[j]); + } + } + + // Update values + QVector<double> values(m_bars); + std::copy(m_out, m_out + m_bars, values.begin()); + if (values != m_values) { + m_values = std::move(values); + emit valuesChanged(m_values); + } +} + +CavaProvider::CavaProvider(int sampleRate, int chunkSize, QObject* parent) + : AudioProvider(sampleRate, chunkSize, parent) + , m_bars(0) { + m_processor = new CavaProcessor(this); + init(); + + connect(static_cast<CavaProcessor*>(m_processor), &CavaProcessor::valuesChanged, this, &CavaProvider::updateValues); +} + +int CavaProvider::bars() const { + return m_bars; +} + +void CavaProvider::setBars(int bars) { + if (bars < 0) { + qWarning() << "CavaProvider::setBars: bars must be greater than 0. Setting to 0."; + bars = 0; + } + + if (m_bars == bars) { + return; + } + + m_bars = bars; + emit barsChanged(); + + QMetaObject::invokeMethod(m_processor, "setBars", Qt::QueuedConnection, Q_ARG(int, bars)); +} + +QVector<double> CavaProvider::values() const { + return m_values; +} + +void CavaProvider::updateValues(QVector<double> values) { + if (values != m_values) { + m_values = std::move(values); + emit valuesChanged(); + } +} + +} // namespace caelestia diff --git a/plugin/src/Caelestia/cavaprovider.hpp b/plugin/src/Caelestia/cavaprovider.hpp new file mode 100644 index 0000000..d819f54 --- /dev/null +++ b/plugin/src/Caelestia/cavaprovider.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include "audioprovider.hpp" +#include <QObject> +#include <cava/cavacore.h> +#include <qqmlintegration.h> + +namespace caelestia { + +class CavaProcessor : public AudioProcessor { + Q_OBJECT + +public: + explicit CavaProcessor(AudioProvider* provider, QObject* parent = nullptr); + ~CavaProcessor(); + +signals: + void valuesChanged(QVector<double> values); + +private: + struct cava_plan* m_plan; + double* m_in; + double* m_out; + + int m_bars; + QVector<double> m_values; + + Q_INVOKABLE void setBars(int bars); + + void reload(); + void initCava(); + void cleanup(); + + void processChunk(const QVector<double>& chunk) override; +}; + +class CavaProvider : public AudioProvider { + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(int bars READ bars WRITE setBars NOTIFY barsChanged) + + Q_PROPERTY(QVector<double> values READ values NOTIFY valuesChanged) + +public: + explicit CavaProvider(int sampleRate = 48000, int chunkSize = 512, QObject* parent = nullptr); + + [[nodiscard]] int bars() const; + void setBars(int bars); + + [[nodiscard]] QVector<double> values() const; + +signals: + void barsChanged(); + void valuesChanged(); + +private: + int m_bars; + QVector<double> m_values; + + void updateValues(QVector<double> values); +}; + +} // namespace caelestia |