Reintegrate projectM Visualizer
-
@Gustavo-L-Conte
Thanks for looking into it. It seems that projectm version 3 is buggy, I think we should try to get version 4 working instead, and drop support for version 3. See the discussion on https://github.com/orgs/projectM-visualizer/discussions/820 -
@jonas roger that, gonna go back focus v4
By the way I did compile latest git from v4 with his fixes, and tested with branch visualisations, did not work, same results. I'm curious about QOpenGLWindow results, with his fixes. -
I had success starting to display stuff with v4 using QOpenGLWindow in a separate, smaller program. Soon I'll upload the code, I just need to adjust some things like resize and OpenGL context. I've used as ConsumeBuffer the pulseaudio capture code. I just need to adjust some stuff i think to make it better, but i got so excited about a positive display in v4 that i wanted to share ASAP
LoooooooooooooooooooooooooooL!
PS: still, several GL error 1282 in the majority of presets tested.
while some actually works
-
the FPS on the video is very low due: * frameskipping at the desktop recorder
- fps was set t o 10 fps for testing purposes on projectm instance
-
I'm probably NOT the founder of the I.O.G.L.N.U.S.A.H.A.
Tthe International Open GL NEVER Ugonna SLEEP AGAIN ha ha ha Society.
Decided to go ahead and do the QOpenGLWindow on Strawberry.
Its worse than just the standalone program. Much more bugs. But the few that works are incredible. We are close. Here's what I've been messing around with my IogLnusaha association (I'm on Gnomes)
// Create and setup the QOpenGLWindow auto *openGLWindow_ = new VisualizationOpenGLWidget(projectm_visualization_); QSurfaceFormat format; format.setVersion(3, 3); // Set OpenGL version to 3.3 format.setProfile(QSurfaceFormat::CoreProfile); // Use the core profile format.setDepthBufferSize(24); format.setStencilBufferSize(8); openGLWindow_->setFormat(format); openGLWindow_->resize(1280, 720); // Default size for the OpenGL window // Wrap the QOpenGLWindow in a QWidget container auto *glContainer = QWidget::createWindowContainer(openGLWindow_); glContainer->setFocusPolicy(Qt::TabFocus); glContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); glContainer->setParent(this); glContainer->setVisible(true); ////////////setCentralWidget(glContainer); // Add the overlay as a child of the container and set its visibility ////////////////overlay_->setParent(glContainer); /////////////////overlay_->resize(320, 200); // Resize the overlay //////////////////overlay_->setVisible(true); }
#ifndef VISUALIZATIONOPENGLWIDGET_H #define VISUALIZATIONOPENGLWIDGET_H #include "config.h" #include <QOpenGLWindow> #include <QOpenGLFunctions> class ProjectMVisualization; class VisualizationOpenGLWidget : public QOpenGLWindow, protected QOpenGLFunctions { Q_OBJECT public: explicit VisualizationOpenGLWidget(ProjectMVisualization* projectm_visualization, QWindow* parent = nullptr); //explicit VisualizationOpenGLWidget(ProjectMVisualization *projectm_visualization, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); //explicit OpenGLWidgetContainer(QOpenGLWindow* openGLWindow, QWidget* parent = nullptr); void initializeGL() override; protected: void paintGL() override; void resizeGL(const int width, const int height) override; private: void Setup(const int width, const int height); private: ProjectMVisualization *projectm_visualization_; }; #endif // VISUALIZATIONOPENGLWIDGET_H
.cpp
#include "config.h" #include <QPainter> #include "core/logging.h" #include "visualizationopenglwidget.h" #include "projectmvisualization.h" VisualizationOpenGLWidget::VisualizationOpenGLWidget(ProjectMVisualization *projectm_visualization, QWindow *parent) : QOpenGLWindow(NoPartialUpdate, parent), projectm_visualization_(projectm_visualization) { } void VisualizationOpenGLWidget::initializeGL() { QOpenGLWindow::initializeGL(); QOpenGLFunctions::initializeOpenGLFunctions(); projectm_visualization_->Init(); } void VisualizationOpenGLWidget::paintGL() { QPainter p(this); //////////resizeGL(width(), height()); p.beginNativePainting(); projectm_visualization_->RenderFrame(width(), height()); p.endNativePainting(); update(); qLog(Debug) << __PRETTY_FUNCTION__ << glGetError(); } void VisualizationOpenGLWidget::resizeGL(const int width, const int height) { Setup(width, height); projectm_visualization_->Resize(width, height); } void VisualizationOpenGLWidget::Setup(const int width, const int height) { glShadeModel(GL_SMOOTH); glClearColor(0, 0, 0, 0); glViewport(0, 0, width, height); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDrawBuffer(GL_BACK); glReadBuffer(GL_BACK); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_LINE_SMOOTH); glEnable(GL_POINT_SMOOTH); glClearColor(0.0F, 0.0F, 0.0F, 0.0F); glLineStipple(2, 0xAAAA); }
ConsumeBuffer:
void ProjectMVisualization::ConsumeBuffer(GstBuffer *buffer, const int pipeline_id, const QString &format) { Q_UNUSED(pipeline_id); Q_UNUSED(format); GstMapInfo map; gst_buffer_map(buffer, &map, GST_MAP_READ); #ifdef HAVE_PROJECTM4 if (projectm_instance_) { const unsigned int samples_per_channel = static_cast<unsigned int> (map.size / sizeof(size_t)) / 2; const int16_t *data = reinterpret_cast<int16_t*>(map.data); projectm_pcm_add_int16(projectm_instance_, data, samples_per_channel, PROJECTM_STEREO); } #else if (projectm_) { const short samples_per_channel = static_cast<short>(map.size) / sizeof(short) / 2; const short *data = reinterpret_cast<short*>(map.data); projectm_->pcm()->addPCM16Data(data, samples_per_channel); } #endif // HAVE_PROJECTM4 gst_buffer_unmap(buffer, &map); gst_buffer_unref(buffer); }
InitprojectM
// Create projectM settings #ifdef HAVE_PROJECTM4 Q_ASSERT(projectm_instance_ == nullptr); Q_ASSERT(projectm_playlist_instance_ == nullptr); projectm_instance_ = projectm_create(); projectm_set_preset_duration(projectm_instance_, duration_); // Set initial window size projectm_set_window_size(projectm_instance_, 1280, 720); // Additional ProjectM setup projectm_set_mesh_size(projectm_instance_, 32, 24); projectm_set_fps(projectm_instance_, 60); projectm_set_aspect_correction(projectm_instance_, true); projectm_set_hard_cut_enabled(projectm_instance_, true); projectm_set_hard_cut_duration(projectm_instance_, 10); projectm_set_hard_cut_sensitivity(projectm_instance_, 1.0); projectm_set_beat_sensitivity(projectm_instance_, 0.5); projectm_set_soft_cut_duration(projectm_instance_, 10); //projectm_set_window_size(projectm_instance_, 512, 512); const char *texture_search_paths[] = { "/usr/local/share/projectM/textures" }; projectm_set_texture_search_paths(projectm_instance_, texture_search_paths, 1); projectm_playlist_instance_ = projectm_playlist_create(projectm_instance_); projectm_playlist_set_shuffle(projectm_playlist_instance_, false);
"patched" SetImmediatePreset (FOR TESTING, but works on v3) void ProjectMVisualization::SetImmediatePreset(const int index) { #ifdef HAVE_PROJECTM4 if (projectm_playlist_instance_) { projectm_playlist_set_position(projectm_playlist_instance_, index, true); /* projectm_playlist_play_previous(projectm_playlist_instance_, true); projectm_set_preset_duration(projectm_instance_, 1); projectm_set_preset_locked(projectm_instance_, false); QTimer::singleShot(1500, this, [index,this]() { projectm_set_preset_locked(projectm_instance_, true); projectm_set_preset_duration(projectm_instance_, duration_); }); */ } #else if (projectm_) { projectm_->selectPreset(index, true); } #endif // HAVE_PROJECTM4 }
It appears that when it wraps to a QWidget, the shader codes don't like and start to stop doing its things. ITs not a matter of context, but its like a corruption happens when you wrap.
I could manage to bind key_S to open the visualizations selector, when setting as central, because I could not figure out why the overlay and interface don't work -
this version I posted works MUCH better giving nice results with the cream-of-cream presets,
-
void VisualizationOpenGLWidget::paintGL() { QPainter p(this); p.beginNativePainting(); int w = width(); int h = height(); this->resize(w*2,h*2); if (projectm_visualization_) { projectm_visualization_->Resize(w*2, h*2); projectm_visualization_->RenderFrame(w*2,h*2); } update(); this->resize(w,h); if (projectm_visualization_) { projectm_visualization_->Resize(w, h); projectm_visualization_->RenderFrame(w,h); } update(); p.endNativePainting(); qLog(Debug) << __PRETTY_FUNCTION__ << glGetError(); }
Its disgusting, but it really makes a HUGE percentage of presets to display instead of black screen.
Ive done this without wraping the openglwindow into a widget;
when you resize the window, it displays, several plugins happens that way.. actually the nicer ones like this one: -
VisualizationContainer::VisualizationContainer(QWidget *parent) : QMainWindow(parent), projectm_visualization_(new ProjectMVisualization(this)), overlay_(new VisualizationOverlay), selector_(new VisualizationSelector(this)), overlay_proxy_(nullptr), engine_(nullptr), menu_(new QMenu(this)), fps_(kDefaultFps), size_(kDefaultTextureSize) { setWindowTitle(tr("Visualizations")); setWindowIcon(IconLoader::Load(QStringLiteral("strawberry"))); setMinimumSize(64, 64); { Settings s; s.beginGroup(QLatin1String(kSettingsGroup)); if (!restoreGeometry(s.value("geometry").toByteArray())) { resize(kDefaultWidth, kDefaultHeight); } fps_ = s.value("fps", kDefaultFps).toInt(); size_ = s.value("size", kDefaultTextureSize).toInt(); s.endGroup(); } QShortcut *close = new QShortcut(QKeySequence::Close, this); QObject::connect(close, &QShortcut::activated, this, &VisualizationContainer::close); QObject::connect(overlay_, &VisualizationOverlay::OpacityChanged, this, &VisualizationContainer::ChangeOverlayOpacity); QObject::connect(overlay_, &VisualizationOverlay::ShowPopupMenu, this, &VisualizationContainer::ShowPopupMenu); ChangeOverlayOpacity(1.0); projectm_visualization_->SetTextureSize(size_); SizeChanged(); selector_->SetVisualization(projectm_visualization_); menu_->addAction(IconLoader::Load(QStringLiteral("view-fullscreen")), tr("Toggle fullscreen"), this, &VisualizationContainer::ToggleFullscreen); QMenu *fps_menu = menu_->addMenu(tr("Framerate")); QActionGroup *fps_group = new QActionGroup(this); AddFramerateMenuItem(tr("Low (%1 fps)").arg(kLowFramerate), kLowFramerate, fps_, fps_group); AddFramerateMenuItem(tr("Medium (%1 fps)").arg(kMediumFramerate), kMediumFramerate, fps_, fps_group); AddFramerateMenuItem(tr("High (%1 fps)").arg(kHighFramerate), kHighFramerate, fps_, fps_group); AddFramerateMenuItem(tr("Super high (%1 fps)").arg(kSuperHighFramerate), kSuperHighFramerate, fps_, fps_group); fps_menu->addActions(fps_group->actions()); QMenu *quality_menu = menu_->addMenu(tr("Quality", "Visualization quality")); QActionGroup *quality_group = new QActionGroup(this); AddQualityMenuItem(tr("Low (256x256)"), 256, size_, quality_group); AddQualityMenuItem(tr("Medium (512x512)"), 512, size_, quality_group); AddQualityMenuItem(tr("High (1024x1024)"), 1024, size_, quality_group); AddQualityMenuItem(tr("Super high (2048x2048)"), 2048, size_, quality_group); quality_menu->addActions(quality_group->actions()); menu_->addAction(tr("Select visualizations..."), selector_, &VisualizationContainer::show); menu_->addSeparator(); menu_->addAction(IconLoader::Load(QStringLiteral("application-exit")), tr("Close visualization"), this, &VisualizationContainer::hide); // OpenGL // Create and configure the QSurfaceFormat QSurfaceFormat format; format.setVersion(3, 3); // Set OpenGL version to 3.3 format.setProfile(QSurfaceFormat::CoreProfile); // Use the core profile format.setDepthBufferSize(24); format.setStencilBufferSize(8); // Initialize the primary OpenGL context QOpenGLContext *openGLContext = new QOpenGLContext(this); openGLContext->setFormat(format); if (!openGLContext->create()) { qWarning() << "Failed to create OpenGL context"; } else { qDebug() << "OpenGL context created successfully"; } // Create a new shared context QOpenGLContext *sharedContext = new QOpenGLContext(this); sharedContext->setFormat(format); if (!sharedContext->create()) { qWarning() << "Failed to create shared OpenGL context"; } else { qDebug() << "Shared OpenGL context created successfully"; } // Set the shared context openGLContext->setShareContext(sharedContext); // Create the OpenGL window using the primary OpenGL context openGLWindow_ = new VisualizationOpenGLWidget(projectm_visualization_, openGLContext); openGLWindow_->setFormat(format); // Make the OpenGL context current if (!openGLContext->makeCurrent(openGLWindow_)) { qWarning() << "Failed to make OpenGL context current"; } // Wrap the QOpenGLWindow in a QWidget container glContainer = QWidget::createWindowContainer(openGLWindow_); glContainer->setFocusPolicy(Qt::TabFocus); glContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); glContainer->setParent(this); glContainer->setVisible(true); setCentralWidget(glContainer); projectm_visualization_->Resize(width(), height()); // Add the overlay as a child of the container and set its visibility overlay_->setParent(glContainer); overlay_->resize(glContainer->size()); // Resize the overlay to match the container size overlay_->setVisible(true); overlay_->raise(); }
#ifndef VISUALIZATIONOPENGLWIDGET_H #define VISUALIZATIONOPENGLWIDGET_H #include "config.h" #include <QOpenGLWindow> #include <QOpenGLFunctions> class ProjectMVisualization; class VisualizationOpenGLWidget : public QOpenGLWindow, protected QOpenGLFunctions { Q_OBJECT public: explicit VisualizationOpenGLWidget(ProjectMVisualization* projectm_visualization, QOpenGLContext* sharedContext, QWindow* parent = nullptr); void initializeGL() override; protected: void paintGL() override; void resizeGL(int width, int height) override; private: void Setup(int width, int height); ProjectMVisualization* projectm_visualization_; QOpenGLContext* sharedContext_; }; #endif // VISUALIZATIONOPENGLWIDGET_H
#include "config.h" #include <QPainter> #include "core/logging.h" #include "visualizationopenglwidget.h" #include "projectmvisualization.h" VisualizationOpenGLWidget::VisualizationOpenGLWidget(ProjectMVisualization* projectm_visualization, QOpenGLContext* sharedContext, QWindow* parent) : QOpenGLWindow(NoPartialUpdate, parent), projectm_visualization_(projectm_visualization), sharedContext_(sharedContext) { setFormat(sharedContext_->format()); } void VisualizationOpenGLWidget::initializeGL() { QOpenGLWindow::initializeGL(); sharedContext_->makeCurrent(this); initializeOpenGLFunctions(); projectm_visualization_->Init(); } void VisualizationOpenGLWidget::paintGL() { sharedContext_->makeCurrent(this); QPainter p(this); p.beginNativePainting(); if (projectm_visualization_) { Setup(width(), height()); projectm_visualization_->RenderFrame(width(), height()); } update(); p.endNativePainting(); GLenum error = glGetError(); if (error != GL_NO_ERROR) { qWarning() << "OpenGL error in paintGL:" << error; } qLog(Debug) << __PRETTY_FUNCTION__ << "Completed"; } void VisualizationOpenGLWidget::resizeGL(int width, int height) { sharedContext_->makeCurrent(this); Setup(width, height); projectm_visualization_->Resize(width, height); GLenum error = glGetError(); if (error != GL_NO_ERROR) { qWarning() << "OpenGL error in resizeGL:" << error; } } void VisualizationOpenGLWidget::Setup(int width, int height) { // Ensure the correct OpenGL context is current if (!sharedContext_->makeCurrent(this)) { qWarning() << "Failed to make OpenGL context current in Setup"; return; } // Initialize OpenGL functions initializeOpenGLFunctions(); // Set up OpenGL state glShadeModel(GL_SMOOTH); glClearColor(0, 0, 0, 0); glViewport(0, 0, width, height); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, width, height, 0, -1, 1); // Set an orthographic projection matrix glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDrawBuffer(GL_BACK); glReadBuffer(GL_BACK); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_LINE_SMOOTH); glEnable(GL_POINT_SMOOTH); glClearColor(0.0F, 0.0F, 0.0F, 0.0F); glLineStipple(2, 0xAAAA); GLenum error = glGetError(); if (error != GL_NO_ERROR) { qWarning() << "OpenGL error in Setup:" << error; } }
-
-
I'm trying lotsa stuff, using FBO and OpenGLWindow, changing profiles (between versions and compatibility/core), I even made an SDL2 window version. Every time I implement a different way, All result are the same: some presets working and the same majority not working.
But now I've tried bypassing the normal way presets are loaded, and only use this, i.e.
projectm_playlist_add_path(projectm_playlist_instance_, "/home/guzpido/presets-cream-of-the-crop", true, true);
it appears to make a bit more presets to work. this could be a clue. Maybe the way we are loading presets has some issue; but I've banging my head to figure it out if this makes even sense.
edit: also i cant determine if ConsumeBuffer is working
-
I finally understood the mystery of why size_t is accidentally working in ConsumeBuffer: all my FLACs are S24LE, I just tested now one of my own songs which are S16LE and the correct int16 aapproach works for detecting and interacting audio with visuals! Therefore Im trying now to make a ConsumeBuffer with lasers to determine automatically the format and width: (but no luck making S24LE to work)
#include <QtCore/QtEndian> void VisualizationSDL2::ConsumeBuffer(GstBuffer *buffer, const int pipeline_id, const QString &format) { Q_UNUSED(pipeline_id); GstMapInfo map; gst_buffer_map(buffer, &map, GST_MAP_READ); if (projectm_instance_) { unsigned int sample_count = 0; const int16_t *int16_data = nullptr; const float *float_data = nullptr; if (format == QStringLiteral("S16LE") || format == QStringLiteral("S16BE")) { sample_count = map.size / sizeof(int16_t) / 2; int16_data = reinterpret_cast<const int16_t *>(map.data); if (format == QStringLiteral("S16BE")) { // Convert big-endian to little-endian if necessary std::vector<int16_t> temp_data(sample_count * 2); for (unsigned int i = 0; i < sample_count * 2; ++i) { temp_data[i] = qFromBigEndian(int16_data[i]); } int16_data = temp_data.data(); } projectm_pcm_add_int16(projectm_instance_, int16_data, sample_count, PROJECTM_STEREO); } else if (format == QStringLiteral("F32LE") || format == QStringLiteral("F32BE")) { sample_count = map.size / sizeof(float) / 2; float_data = reinterpret_cast<const float *>(map.data); if (format == QStringLiteral("F32BE")) { // Convert big-endian to little-endian if necessary std::vector<float> temp_data(sample_count * 2); for (unsigned int i = 0; i < sample_count * 2; ++i) { temp_data[i] = qFromBigEndian(float_data[i]); } float_data = temp_data.data(); } projectm_pcm_add_float(projectm_instance_, float_data, sample_count, PROJECTM_STEREO); } else if (format == QStringLiteral("S24LE")) { // Handle 24-bit audio (3 bytes per sample) sample_count = map.size / 3 / 2; std::vector<int16_t> int16_samples(sample_count * 2); for (unsigned int i = 0; i < sample_count * 2; ++i) { int16_samples[i] = static_cast<int16_t>((map.data[i * 3 + 2] << 8) | map.data[i * 3 + 1]); } projectm_pcm_add_int16(projectm_instance_, int16_samples.data(), sample_count, PROJECTM_STEREO); } gst_buffer_unmap(buffer, &map); } gst_buffer_unref(buffer); }