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:
redPanther
2017-08-04 12:01:45 +02:00
committed by GitHub
parent 6625a318ac
commit 6279dcb2a9
44 changed files with 824 additions and 568 deletions

View File

@@ -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);