plugin: add cava provider
This commit is contained in:
parent
2c5801237c
commit
07bee23126
4 changed files with 212 additions and 3 deletions
|
|
@ -22,6 +22,7 @@
|
|||
qt6,
|
||||
quickshell,
|
||||
aubio,
|
||||
libcava,
|
||||
xkeyboard-config,
|
||||
cmake,
|
||||
ninja,
|
||||
|
|
@ -86,7 +87,7 @@
|
|||
};
|
||||
|
||||
nativeBuildInputs = [cmake ninja pkg-config];
|
||||
buildInputs = [qt6.qtbase qt6.qtdeclarative qt6.qtmultimedia libqalculate aubio];
|
||||
buildInputs = [qt6.qtbase qt6.qtdeclarative qt6.qtmultimedia libqalculate aubio libcava];
|
||||
|
||||
dontWrapQtApps = true;
|
||||
cmakeFlags =
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
)
|
||||
|
|
|
|||
140
plugin/src/Caelestia/cavaprovider.cpp
Normal file
140
plugin/src/Caelestia/cavaprovider.cpp
Normal file
|
|
@ -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
|
||||
64
plugin/src/Caelestia/cavaprovider.hpp
Normal file
64
plugin/src/Caelestia/cavaprovider.hpp
Normal file
|
|
@ -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
|
||||
Loading…
Add table
Reference in a new issue