mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
per effect smoothing (#456)
* add dynamic smoothing first step * extend prio muxer to hold smoothing preset id * add icons for systray * fix missing changes in prio muxer * implement specific smoothing params for effects * refactoring: std::min/max to qMin/Max * some code optimization * fix schema and translation * revoke change of python include order * fix eol in effect shemas * optimize random,candle and fadecandy json schemas
This commit is contained in:
@@ -2,14 +2,12 @@
|
||||
#include <Python.h>
|
||||
#undef B0
|
||||
|
||||
// Stl includes
|
||||
#include <fstream>
|
||||
|
||||
// Qt includes
|
||||
#include <QResource>
|
||||
#include <QMetaType>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QMap>
|
||||
|
||||
// hyperion util includes
|
||||
#include <utils/jsonschema/QJsonSchemaChecker.h>
|
||||
@@ -41,7 +39,7 @@ EffectEngine::EffectEngine(Hyperion * hyperion, const QJsonObject & jsonEffectCo
|
||||
readEffects();
|
||||
|
||||
// initialize the python interpreter
|
||||
Debug(_log,"Initializing Python interpreter");
|
||||
Debug(_log, "Initializing Python interpreter");
|
||||
Effect::registerHyperionExtensionModule();
|
||||
Py_InitializeEx(0);
|
||||
PyEval_InitThreads(); // Create the GIL
|
||||
@@ -63,11 +61,11 @@ const std::list<ActiveEffectDefinition> &EffectEngine::getActiveEffects()
|
||||
for (Effect * effect : _activeEffects)
|
||||
{
|
||||
ActiveEffectDefinition activeEffectDefinition;
|
||||
activeEffectDefinition.script = effect->getScript();
|
||||
activeEffectDefinition.name = effect->getName();
|
||||
activeEffectDefinition.script = effect->getScript();
|
||||
activeEffectDefinition.name = effect->getName();
|
||||
activeEffectDefinition.priority = effect->getPriority();
|
||||
activeEffectDefinition.timeout = effect->getTimeout();
|
||||
activeEffectDefinition.args = effect->getArgs();
|
||||
activeEffectDefinition.timeout = effect->getTimeout();
|
||||
activeEffectDefinition.args = effect->getArgs();
|
||||
_availableActiveEffects.push_back(activeEffectDefinition);
|
||||
}
|
||||
|
||||
@@ -86,7 +84,7 @@ bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effe
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
Error( log, "Effect file '%s' could not be loaded", fileName.toUtf8().constData());
|
||||
Error( log, "Effect file '%s' could not be loaded", QSTRING_CSTR(fileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -108,7 +106,7 @@ bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effe
|
||||
}
|
||||
}
|
||||
|
||||
Error( log, "Error while reading effect: '%s' at Line: '%i' , Column: %i", error.errorString().toUtf8().constData(), errorLine, errorColumn);
|
||||
Error( log, "Error while reading effect: '%s' at Line: '%i' , Column: %i",QSTRING_CSTR( error.errorString()), errorLine, errorColumn);
|
||||
}
|
||||
|
||||
file.close();
|
||||
@@ -120,7 +118,7 @@ bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effe
|
||||
|
||||
if (!schema.open(QIODevice::ReadOnly))
|
||||
{
|
||||
Error( log, "Schema not found: %s", schema.errorString().toUtf8().constData());
|
||||
Error( log, "Schema not found: %s", QSTRING_CSTR(schema.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -142,7 +140,7 @@ bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effe
|
||||
}
|
||||
}
|
||||
|
||||
Error( log, "ERROR: Json schema wrong: '%s' at Line: '%i' , Column: %i", error.errorString().toUtf8().constData(), errorLine, errorColumn);
|
||||
Error( log, "ERROR: Json schema wrong: '%s' at Line: '%i' , Column: %i", QSTRING_CSTR(error.errorString()), errorLine, errorColumn);
|
||||
}
|
||||
|
||||
schema.close();
|
||||
@@ -154,7 +152,7 @@ bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effe
|
||||
if (!schemaChecker.validate(configEffect.object()).first)
|
||||
{
|
||||
const QStringList & errors = schemaChecker.getMessages();
|
||||
foreach (auto & error, errors)
|
||||
for (auto & error : errors)
|
||||
{
|
||||
Error( log, "Error while checking '%s':%s", QSTRING_CSTR(fileName), QSTRING_CSTR(error));
|
||||
}
|
||||
@@ -180,12 +178,27 @@ bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effe
|
||||
} else
|
||||
{
|
||||
(!fileInfo.exists())
|
||||
? effectDefinition.script = path + QDir::separator().toLatin1() + scriptName
|
||||
? effectDefinition.script = path + QDir::separator() + scriptName
|
||||
: effectDefinition.script = scriptName;
|
||||
}
|
||||
|
||||
effectDefinition.args = config["args"].toObject();
|
||||
|
||||
effectDefinition.smoothCfg = SMOOTHING_MODE_PAUSE;
|
||||
if (effectDefinition.args["smoothing-custom-settings"].toBool())
|
||||
{
|
||||
bool pause = effectDefinition.args["smoothing-pause"].toBool();
|
||||
if (pause)
|
||||
{
|
||||
effectDefinition.smoothCfg = _hyperion->addSmoothingConfig(pause);
|
||||
}
|
||||
else
|
||||
{
|
||||
effectDefinition.smoothCfg = _hyperion->addSmoothingConfig(
|
||||
effectDefinition.args["smoothing-time_ms"].toInt(),
|
||||
effectDefinition.args["smoothing-updateFrequency"].toDouble(),
|
||||
effectDefinition.args["smoothing-updateDelay"].toInt() );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -201,7 +214,7 @@ bool EffectEngine::loadEffectSchema(const QString &path, const QString &effectSc
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
Error( log, "Effect schema '%s' could not be loaded", fileName.toUtf8().constData());
|
||||
Error( log, "Effect schema '%s' could not be loaded", QSTRING_CSTR(fileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -223,7 +236,7 @@ bool EffectEngine::loadEffectSchema(const QString &path, const QString &effectSc
|
||||
}
|
||||
}
|
||||
|
||||
Error( log, "Error while reading effect schema: '%s' at Line: '%i' , Column: %i", error.errorString().toUtf8().constData(), errorLine, errorColumn);
|
||||
Error( log, "Error while reading effect schema: '%s' at Line: '%i' , Column: %i", QSTRING_CSTR(error.errorString()), errorLine, errorColumn);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -240,17 +253,13 @@ bool EffectEngine::loadEffectSchema(const QString &path, const QString &effectSc
|
||||
if (scriptName.isEmpty() || !pyFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
fileName = path + "schema/" + QDir::separator() + effectSchemaFile;
|
||||
Error( log, "Python script '%s' in effect schema '%s' could not be loaded", scriptName.toUtf8().constData(), fileName.toUtf8().constData());
|
||||
Error( log, "Python script '%s' in effect schema '%s' could not be loaded", QSTRING_CSTR(scriptName), QSTRING_CSTR(fileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
pyFile.close();
|
||||
|
||||
if (scriptName.mid(0, 1) == ":" )
|
||||
effectSchema.pyFile = ":/effects/"+scriptName.mid(1);
|
||||
else
|
||||
effectSchema.pyFile = path + QDir::separator().toLatin1() + scriptName;
|
||||
|
||||
effectSchema.pyFile = (scriptName.mid(0, 1) == ":" ) ? ":/effects/"+scriptName.mid(1) : path + QDir::separator() + scriptName;
|
||||
effectSchema.pySchema = tempSchemaEffect;
|
||||
|
||||
return true;
|
||||
@@ -279,38 +288,36 @@ void EffectEngine::readEffects()
|
||||
disableList << efx.toString();
|
||||
}
|
||||
|
||||
std::map<QString, EffectDefinition> availableEffects;
|
||||
foreach (const QString & path, efxPathList )
|
||||
QMap<QString, EffectDefinition> availableEffects;
|
||||
for (const QString & path : efxPathList )
|
||||
{
|
||||
QDir directory(path);
|
||||
if (!directory.exists())
|
||||
{
|
||||
if(directory.mkpath(path))
|
||||
{
|
||||
Warning(_log, "New Effect path \"%s\" created successfull",path.toUtf8().constData() );
|
||||
Warning(_log, "New Effect path \"%s\" created successfull", QSTRING_CSTR(path) );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning(_log, "Failed to create Effect path \"%s\", please check permissions",path.toUtf8().constData() );
|
||||
Warning(_log, "Failed to create Effect path \"%s\", please check permissions", QSTRING_CSTR(path) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int efxCount = 0;
|
||||
QStringList filenames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
foreach (const QString & filename, filenames)
|
||||
for (const QString & filename : filenames)
|
||||
{
|
||||
EffectDefinition def;
|
||||
if (loadEffectDefinition(path, filename, def))
|
||||
{
|
||||
if (availableEffects.find(def.name) != availableEffects.end())
|
||||
{
|
||||
Info(_log, "effect overload effect '%s' is now taken from %s'", def.name.toUtf8().constData(), path.toUtf8().constData() );
|
||||
}
|
||||
InfoIf(availableEffects.find(def.name) != availableEffects.end(), _log,
|
||||
"effect overload effect '%s' is now taken from %s'", QSTRING_CSTR(def.name), QSTRING_CSTR(path) );
|
||||
|
||||
if ( disableList.contains(def.name) )
|
||||
{
|
||||
Info(_log, "effect '%s' not loaded, because it is disabled in hyperion config", def.name.toUtf8().constData());
|
||||
Info(_log, "effect '%s' not loaded, because it is disabled in hyperion config", QSTRING_CSTR(def.name));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -319,13 +326,13 @@ void EffectEngine::readEffects()
|
||||
}
|
||||
}
|
||||
}
|
||||
Info(_log, "%d effects loaded from directory %s", efxCount, path.toUtf8().constData());
|
||||
Info(_log, "%d effects loaded from directory %s", efxCount, QSTRING_CSTR(path));
|
||||
|
||||
// collect effect schemas
|
||||
efxCount = 0;
|
||||
directory = path + "schema/";
|
||||
QStringList pynames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
foreach (const QString & pyname, pynames)
|
||||
for (const QString & pyname : pynames)
|
||||
{
|
||||
EffectSchema pyEffect;
|
||||
if (loadEffectSchema(path, pyname, pyEffect))
|
||||
@@ -334,20 +341,16 @@ void EffectEngine::readEffects()
|
||||
efxCount++;
|
||||
}
|
||||
}
|
||||
if (efxCount > 0)
|
||||
Info(_log, "%d effect schemas loaded from directory %s", efxCount, (path + "schema/").toUtf8().constData());
|
||||
InfoIf(efxCount > 0, _log, "%d effect schemas loaded from directory %s", efxCount, QSTRING_CSTR((path + "schema/")));
|
||||
}
|
||||
}
|
||||
|
||||
foreach(auto item, availableEffects)
|
||||
for(auto item : availableEffects)
|
||||
{
|
||||
_availableEffects.push_back(item.second);
|
||||
_availableEffects.push_back(item);
|
||||
}
|
||||
|
||||
if (_availableEffects.size() == 0)
|
||||
{
|
||||
Error(_log, "no effects found, check your effect directories");
|
||||
}
|
||||
ErrorIf(_availableEffects.size()==0, _log, "no effects found, check your effect directories");
|
||||
}
|
||||
|
||||
int EffectEngine::runEffect(const QString &effectName, int priority, int timeout, const QString &origin)
|
||||
@@ -355,9 +358,9 @@ int EffectEngine::runEffect(const QString &effectName, int priority, int timeout
|
||||
return runEffect(effectName, QJsonObject(), priority, timeout, "", origin);
|
||||
}
|
||||
|
||||
int EffectEngine::runEffect(const QString &effectName, const QJsonObject &args, int priority, int timeout, const QString &pythonScript, const QString &origin)
|
||||
int EffectEngine::runEffect(const QString &effectName, const QJsonObject &args, int priority, int timeout, const QString &pythonScript, const QString &origin, unsigned smoothCfg)
|
||||
{
|
||||
Info( _log, "run effect %s on channel %d", effectName.toUtf8().constData(), priority);
|
||||
Info( _log, "run effect %s on channel %d", QSTRING_CSTR(effectName), priority);
|
||||
|
||||
if (pythonScript.isEmpty())
|
||||
{
|
||||
@@ -373,23 +376,23 @@ int EffectEngine::runEffect(const QString &effectName, const QJsonObject &args,
|
||||
if (effectDefinition == nullptr)
|
||||
{
|
||||
// no such effect
|
||||
Error(_log, "effect %s not found", effectName.toUtf8().constData());
|
||||
Error(_log, "effect %s not found", QSTRING_CSTR(effectName));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return runEffectScript(effectDefinition->script, effectName, (args.isEmpty() ? effectDefinition->args : args), priority, timeout, origin);
|
||||
return runEffectScript(effectDefinition->script, effectName, (args.isEmpty() ? effectDefinition->args : args), priority, timeout, origin, effectDefinition->smoothCfg);
|
||||
}
|
||||
return runEffectScript(pythonScript, effectName, args, priority, timeout, origin);
|
||||
return runEffectScript(pythonScript, effectName, args, priority, timeout, origin, smoothCfg);
|
||||
}
|
||||
|
||||
int EffectEngine::runEffectScript(const QString &script, const QString &name, const QJsonObject &args, int priority, int timeout, const QString & origin)
|
||||
int EffectEngine::runEffectScript(const QString &script, const QString &name, const QJsonObject &args, int priority, int timeout, const QString & origin, unsigned smoothCfg)
|
||||
{
|
||||
// clear current effect on the channel
|
||||
channelCleared(priority);
|
||||
|
||||
// create the effect
|
||||
Effect * effect = new Effect(_mainThreadState, priority, timeout, script, name, args, origin);
|
||||
connect(effect, SIGNAL(setColors(int,std::vector<ColorRgb>,int,bool,hyperion::Components,const QString)), _hyperion, SLOT(setColors(int,std::vector<ColorRgb>,int,bool,hyperion::Components,const QString)), Qt::QueuedConnection);
|
||||
Effect * effect = new Effect(_mainThreadState, priority, timeout, script, name, args, origin, smoothCfg);
|
||||
connect(effect, SIGNAL(setColors(int,std::vector<ColorRgb>,int,bool,hyperion::Components,const QString,unsigned)), _hyperion, SLOT(setColors(int,std::vector<ColorRgb>,int,bool,hyperion::Components,const QString,unsigned)), Qt::QueuedConnection);
|
||||
connect(effect, SIGNAL(effectFinished(Effect*)), this, SLOT(effectFinished(Effect*)));
|
||||
_activeEffects.push_back(effect);
|
||||
|
||||
|
Reference in New Issue
Block a user