/*
* 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
*/
#ifndef AUDIOMODEL_H
#define AUDIOMODEL_H
#include "Komplex_global.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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 createBlackmanWindow(int size);
static std::vector smoothData(const std::vector& data, int windowSize = 5);
struct impl
{
pw_main_loop *loop;
pw_stream *stream;
spa_audio_info format;
unsigned move:1;
QVector samples; // we need at least 2048 samples
QVector 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