2010-06-06 16:06:23 +02:00
|
|
|
/* This file is part of Clementine.
|
2010-11-20 14:27:10 +01:00
|
|
|
Copyright 2010, David Sansome <me@davidsansome.com>
|
2010-06-06 16:06:23 +02:00
|
|
|
|
|
|
|
Clementine 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.
|
|
|
|
|
|
|
|
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "projectmvisualisation.h"
|
|
|
|
|
2010-07-10 19:21:23 +02:00
|
|
|
#include <QCoreApplication>
|
|
|
|
#include <QDir>
|
|
|
|
#include <QFile>
|
2010-06-06 20:18:06 +02:00
|
|
|
#include <QGLWidget>
|
|
|
|
#include <QGraphicsView>
|
2010-07-10 19:21:23 +02:00
|
|
|
#include <QMessageBox>
|
|
|
|
#include <QPaintEngine>
|
|
|
|
#include <QPainter>
|
2010-06-07 03:55:21 +02:00
|
|
|
#include <QSettings>
|
2010-07-18 15:26:01 +02:00
|
|
|
#include <QTemporaryFile>
|
2010-07-10 19:21:23 +02:00
|
|
|
#include <QTimerEvent>
|
2020-09-18 16:15:19 +02:00
|
|
|
#include <QtDebug>
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "projectmpresetmodel.h"
|
|
|
|
#include "visualisationcontainer.h"
|
2010-06-06 16:06:23 +02:00
|
|
|
|
2021-04-09 07:58:26 +02:00
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
|
|
|
|
#include <QRandomGenerator>
|
|
|
|
#endif
|
|
|
|
|
2010-07-10 22:21:06 +02:00
|
|
|
#ifdef USE_SYSTEM_PROJECTM
|
2014-02-07 16:34:20 +01:00
|
|
|
#include <libprojectM/projectM.hpp>
|
2010-07-10 22:21:06 +02:00
|
|
|
#else
|
2014-02-07 16:34:20 +01:00
|
|
|
#include "projectM.hpp"
|
2010-07-10 22:21:06 +02:00
|
|
|
#endif
|
2010-06-06 16:06:23 +02:00
|
|
|
|
2010-06-07 12:23:11 +02:00
|
|
|
#ifdef Q_OS_MAC
|
2014-02-07 16:34:20 +01:00
|
|
|
#include <OpenGL/gl.h>
|
2020-09-18 16:15:19 +02:00
|
|
|
|
|
|
|
#include "core/mac_startup.h"
|
2010-06-07 19:57:07 +02:00
|
|
|
#else
|
2014-02-07 16:34:20 +01:00
|
|
|
#include <GL/gl.h>
|
2010-06-07 12:23:11 +02:00
|
|
|
#endif
|
|
|
|
|
2021-04-07 22:05:37 +02:00
|
|
|
ProjectMVisualisation::ProjectMVisualisation(VisualisationContainer* container)
|
|
|
|
: QGraphicsScene(container),
|
2014-02-07 16:34:20 +01:00
|
|
|
preset_model_(nullptr),
|
|
|
|
mode_(Random),
|
|
|
|
duration_(15),
|
2021-04-07 22:05:37 +02:00
|
|
|
texture_size_(512),
|
|
|
|
pixel_ratio_(container->devicePixelRatio()),
|
|
|
|
container_(container) {
|
2014-02-07 16:34:20 +01:00
|
|
|
connect(this, SIGNAL(sceneRectChanged(QRectF)),
|
|
|
|
SLOT(SceneRectChanged(QRectF)));
|
2010-06-07 15:29:57 +02:00
|
|
|
|
2014-02-06 14:48:00 +01:00
|
|
|
for (int i = 0; i < TOTAL_RATING_TYPES; ++i)
|
2010-06-07 15:29:57 +02:00
|
|
|
default_rating_list_.push_back(3);
|
2010-06-06 16:06:23 +02:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
ProjectMVisualisation::~ProjectMVisualisation() {}
|
2010-06-06 16:06:23 +02:00
|
|
|
|
2010-06-07 12:23:11 +02:00
|
|
|
void ProjectMVisualisation::InitProjectM() {
|
|
|
|
// Find the projectM presets
|
|
|
|
QStringList paths = QStringList()
|
2011-01-16 16:11:23 +01:00
|
|
|
#ifdef USE_INSTALL_PREFIX
|
2014-02-07 16:34:20 +01:00
|
|
|
<< CMAKE_INSTALL_PREFIX
|
|
|
|
"/share/clementine/projectm-presets"
|
|
|
|
<< CMAKE_INSTALL_PREFIX "/share/projectM/presets"
|
2010-06-22 17:46:51 +02:00
|
|
|
#endif
|
2014-02-07 16:34:20 +01:00
|
|
|
<< "/usr/share/clementine/projectm-presets"
|
|
|
|
<< "/usr/local/share/clementine/projectm-presets"
|
|
|
|
<< "/usr/share/projectM/presets"
|
|
|
|
<< "/usr/local/share/projectM/presets";
|
2010-06-07 12:23:11 +02:00
|
|
|
|
|
|
|
#if defined(Q_OS_WIN32)
|
|
|
|
paths.prepend(QCoreApplication::applicationDirPath() + "/projectm-presets");
|
|
|
|
#elif defined(Q_OS_MAC)
|
|
|
|
paths.prepend(mac::GetResourcesPath() + "/projectm-presets");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
QString preset_path;
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const QString& path : paths) {
|
2014-02-07 16:34:20 +01:00
|
|
|
if (!QFile::exists(path)) continue;
|
2010-06-22 21:44:38 +02:00
|
|
|
|
|
|
|
// Don't use empty directories
|
|
|
|
if (QDir(path).entryList(QDir::Files | QDir::NoDotAndDotDot).isEmpty())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
preset_path = path;
|
|
|
|
break;
|
2010-06-07 12:23:11 +02:00
|
|
|
}
|
|
|
|
|
2010-07-18 15:26:01 +02:00
|
|
|
// Write an empty font out to a temporary directory. libprojectM dies if it's
|
|
|
|
// compiled with FTGL support and you pass it an empty font URL, so we have
|
|
|
|
// to give it a dummy font even though we won't use it.
|
|
|
|
temporary_font_.reset(QTemporaryFile::createLocalFile(":blank.ttf"));
|
|
|
|
const QString font_path = temporary_font_->fileName();
|
|
|
|
|
2010-06-07 12:23:11 +02:00
|
|
|
// Create projectM settings
|
|
|
|
projectM::Settings s;
|
|
|
|
s.meshX = 32;
|
|
|
|
s.meshY = 24;
|
|
|
|
s.textureSize = texture_size_;
|
|
|
|
s.fps = 35;
|
|
|
|
s.windowWidth = 512;
|
|
|
|
s.windowHeight = 512;
|
2010-06-07 15:36:50 +02:00
|
|
|
s.smoothPresetDuration = 5;
|
|
|
|
s.presetDuration = duration_;
|
2010-06-07 12:23:11 +02:00
|
|
|
s.presetURL = preset_path.toStdString();
|
|
|
|
s.shuffleEnabled = true;
|
2014-02-07 16:34:20 +01:00
|
|
|
s.easterEgg = 0; // ??
|
2010-06-07 12:23:11 +02:00
|
|
|
s.softCutRatingsEnabled = false;
|
2010-07-18 15:26:01 +02:00
|
|
|
s.menuFontURL = font_path.toStdString();
|
|
|
|
s.titleFontURL = font_path.toStdString();
|
2010-06-07 12:23:11 +02:00
|
|
|
|
|
|
|
projectm_.reset(new projectM(s));
|
2010-06-07 15:29:57 +02:00
|
|
|
preset_model_ = new ProjectMPresetModel(this, this);
|
2010-06-07 12:23:11 +02:00
|
|
|
Load();
|
2010-07-10 19:21:23 +02:00
|
|
|
|
2013-12-02 11:24:15 +01:00
|
|
|
// Start at a random preset.
|
|
|
|
if (projectm_->getPlaylistSize() > 0) {
|
2021-04-09 07:58:26 +02:00
|
|
|
#if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0))
|
|
|
|
int selection = qrand() % projectm_->getPlaylistSize();
|
|
|
|
#else
|
|
|
|
int selection =
|
|
|
|
QRandomGenerator::global()->bounded(projectm_->getPlaylistSize());
|
|
|
|
#endif
|
|
|
|
projectm_->selectPreset(selection, true);
|
2013-12-02 11:24:15 +01:00
|
|
|
}
|
|
|
|
|
2010-07-18 15:26:01 +02:00
|
|
|
if (font_path.isNull()) {
|
2010-07-10 19:21:23 +02:00
|
|
|
qWarning("ProjectM presets could not be found, search path was:\n %s",
|
|
|
|
paths.join("\n ").toLocal8Bit().constData());
|
2014-02-07 16:34:20 +01:00
|
|
|
QMessageBox::warning(
|
|
|
|
nullptr, tr("Missing projectM presets"),
|
|
|
|
tr("Clementine could not load any projectM visualisations. Check that "
|
|
|
|
"you have installed Clementine properly."));
|
2010-07-10 19:21:23 +02:00
|
|
|
}
|
2010-06-07 12:23:11 +02:00
|
|
|
}
|
|
|
|
|
2010-06-06 20:18:06 +02:00
|
|
|
void ProjectMVisualisation::drawBackground(QPainter* p, const QRectF&) {
|
|
|
|
p->beginNativePainting();
|
2010-06-06 16:06:23 +02:00
|
|
|
|
2010-06-06 20:18:06 +02:00
|
|
|
if (!projectm_) {
|
2010-06-07 12:23:11 +02:00
|
|
|
InitProjectM();
|
2010-06-06 20:18:06 +02:00
|
|
|
}
|
2010-06-06 16:06:23 +02:00
|
|
|
|
2021-04-07 22:05:37 +02:00
|
|
|
projectm_->projectM_resetGL(sceneRect().width() * pixel_ratio_,
|
|
|
|
sceneRect().height() * pixel_ratio_);
|
2010-06-06 16:06:23 +02:00
|
|
|
projectm_->renderFrame();
|
2010-06-06 20:18:06 +02:00
|
|
|
|
|
|
|
p->endNativePainting();
|
2010-06-06 16:06:23 +02:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void ProjectMVisualisation::SceneRectChanged(const QRectF& rect) {
|
2021-04-07 22:05:37 +02:00
|
|
|
// NOTE: This should be updated on a QScreen dpi change signal. Accessing the
|
|
|
|
// QScreen becomes a lot easier in Qt 5.14 with QWidget::screen().
|
|
|
|
pixel_ratio_ = container_->devicePixelRatio();
|
|
|
|
|
|
|
|
if (projectm_)
|
|
|
|
projectm_->projectM_resetGL(rect.width() * pixel_ratio_,
|
|
|
|
rect.height() * pixel_ratio_);
|
2010-06-06 16:06:23 +02:00
|
|
|
}
|
|
|
|
|
2010-06-07 01:05:11 +02:00
|
|
|
void ProjectMVisualisation::SetTextureSize(int size) {
|
|
|
|
texture_size_ = size;
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
if (projectm_) projectm_->changeTextureSize(texture_size_);
|
2010-06-07 01:05:11 +02:00
|
|
|
}
|
|
|
|
|
2010-06-07 15:36:50 +02:00
|
|
|
void ProjectMVisualisation::SetDuration(int seconds) {
|
|
|
|
duration_ = seconds;
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
if (projectm_) projectm_->changePresetDuration(duration_);
|
2013-12-02 11:15:08 +01:00
|
|
|
|
|
|
|
Save();
|
2010-06-07 15:36:50 +02:00
|
|
|
}
|
|
|
|
|
2011-03-20 22:40:53 +01:00
|
|
|
void ProjectMVisualisation::ConsumeBuffer(GstBuffer* buffer, int) {
|
2013-09-25 15:42:13 +02:00
|
|
|
GstMapInfo map;
|
|
|
|
gst_buffer_map(buffer, &map, GST_MAP_READ);
|
|
|
|
const int samples_per_channel = map.size / sizeof(short) / 2;
|
|
|
|
const short* data = reinterpret_cast<short*>(map.data);
|
2010-06-06 16:06:23 +02:00
|
|
|
|
2014-06-09 08:20:24 +02:00
|
|
|
if (projectm_) {
|
2010-06-07 00:22:02 +02:00
|
|
|
projectm_->pcm()->addPCM16Data(data, samples_per_channel);
|
2014-06-09 08:20:24 +02:00
|
|
|
}
|
2013-09-25 15:42:13 +02:00
|
|
|
|
|
|
|
gst_buffer_unmap(buffer, &map);
|
2015-04-23 12:25:00 +02:00
|
|
|
gst_buffer_unref(buffer);
|
2010-06-06 16:06:23 +02:00
|
|
|
}
|
2010-06-07 03:55:21 +02:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void ProjectMVisualisation::SetSelected(const QStringList& paths,
|
|
|
|
bool selected) {
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const QString& path : paths) {
|
2010-06-07 15:29:57 +02:00
|
|
|
int index = IndexOfPreset(path);
|
|
|
|
if (selected && index == -1) {
|
2014-02-07 16:34:20 +01:00
|
|
|
projectm_->addPresetURL(path.toStdString(), std::string(),
|
|
|
|
default_rating_list_);
|
2010-06-07 15:29:57 +02:00
|
|
|
} else if (!selected && index != -1) {
|
|
|
|
projectm_->removePreset(index);
|
|
|
|
}
|
|
|
|
}
|
2010-06-07 03:55:21 +02:00
|
|
|
|
|
|
|
Save();
|
|
|
|
}
|
|
|
|
|
2010-06-07 15:29:57 +02:00
|
|
|
void ProjectMVisualisation::ClearSelected() {
|
|
|
|
projectm_->clearPlaylist();
|
2010-06-07 03:55:21 +02:00
|
|
|
Save();
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
int ProjectMVisualisation::IndexOfPreset(const QString& path) const {
|
|
|
|
for (uint i = 0; i < projectm_->getPlaylistSize(); ++i) {
|
|
|
|
if (QString::fromStdString(projectm_->getPresetURL(i)) == path) return i;
|
2010-06-07 15:29:57 +02:00
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-06-07 03:55:21 +02:00
|
|
|
void ProjectMVisualisation::Load() {
|
|
|
|
QSettings s;
|
|
|
|
s.beginGroup(VisualisationContainer::kSettingsGroup);
|
2010-06-07 15:29:57 +02:00
|
|
|
mode_ = Mode(s.value("mode", 0).toInt());
|
2013-12-02 11:15:08 +01:00
|
|
|
duration_ = s.value("duration", duration_).toInt();
|
2010-06-07 15:29:57 +02:00
|
|
|
|
2013-12-02 11:15:08 +01:00
|
|
|
projectm_->changePresetDuration(duration_);
|
2010-06-07 15:29:57 +02:00
|
|
|
projectm_->clearPlaylist();
|
|
|
|
switch (mode_) {
|
|
|
|
case Random:
|
2014-02-07 16:34:20 +01:00
|
|
|
for (int i = 0; i < preset_model_->all_presets_.count(); ++i) {
|
|
|
|
projectm_->addPresetURL(
|
|
|
|
preset_model_->all_presets_[i].path_.toStdString(), std::string(),
|
|
|
|
default_rating_list_);
|
2010-06-07 15:29:57 +02:00
|
|
|
preset_model_->all_presets_[i].selected_ = true;
|
|
|
|
}
|
|
|
|
break;
|
2010-06-07 03:55:21 +02:00
|
|
|
|
2010-06-07 15:29:57 +02:00
|
|
|
case FromList: {
|
|
|
|
QStringList paths(s.value("preset_paths").toStringList());
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const QString& path : paths) {
|
2014-02-07 16:34:20 +01:00
|
|
|
projectm_->addPresetURL(path.toStdString(), std::string(),
|
|
|
|
default_rating_list_);
|
2010-06-07 15:29:57 +02:00
|
|
|
preset_model_->MarkSelected(path, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-06-07 03:55:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ProjectMVisualisation::Save() {
|
2010-06-07 15:29:57 +02:00
|
|
|
QStringList paths;
|
2010-06-07 03:55:21 +02:00
|
|
|
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const ProjectMPresetModel::Preset& preset :
|
|
|
|
preset_model_->all_presets_) {
|
2014-02-07 16:34:20 +01:00
|
|
|
if (preset.selected_) paths << preset.path_;
|
2010-06-07 03:55:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QSettings s;
|
|
|
|
s.beginGroup(VisualisationContainer::kSettingsGroup);
|
2010-06-07 15:29:57 +02:00
|
|
|
s.setValue("preset_paths", paths);
|
2010-06-07 03:55:21 +02:00
|
|
|
s.setValue("mode", mode_);
|
2013-12-02 11:15:08 +01:00
|
|
|
s.setValue("duration", duration_);
|
2010-06-07 03:55:21 +02:00
|
|
|
}
|
|
|
|
|
2010-06-07 15:29:57 +02:00
|
|
|
void ProjectMVisualisation::SetMode(Mode mode) {
|
2010-06-07 03:55:21 +02:00
|
|
|
mode_ = mode;
|
|
|
|
Save();
|
|
|
|
}
|
|
|
|
|
2010-06-07 15:29:57 +02:00
|
|
|
QString ProjectMVisualisation::preset_url() const {
|
|
|
|
return QString::fromStdString(projectm_->settings().presetURL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProjectMVisualisation::SetImmediatePreset(const QString& path) {
|
|
|
|
int index = IndexOfPreset(path);
|
|
|
|
if (index == -1) {
|
2014-02-07 16:34:20 +01:00
|
|
|
index = projectm_->addPresetURL(path.toStdString(), std::string(),
|
|
|
|
default_rating_list_);
|
2010-06-07 15:29:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
projectm_->selectPreset(index, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProjectMVisualisation::Lock(bool lock) {
|
|
|
|
projectm_->setPresetLock(lock);
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
if (!lock) Load();
|
2010-06-07 15:29:57 +02:00
|
|
|
}
|