Files
komplex/plugin/AudioModel.h
2025-08-10 05:06:15 -04:00

136 lines
4.0 KiB
C++

/*
* Komplex Wallpaper Engine
* Copyright (C) 2025 @DigitalArtifex | github.com/DigitalArtifex
*
* AudioModel.h
*
* This is pretty much just a reimplementation of the audiocapture example
* from the PipeWire docs.
*
* NOTICE:
* The spectrum data is currently out of spec according to the documentation
* https://webaudio.github.io/web-audio-api/#smoothing-over-time
*
* The described smoothing method was resulting in inconsistent data. This
* is likely due to a poor implementation. A linear smoothing algo seems to work
* (at least visually). Will need to revisit the temporal implementation if
* things do not work as expected.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>
*/
#ifndef AUDIOMODEL_H
#define AUDIOMODEL_H
#include "Komplex_global.h"
#include <QObject>
#include <QString>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonParseError>
#include <QThread>
#include <QtEndian>
#include <QPixmap>
#include <QQmlEngine>
#include <QJSValue>
#include <QVector>
#include <QPainter>
#include <QBrush>
#include <QPen>
#include <QThread>
#include <QMutex>
#include <QtConcurrent/QtConcurrent>
#include <QtQml/qqmlregistration.h>
#include <complex>
#include <pipewire/pipewire.h>
#include <spa/param/audio/raw.h>
#include <spa/pod/pod.h>
#include <spa/pod/builder.h>
#include <spa/param/format-types.h>
#include <spa/param/buffers.h>
#include <spa/param/audio/format-utils.h>
class KOMPLEX_EXPORT AudioModel : public QObject
{
Q_OBJECT
QML_SINGLETON
QML_NAMED_ELEMENT(AudioModel)
public:
AudioModel(QObject *parent = nullptr);
~AudioModel();
/**!
* @brief frame
* This function returns the current audio frame as a QPixmap.
* It is expected to be called after the frameChanged signal is emitted, if using from CPP
*
* If it is being used from QML, it will need to be resolved from the AuidoTexture Image Provider (image:/audio/frame#.jpg).
* See AudioImage provider for more details.
*
* @return QPixmap containing the current audio frame.
*/
static QPixmap frame();
// Q_INVOKABLE bool init();
Q_INVOKABLE static void startCapture();
Q_INVOKABLE static void stopCapture();
private Q_SLOTS:
static void startCaptureAsync();
private:
static std::vector<double> createBlackmanWindow(int size);
static std::vector<float> smoothData(const std::vector<float>& data, int windowSize = 5);
struct impl
{
pw_main_loop *loop;
pw_stream *stream;
spa_audio_info format;
unsigned move:1;
QVector<qreal> samples; // we need at least 2048 samples
QVector<qreal> smoothed; // we're supposed to save for smoothing, but I couldn't get this method to work
qreal last;
};
inline static AudioModel *m_instance = nullptr;
inline static QThread *m_thread = nullptr;
inline static QMutex m_mutex;
QPixmap m_frame;
inline static impl m_impl_data;
inline static bool m_running = false;
static void on_process(void *user_data);
static void do_quit(void *user_data, int signal_number);
static void on_stream_param_changed(void *_data, uint32_t id, const struct spa_pod *param);
inline static const struct pw_stream_events stream_events = {
.version = PW_VERSION_STREAM_EVENTS,
.param_changed = on_stream_param_changed,
.process = on_process,
};
};
Q_DECLARE_METATYPE(AudioModel)
#endif