mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
adfe2a4b23
* implement effects included in hyperiond binary * cleanup * remove install of effects dir. People who wants to develop effects has to copy them from github effect params for initial effects can be changed in config permanently and other effect params can be changed via json (currently only temporarily) * fix schema of fadecandy webui fix display of specific led options * add leddevice write support * cleanup * webui: tune hue code * when use json effect definition from putsiede hyperiond but want to use py script from inside hyperiond use ad a : e.g. fade.py needs a fade.py near the json file, but :fade.py is taken from resource inside hyperiond * add ability to di * add abiloty to diable effcts via hyperion config * use effect name instead of script in active effects and prio register * finally solve open file handle during effect is playing. Now script is read before, then file closed and then t is run by python * fix some webui things - led config tabs - inital loading screen optimize qrc file generation fix compile warning in hyperion.cpp * cleanup * more cleanup
952 lines
29 KiB
C++
952 lines
29 KiB
C++
|
|
// STL includes
|
|
#include <cassert>
|
|
#include <exception>
|
|
#include <sstream>
|
|
|
|
// QT includes
|
|
#include <QDateTime>
|
|
#include <QThread>
|
|
#include <QRegExp>
|
|
#include <QString>
|
|
#include <QStringList>
|
|
|
|
// JsonSchema include
|
|
#include <utils/jsonschema/JsonFactory.h>
|
|
|
|
// hyperion include
|
|
#include <hyperion/Hyperion.h>
|
|
#include <hyperion/ImageProcessorFactory.h>
|
|
#include <hyperion/ColorTransform.h>
|
|
#include <hyperion/ColorCorrection.h>
|
|
#include <hyperion/ColorAdjustment.h>
|
|
|
|
// Leddevice includes
|
|
#include <leddevice/LedDevice.h>
|
|
#include <leddevice/LedDeviceFactory.h>
|
|
|
|
#include "MultiColorTransform.h"
|
|
#include "MultiColorCorrection.h"
|
|
#include "MultiColorAdjustment.h"
|
|
#include "LinearColorSmoothing.h"
|
|
|
|
// effect engine includes
|
|
#include <effectengine/EffectEngine.h>
|
|
|
|
Hyperion* Hyperion::_hyperion = nullptr;
|
|
|
|
Hyperion* Hyperion::initInstance(const Json::Value& jsonConfig, const std::string configFile)
|
|
{
|
|
if ( Hyperion::_hyperion != nullptr )
|
|
throw std::runtime_error("Hyperion::initInstance can be called only one time");
|
|
Hyperion::_hyperion = new Hyperion(jsonConfig,configFile);
|
|
|
|
return Hyperion::_hyperion;
|
|
}
|
|
|
|
Hyperion* Hyperion::getInstance()
|
|
{
|
|
if ( Hyperion::_hyperion == nullptr )
|
|
throw std::runtime_error("Hyperion::getInstance used without call of Hyperion::initInstance before");
|
|
|
|
return Hyperion::_hyperion;
|
|
}
|
|
|
|
ColorOrder Hyperion::createColorOrder(const Json::Value &deviceConfig)
|
|
{
|
|
return stringToColorOrder( deviceConfig.get("colorOrder", "rgb").asString() );
|
|
}
|
|
|
|
ColorTransform * Hyperion::createColorTransform(const Json::Value & transformConfig)
|
|
{
|
|
const std::string id = transformConfig.get("id", "default").asString();
|
|
|
|
RgbChannelTransform * redTransform = createRgbChannelTransform(transformConfig["red"]);
|
|
RgbChannelTransform * greenTransform = createRgbChannelTransform(transformConfig["green"]);
|
|
RgbChannelTransform * blueTransform = createRgbChannelTransform(transformConfig["blue"]);
|
|
|
|
HsvTransform * hsvTransform = createHsvTransform(transformConfig["hsv"]);
|
|
HslTransform * hslTransform = createHslTransform(transformConfig["hsl"]);
|
|
|
|
ColorTransform * transform = new ColorTransform();
|
|
transform->_id = id;
|
|
transform->_rgbRedTransform = *redTransform;
|
|
transform->_rgbGreenTransform = *greenTransform;
|
|
transform->_rgbBlueTransform = *blueTransform;
|
|
transform->_hsvTransform = *hsvTransform;
|
|
transform->_hslTransform = *hslTransform;
|
|
|
|
|
|
// Cleanup the allocated individual transforms
|
|
delete redTransform;
|
|
delete greenTransform;
|
|
delete blueTransform;
|
|
delete hsvTransform;
|
|
delete hslTransform;
|
|
|
|
return transform;
|
|
}
|
|
|
|
|
|
ColorCorrection * Hyperion::createColorCorrection(const Json::Value & correctionConfig)
|
|
{
|
|
const std::string id = correctionConfig.get("id", "default").asString();
|
|
|
|
RgbChannelAdjustment * rgbCorrection = createRgbChannelCorrection(correctionConfig["correctionValues"]);
|
|
|
|
ColorCorrection * correction = new ColorCorrection();
|
|
correction->_id = id;
|
|
correction->_rgbCorrection = *rgbCorrection;
|
|
|
|
// Cleanup the allocated individual transforms
|
|
delete rgbCorrection;
|
|
|
|
return correction;
|
|
}
|
|
|
|
|
|
ColorAdjustment * Hyperion::createColorAdjustment(const Json::Value & adjustmentConfig)
|
|
{
|
|
const std::string id = adjustmentConfig.get("id", "default").asString();
|
|
|
|
RgbChannelAdjustment * redAdjustment = createRgbChannelAdjustment(adjustmentConfig["pureRed"],RED);
|
|
RgbChannelAdjustment * greenAdjustment = createRgbChannelAdjustment(adjustmentConfig["pureGreen"],GREEN);
|
|
RgbChannelAdjustment * blueAdjustment = createRgbChannelAdjustment(adjustmentConfig["pureBlue"],BLUE);
|
|
|
|
ColorAdjustment * adjustment = new ColorAdjustment();
|
|
adjustment->_id = id;
|
|
adjustment->_rgbRedAdjustment = *redAdjustment;
|
|
adjustment->_rgbGreenAdjustment = *greenAdjustment;
|
|
adjustment->_rgbBlueAdjustment = *blueAdjustment;
|
|
|
|
// Cleanup the allocated individual adjustments
|
|
delete redAdjustment;
|
|
delete greenAdjustment;
|
|
delete blueAdjustment;
|
|
|
|
return adjustment;
|
|
}
|
|
|
|
|
|
MultiColorTransform * Hyperion::createLedColorsTransform(const unsigned ledCnt, const Json::Value & colorConfig)
|
|
{
|
|
// Create the result, the transforms are added to this
|
|
MultiColorTransform * transform = new MultiColorTransform(ledCnt);
|
|
Logger * log = Logger::getInstance("Core");
|
|
|
|
const Json::Value transformConfig = colorConfig.get("transform", Json::nullValue);
|
|
if (transformConfig.isNull())
|
|
{
|
|
// Old style color transformation config (just one for all leds)
|
|
ColorTransform * colorTransform = createColorTransform(colorConfig);
|
|
transform->addTransform(colorTransform);
|
|
transform->setTransformForLed(colorTransform->_id, 0, ledCnt-1);
|
|
}
|
|
else if (!transformConfig.isArray())
|
|
{
|
|
ColorTransform * colorTransform = createColorTransform(transformConfig);
|
|
transform->addTransform(colorTransform);
|
|
transform->setTransformForLed(colorTransform->_id, 0, ledCnt-1);
|
|
}
|
|
else
|
|
{
|
|
const QRegExp overallExp("([0-9]+(\\-[0-9]+)?)(,[ ]*([0-9]+(\\-[0-9]+)?))*");
|
|
|
|
for (Json::UInt i = 0; i < transformConfig.size(); ++i)
|
|
{
|
|
const Json::Value & config = transformConfig[i];
|
|
ColorTransform * colorTransform = createColorTransform(config);
|
|
transform->addTransform(colorTransform);
|
|
|
|
const QString ledIndicesStr = QString(config.get("leds", "").asCString()).trimmed();
|
|
if (ledIndicesStr.compare("*") == 0)
|
|
{
|
|
// Special case for indices '*' => all leds
|
|
transform->setTransformForLed(colorTransform->_id, 0, ledCnt-1);
|
|
Info(log, "ColorTransform '%s' => [0; %d]", colorTransform->_id.c_str(), ledCnt-1);
|
|
continue;
|
|
}
|
|
|
|
if (!overallExp.exactMatch(ledIndicesStr))
|
|
{
|
|
Error(log, "Given led indices %d not correct format: %s", i, ledIndicesStr.toStdString().c_str());
|
|
continue;
|
|
}
|
|
|
|
std::stringstream ss;
|
|
const QStringList ledIndexList = ledIndicesStr.split(",");
|
|
for (int i=0; i<ledIndexList.size(); ++i) {
|
|
if (i > 0)
|
|
{
|
|
ss << ", ";
|
|
}
|
|
if (ledIndexList[i].contains("-"))
|
|
{
|
|
QStringList ledIndices = ledIndexList[i].split("-");
|
|
int startInd = ledIndices[0].toInt();
|
|
int endInd = ledIndices[1].toInt();
|
|
|
|
transform->setTransformForLed(colorTransform->_id, startInd, endInd);
|
|
ss << startInd << "-" << endInd;
|
|
}
|
|
else
|
|
{
|
|
int index = ledIndexList[i].toInt();
|
|
transform->setTransformForLed(colorTransform->_id, index, index);
|
|
ss << index;
|
|
}
|
|
}
|
|
Info(log, "ColorTransform '%s' => [%s]", colorTransform->_id.c_str(), ss.str().c_str());
|
|
}
|
|
}
|
|
return transform;
|
|
}
|
|
|
|
MultiColorCorrection * Hyperion::createLedColorsTemperature(const unsigned ledCnt, const Json::Value & colorConfig)
|
|
{
|
|
// Create the result, the corrections are added to this
|
|
MultiColorCorrection * correction = new MultiColorCorrection(ledCnt);
|
|
Logger * log = Logger::getInstance("Core");
|
|
|
|
const std::string jsonKey = colorConfig.isMember("temperature") ? "temperature" : "correction";
|
|
const Json::Value correctionConfig = colorConfig.get(jsonKey, Json::nullValue);
|
|
if (correctionConfig.isNull())
|
|
{
|
|
// Old style color correction config (just one for all leds)
|
|
ColorCorrection * colorCorrection = createColorCorrection(colorConfig);
|
|
correction->addCorrection(colorCorrection);
|
|
correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1);
|
|
}
|
|
else if (!correctionConfig.isArray())
|
|
{
|
|
ColorCorrection * colorCorrection = createColorCorrection(correctionConfig);
|
|
correction->addCorrection(colorCorrection);
|
|
correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1);
|
|
}
|
|
else
|
|
{
|
|
const QRegExp overallExp("([0-9]+(\\-[0-9]+)?)(,[ ]*([0-9]+(\\-[0-9]+)?))*");
|
|
|
|
for (Json::UInt i = 0; i < correctionConfig.size(); ++i)
|
|
{
|
|
const Json::Value & config = correctionConfig[i];
|
|
ColorCorrection * colorCorrection = createColorCorrection(config);
|
|
correction->addCorrection(colorCorrection);
|
|
|
|
const QString ledIndicesStr = QString(config.get("leds", "").asCString()).trimmed();
|
|
if (ledIndicesStr.compare("*") == 0)
|
|
{
|
|
// Special case for indices '*' => all leds
|
|
correction->setCorrectionForLed(colorCorrection->_id, 0, ledCnt-1);
|
|
Info(log, "ColorTemperature '%s' => [0; %d]", colorCorrection->_id.c_str(), ledCnt-1);
|
|
continue;
|
|
}
|
|
|
|
if (!overallExp.exactMatch(ledIndicesStr))
|
|
{
|
|
Error(log, "Given led indices %d not correct format: %s", i, ledIndicesStr.toStdString().c_str());
|
|
continue;
|
|
}
|
|
|
|
std::stringstream ss;
|
|
const QStringList ledIndexList = ledIndicesStr.split(",");
|
|
for (int i=0; i<ledIndexList.size(); ++i) {
|
|
if (i > 0)
|
|
{
|
|
ss << ", ";
|
|
}
|
|
if (ledIndexList[i].contains("-"))
|
|
{
|
|
QStringList ledIndices = ledIndexList[i].split("-");
|
|
int startInd = ledIndices[0].toInt();
|
|
int endInd = ledIndices[1].toInt();
|
|
|
|
correction->setCorrectionForLed(colorCorrection->_id, startInd, endInd);
|
|
ss << startInd << "-" << endInd;
|
|
}
|
|
else
|
|
{
|
|
int index = ledIndexList[i].toInt();
|
|
correction->setCorrectionForLed(colorCorrection->_id, index, index);
|
|
ss << index;
|
|
}
|
|
}
|
|
Info(log, "ColorTemperature '%s' => [%s]", colorCorrection->_id.c_str(), ss.str().c_str());
|
|
}
|
|
}
|
|
return correction;
|
|
}
|
|
|
|
|
|
MultiColorAdjustment * Hyperion::createLedColorsAdjustment(const unsigned ledCnt, const Json::Value & colorConfig)
|
|
{
|
|
// Create the result, the transforms are added to this
|
|
MultiColorAdjustment * adjustment = new MultiColorAdjustment(ledCnt);
|
|
Logger * log = Logger::getInstance("Core");
|
|
|
|
const Json::Value adjustmentConfig = colorConfig.get("channelAdjustment", Json::nullValue);
|
|
if (adjustmentConfig.isNull())
|
|
{
|
|
// Old style color transformation config (just one for all leds)
|
|
ColorAdjustment * colorAdjustment = createColorAdjustment(colorConfig);
|
|
adjustment->addAdjustment(colorAdjustment);
|
|
adjustment->setAdjustmentForLed(colorAdjustment->_id, 0, ledCnt-1);
|
|
}
|
|
else if (!adjustmentConfig.isArray())
|
|
{
|
|
ColorAdjustment * colorAdjustment = createColorAdjustment(adjustmentConfig);
|
|
adjustment->addAdjustment(colorAdjustment);
|
|
adjustment->setAdjustmentForLed(colorAdjustment->_id, 0, ledCnt-1);
|
|
}
|
|
else
|
|
{
|
|
const QRegExp overallExp("([0-9]+(\\-[0-9]+)?)(,[ ]*([0-9]+(\\-[0-9]+)?))*");
|
|
|
|
for (Json::UInt i = 0; i < adjustmentConfig.size(); ++i)
|
|
{
|
|
const Json::Value & config = adjustmentConfig[i];
|
|
ColorAdjustment * colorAdjustment = createColorAdjustment(config);
|
|
adjustment->addAdjustment(colorAdjustment);
|
|
|
|
const QString ledIndicesStr = QString(config.get("leds", "").asCString()).trimmed();
|
|
if (ledIndicesStr.compare("*") == 0)
|
|
{
|
|
// Special case for indices '*' => all leds
|
|
adjustment->setAdjustmentForLed(colorAdjustment->_id, 0, ledCnt-1);
|
|
Info(log, "ColorAdjustment '%s' => [0; %d]", colorAdjustment->_id.c_str(), ledCnt-1);
|
|
continue;
|
|
}
|
|
|
|
if (!overallExp.exactMatch(ledIndicesStr))
|
|
{
|
|
Error(log, "Given led indices %d not correct format: %s", i, ledIndicesStr.toStdString().c_str());
|
|
continue;
|
|
}
|
|
|
|
std::stringstream ss;
|
|
const QStringList ledIndexList = ledIndicesStr.split(",");
|
|
for (int i=0; i<ledIndexList.size(); ++i) {
|
|
if (i > 0)
|
|
{
|
|
ss << ", ";
|
|
}
|
|
if (ledIndexList[i].contains("-"))
|
|
{
|
|
QStringList ledIndices = ledIndexList[i].split("-");
|
|
int startInd = ledIndices[0].toInt();
|
|
int endInd = ledIndices[1].toInt();
|
|
|
|
adjustment->setAdjustmentForLed(colorAdjustment->_id, startInd, endInd);
|
|
ss << startInd << "-" << endInd;
|
|
}
|
|
else
|
|
{
|
|
int index = ledIndexList[i].toInt();
|
|
adjustment->setAdjustmentForLed(colorAdjustment->_id, index, index);
|
|
ss << index;
|
|
}
|
|
}
|
|
Info(log, "ColorAdjustment '%s' => [%s]", colorAdjustment->_id.c_str(), ss.str().c_str());
|
|
}
|
|
}
|
|
return adjustment;
|
|
}
|
|
|
|
HsvTransform * Hyperion::createHsvTransform(const Json::Value & hsvConfig)
|
|
{
|
|
const double saturationGain = hsvConfig.get("saturationGain", 1.0).asDouble();
|
|
const double valueGain = hsvConfig.get("valueGain", 1.0).asDouble();
|
|
|
|
return new HsvTransform(saturationGain, valueGain);
|
|
}
|
|
|
|
HslTransform * Hyperion::createHslTransform(const Json::Value & hslConfig)
|
|
{
|
|
const double saturationGain = hslConfig.get("saturationGain", 1.0).asDouble();
|
|
const double luminanceGain = hslConfig.get("luminanceGain", 1.0).asDouble();
|
|
const double luminanceMinimum = hslConfig.get("luminanceMinimum", 0.0).asDouble();
|
|
|
|
return new HslTransform(saturationGain, luminanceGain, luminanceMinimum);
|
|
}
|
|
|
|
RgbChannelTransform* Hyperion::createRgbChannelTransform(const Json::Value& colorConfig)
|
|
{
|
|
const double threshold = colorConfig.get("threshold", 0.0).asDouble();
|
|
const double gamma = colorConfig.get("gamma", 1.0).asDouble();
|
|
const double blacklevel = colorConfig.get("blacklevel", 0.0).asDouble();
|
|
const double whitelevel = colorConfig.get("whitelevel", 1.0).asDouble();
|
|
|
|
RgbChannelTransform* transform = new RgbChannelTransform(threshold, gamma, blacklevel, whitelevel);
|
|
return transform;
|
|
}
|
|
|
|
RgbChannelAdjustment* Hyperion::createRgbChannelCorrection(const Json::Value& colorConfig)
|
|
{
|
|
const int varR = colorConfig.get("red", 255).asInt();
|
|
const int varG = colorConfig.get("green", 255).asInt();
|
|
const int varB = colorConfig.get("blue", 255).asInt();
|
|
|
|
RgbChannelAdjustment* correction = new RgbChannelAdjustment(varR, varG, varB);
|
|
return correction;
|
|
}
|
|
|
|
RgbChannelAdjustment* Hyperion::createRgbChannelAdjustment(const Json::Value& colorConfig, const RgbChannel color)
|
|
{
|
|
int varR=0, varG=0, varB=0;
|
|
if (color == RED)
|
|
{
|
|
varR = colorConfig.get("redChannel", 255).asInt();
|
|
varG = colorConfig.get("greenChannel", 0).asInt();
|
|
varB = colorConfig.get("blueChannel", 0).asInt();
|
|
}
|
|
else if (color == GREEN)
|
|
{
|
|
varR = colorConfig.get("redChannel", 0).asInt();
|
|
varG = colorConfig.get("greenChannel", 255).asInt();
|
|
varB = colorConfig.get("blueChannel", 0).asInt();
|
|
}
|
|
else if (color == BLUE)
|
|
{
|
|
varR = colorConfig.get("redChannel", 0).asInt();
|
|
varG = colorConfig.get("greenChannel", 0).asInt();
|
|
varB = colorConfig.get("blueChannel", 255).asInt();
|
|
}
|
|
|
|
RgbChannelAdjustment* adjustment = new RgbChannelAdjustment(varR, varG, varB);
|
|
return adjustment;
|
|
}
|
|
|
|
LedString Hyperion::createLedString(const Json::Value& ledsConfig, const ColorOrder deviceOrder)
|
|
{
|
|
LedString ledString;
|
|
const std::string deviceOrderStr = colorOrderToString(deviceOrder);
|
|
int maxLedId = ledsConfig.size();
|
|
for (const Json::Value& ledConfig : ledsConfig)
|
|
{
|
|
Led led;
|
|
led.index = ledConfig["index"].asInt();
|
|
led.clone = ledConfig.get("clone",-1).asInt();
|
|
if ( led.clone < -1 || led.clone >= maxLedId )
|
|
{
|
|
Warning(Logger::getInstance("Core"), "LED %d: clone index of %d is out of range, clone ignored", led.index, led.clone);
|
|
led.clone = -1;
|
|
}
|
|
|
|
if ( led.clone < 0 )
|
|
{
|
|
const Json::Value& hscanConfig = ledConfig["hscan"];
|
|
const Json::Value& vscanConfig = ledConfig["vscan"];
|
|
led.minX_frac = std::max(0.0, std::min(1.0, hscanConfig["minimum"].asDouble()));
|
|
led.maxX_frac = std::max(0.0, std::min(1.0, hscanConfig["maximum"].asDouble()));
|
|
led.minY_frac = std::max(0.0, std::min(1.0, vscanConfig["minimum"].asDouble()));
|
|
led.maxY_frac = std::max(0.0, std::min(1.0, vscanConfig["maximum"].asDouble()));
|
|
// Fix if the user swapped min and max
|
|
if (led.minX_frac > led.maxX_frac)
|
|
{
|
|
std::swap(led.minX_frac, led.maxX_frac);
|
|
}
|
|
if (led.minY_frac > led.maxY_frac)
|
|
{
|
|
std::swap(led.minY_frac, led.maxY_frac);
|
|
}
|
|
|
|
// Get the order of the rgb channels for this led (default is device order)
|
|
led.colorOrder = stringToColorOrder(ledConfig.get("colorOrder", deviceOrderStr).asString());
|
|
ledString.leds().push_back(led);
|
|
}
|
|
}
|
|
|
|
// Make sure the leds are sorted (on their indices)
|
|
std::sort(ledString.leds().begin(), ledString.leds().end(), [](const Led& lhs, const Led& rhs){ return lhs.index < rhs.index; });
|
|
return ledString;
|
|
}
|
|
|
|
LedString Hyperion::createLedStringClone(const Json::Value& ledsConfig, const ColorOrder deviceOrder)
|
|
{
|
|
LedString ledString;
|
|
const std::string deviceOrderStr = colorOrderToString(deviceOrder);
|
|
int maxLedId = ledsConfig.size();
|
|
for (const Json::Value& ledConfig : ledsConfig)
|
|
{
|
|
Led led;
|
|
led.index = ledConfig["index"].asInt();
|
|
led.clone = ledConfig.get("clone",-1).asInt();
|
|
if ( led.clone < -1 || led.clone >= maxLedId )
|
|
{
|
|
Warning(Logger::getInstance("Core"), "LED %d: clone index of %d is out of range, clone ignored", led.index, led.clone);
|
|
led.clone = -1;
|
|
}
|
|
|
|
if ( led.clone >= 0 )
|
|
{
|
|
Debug(Logger::getInstance("Core"), "LED %d: clone from led %d", led.index, led.clone);
|
|
led.minX_frac = 0;
|
|
led.maxX_frac = 0;
|
|
led.minY_frac = 0;
|
|
led.maxY_frac = 0;
|
|
// Get the order of the rgb channels for this led (default is device order)
|
|
led.colorOrder = stringToColorOrder(ledConfig.get("colorOrder", deviceOrderStr).asString());
|
|
|
|
ledString.leds().push_back(led);
|
|
}
|
|
|
|
}
|
|
|
|
// Make sure the leds are sorted (on their indices)
|
|
std::sort(ledString.leds().begin(), ledString.leds().end(), [](const Led& lhs, const Led& rhs){ return lhs.index < rhs.index; });
|
|
return ledString;
|
|
}
|
|
|
|
LinearColorSmoothing * Hyperion::createColorSmoothing(const Json::Value & smoothingConfig, LedDevice* leddevice)
|
|
{
|
|
Logger * log = Logger::getInstance("Core");
|
|
std::string type = smoothingConfig.get("type", "linear").asString();
|
|
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
|
|
LinearColorSmoothing * device = nullptr;
|
|
type = "linear"; // TODO currently hardcoded type, delete it if we have more types
|
|
|
|
if (type == "linear")
|
|
{
|
|
Info( log, "Creating linear smoothing");
|
|
device = new LinearColorSmoothing(
|
|
leddevice,
|
|
smoothingConfig.get("updateFrequency", 25.0).asDouble(),
|
|
smoothingConfig.get("time_ms", 200).asInt(),
|
|
smoothingConfig.get("updateDelay", 0).asUInt(),
|
|
smoothingConfig.get("continuousOutput", true).asBool()
|
|
);
|
|
}
|
|
else
|
|
{
|
|
Error(log, "Smoothing disabled, because of unknown type '%s'.", type.c_str());
|
|
}
|
|
|
|
device->setEnable(smoothingConfig.get("enable", true).asBool());
|
|
InfoIf(!device->enabled(), log,"Smoothing disabled");
|
|
|
|
assert(device == nullptr);
|
|
return device;
|
|
}
|
|
|
|
MessageForwarder * Hyperion::createMessageForwarder(const Json::Value & forwarderConfig)
|
|
{
|
|
MessageForwarder * forwarder = new MessageForwarder();
|
|
if ( ( ! forwarderConfig.isNull() ) && ( forwarderConfig.get("enable", true).asBool() ) )
|
|
{
|
|
if ( ! forwarderConfig["json"].isNull() && forwarderConfig["json"].isArray() )
|
|
{
|
|
for (const Json::Value& addr : forwarderConfig["json"])
|
|
{
|
|
Info(Logger::getInstance("Core"), "Json forward to %s", addr.asString().c_str());
|
|
forwarder->addJsonSlave(addr.asString());
|
|
}
|
|
}
|
|
|
|
if ( ! forwarderConfig["proto"].isNull() && forwarderConfig["proto"].isArray() )
|
|
{
|
|
for (const Json::Value& addr : forwarderConfig["proto"])
|
|
{
|
|
Info(Logger::getInstance("Core"), "Proto forward to %s", addr.asString().c_str());
|
|
forwarder->addProtoSlave(addr.asString());
|
|
}
|
|
}
|
|
}
|
|
|
|
return forwarder;
|
|
}
|
|
|
|
MessageForwarder * Hyperion::getForwarder()
|
|
{
|
|
return _messageForwarder;
|
|
}
|
|
|
|
Hyperion::Hyperion(const Json::Value &jsonConfig, const std::string configFile)
|
|
: _ledString(createLedString(jsonConfig["leds"], createColorOrder(jsonConfig["device"])))
|
|
, _ledStringClone(createLedStringClone(jsonConfig["leds"], createColorOrder(jsonConfig["device"])))
|
|
, _muxer(_ledString.leds().size())
|
|
, _raw2ledTransform(createLedColorsTransform(_ledString.leds().size(), jsonConfig["color"]))
|
|
, _raw2ledTemperature(createLedColorsTemperature(_ledString.leds().size(), jsonConfig["color"]))
|
|
, _raw2ledAdjustment(createLedColorsAdjustment(_ledString.leds().size(), jsonConfig["color"]))
|
|
, _effectEngine(nullptr)
|
|
, _messageForwarder(createMessageForwarder(jsonConfig["forwarder"]))
|
|
, _jsonConfig(jsonConfig)
|
|
, _configFile(configFile)
|
|
, _timer()
|
|
, _log(Logger::getInstance("Core"))
|
|
, _hwLedCount(_ledString.leds().size())
|
|
, _sourceAutoSelectEnabled(true)
|
|
{
|
|
registerPriority("Off", PriorityMuxer::LOWEST_PRIORITY);
|
|
|
|
if (!_raw2ledAdjustment->verifyAdjustments())
|
|
{
|
|
throw std::runtime_error("Color adjustment incorrectly set");
|
|
}
|
|
if (!_raw2ledTemperature->verifyCorrections())
|
|
{
|
|
throw std::runtime_error("Color temperature incorrectly set");
|
|
}
|
|
if (!_raw2ledTransform->verifyTransforms())
|
|
{
|
|
throw std::runtime_error("Color transformation incorrectly set");
|
|
}
|
|
// set color correction activity state
|
|
_transformEnabled = jsonConfig["color"].get("transform_enable",true).asBool();
|
|
_adjustmentEnabled = jsonConfig["color"].get("channelAdjustment_enable",true).asBool();
|
|
_temperatureEnabled = jsonConfig["color"].get("temperature_enable",true).asBool();
|
|
|
|
InfoIf(!_transformEnabled , _log, "Color transformation disabled" );
|
|
InfoIf(!_adjustmentEnabled , _log, "Color adjustment disabled" );
|
|
InfoIf(!_temperatureEnabled, _log, "Color temperature disabled" );
|
|
|
|
// initialize the image processor factory
|
|
ImageProcessorFactory::getInstance().init(
|
|
_ledString,
|
|
jsonConfig["blackborderdetector"]
|
|
);
|
|
getComponentRegister().componentStateChanged(hyperion::COMP_FORWARDER, _messageForwarder->forwardingEnabled());
|
|
|
|
// initialize leddevices
|
|
_device = LedDeviceFactory::construct(jsonConfig["device"]);
|
|
_deviceSmooth = createColorSmoothing(jsonConfig["smoothing"], _device);
|
|
getComponentRegister().componentStateChanged(hyperion::COMP_SMOOTHING, _deviceSmooth->componentState());
|
|
|
|
// setup the timer
|
|
_timer.setSingleShot(true);
|
|
QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(update()));
|
|
|
|
// create the effect engine
|
|
_effectEngine = new EffectEngine(this,jsonConfig["effects"]);
|
|
|
|
unsigned int hwLedCount = jsonConfig["device"].get("ledCount",getLedCount()).asUInt();
|
|
_hwLedCount = std::max(hwLedCount, getLedCount());
|
|
Debug(_log,"configured leds: %d hw leds: %d", getLedCount(), _hwLedCount);
|
|
WarningIf(hwLedCount < getLedCount(), _log, "more leds configured than available. check 'ledCount' in 'device' section");
|
|
|
|
// initialize the leds
|
|
update();
|
|
}
|
|
|
|
|
|
Hyperion::~Hyperion()
|
|
{
|
|
// switch off all leds
|
|
clearall();
|
|
_device->switchOff();
|
|
|
|
// delete components on exit of hyperion core
|
|
delete _effectEngine;
|
|
delete _device;
|
|
delete _raw2ledTransform;
|
|
delete _raw2ledTemperature;
|
|
delete _raw2ledAdjustment;
|
|
delete _messageForwarder;
|
|
}
|
|
|
|
unsigned Hyperion::getLedCount() const
|
|
{
|
|
return _ledString.leds().size();
|
|
}
|
|
|
|
void Hyperion::registerPriority(const std::string name, const int priority)
|
|
{
|
|
Info(_log, "Register new input source named '%s' for priority channel '%d'", name.c_str(), priority );
|
|
|
|
for(auto const &entry : _priorityRegister)
|
|
{
|
|
WarningIf( ( entry.first != name && entry.second == priority), _log,
|
|
"Input source '%s' uses same priority channel (%d) as '%s'.", name.c_str(), priority, entry.first.c_str());
|
|
}
|
|
|
|
_priorityRegister.emplace(name,priority);
|
|
}
|
|
|
|
void Hyperion::unRegisterPriority(const std::string name)
|
|
{
|
|
Info(_log, "Unregister input source named '%s' from priority register", name.c_str());
|
|
_priorityRegister.erase(name);
|
|
}
|
|
|
|
void Hyperion::setSourceAutoSelectEnabled(bool enabled)
|
|
{
|
|
_sourceAutoSelectEnabled = enabled;
|
|
if (! _sourceAutoSelectEnabled)
|
|
{
|
|
setCurrentSourcePriority(_muxer.getCurrentPriority());
|
|
}
|
|
DebugIf( !_sourceAutoSelectEnabled, _log, "source auto select is disabled");
|
|
InfoIf(_sourceAutoSelectEnabled, _log, "set current input source to auto select");
|
|
}
|
|
|
|
bool Hyperion::setCurrentSourcePriority(int priority )
|
|
{
|
|
bool priorityValid = _muxer.hasPriority(priority);
|
|
if (priorityValid)
|
|
{
|
|
DebugIf(_sourceAutoSelectEnabled, _log, "source auto select is disabled");
|
|
_sourceAutoSelectEnabled = false;
|
|
_currentSourcePriority = priority;
|
|
Info(_log, "set current input source to priority channel %d", _currentSourcePriority);
|
|
}
|
|
|
|
return priorityValid;
|
|
}
|
|
|
|
void Hyperion::setComponentState(const hyperion::Components component, const bool state)
|
|
{
|
|
if (component == hyperion::COMP_SMOOTHING)
|
|
{
|
|
_deviceSmooth->setEnable(state);
|
|
getComponentRegister().componentStateChanged(hyperion::COMP_SMOOTHING, _deviceSmooth->componentState());
|
|
}
|
|
else
|
|
{
|
|
emit componentStateChanged(component, state);
|
|
}
|
|
}
|
|
|
|
void Hyperion::setColor(int priority, const ColorRgb &color, const int timeout_ms, bool clearEffects)
|
|
{
|
|
// create led output
|
|
std::vector<ColorRgb> ledColors(_ledString.leds().size(), color);
|
|
|
|
// set colors
|
|
setColors(priority, ledColors, timeout_ms, clearEffects);
|
|
}
|
|
|
|
void Hyperion::setColors(int priority, const std::vector<ColorRgb>& ledColors, const int timeout_ms, bool clearEffects)
|
|
{
|
|
// clear effects if this call does not come from an effect
|
|
if (clearEffects)
|
|
{
|
|
_effectEngine->channelCleared(priority);
|
|
}
|
|
|
|
if (timeout_ms > 0)
|
|
{
|
|
const uint64_t timeoutTime = QDateTime::currentMSecsSinceEpoch() + timeout_ms;
|
|
_muxer.setInput(priority, ledColors, timeoutTime);
|
|
}
|
|
else
|
|
{
|
|
_muxer.setInput(priority, ledColors);
|
|
}
|
|
|
|
if (priority == _muxer.getCurrentPriority())
|
|
{
|
|
update();
|
|
}
|
|
}
|
|
|
|
const std::vector<std::string> & Hyperion::getTransformIds() const
|
|
{
|
|
return _raw2ledTransform->getTransformIds();
|
|
}
|
|
|
|
const std::vector<std::string> & Hyperion::getTemperatureIds() const
|
|
{
|
|
return _raw2ledTemperature->getCorrectionIds();
|
|
}
|
|
|
|
const std::vector<std::string> & Hyperion::getAdjustmentIds() const
|
|
{
|
|
return _raw2ledAdjustment->getAdjustmentIds();
|
|
}
|
|
|
|
ColorTransform * Hyperion::getTransform(const std::string& id)
|
|
{
|
|
return _raw2ledTransform->getTransform(id);
|
|
}
|
|
|
|
ColorCorrection * Hyperion::getTemperature(const std::string& id)
|
|
{
|
|
return _raw2ledTemperature->getCorrection(id);
|
|
}
|
|
|
|
ColorAdjustment * Hyperion::getAdjustment(const std::string& id)
|
|
{
|
|
return _raw2ledAdjustment->getAdjustment(id);
|
|
}
|
|
|
|
void Hyperion::transformsUpdated()
|
|
{
|
|
update();
|
|
}
|
|
|
|
void Hyperion::correctionsUpdated()
|
|
{
|
|
update();
|
|
}
|
|
|
|
void Hyperion::temperaturesUpdated()
|
|
{
|
|
update();
|
|
}
|
|
|
|
void Hyperion::adjustmentsUpdated()
|
|
{
|
|
update();
|
|
}
|
|
|
|
void Hyperion::clear(int priority)
|
|
{
|
|
if (_muxer.hasPriority(priority))
|
|
{
|
|
_muxer.clearInput(priority);
|
|
|
|
// update leds if necessary
|
|
if (priority < _muxer.getCurrentPriority())
|
|
{
|
|
update();
|
|
}
|
|
}
|
|
|
|
// send clear signal to the effect engine
|
|
// (outside the check so the effect gets cleared even when the effect is not sending colors)
|
|
_effectEngine->channelCleared(priority);
|
|
}
|
|
|
|
void Hyperion::clearall()
|
|
{
|
|
_muxer.clearAll();
|
|
|
|
// update leds
|
|
update();
|
|
|
|
// send clearall signal to the effect engine
|
|
_effectEngine->allChannelsCleared();
|
|
}
|
|
|
|
int Hyperion::getCurrentPriority() const
|
|
{
|
|
|
|
return _sourceAutoSelectEnabled || !_muxer.hasPriority(_currentSourcePriority) ? _muxer.getCurrentPriority() : _currentSourcePriority;
|
|
}
|
|
|
|
QList<int> Hyperion::getActivePriorities() const
|
|
{
|
|
return _muxer.getPriorities();
|
|
}
|
|
|
|
const Hyperion::InputInfo &Hyperion::getPriorityInfo(const int priority) const
|
|
{
|
|
return _muxer.getInputInfo(priority);
|
|
}
|
|
|
|
const std::list<EffectDefinition> & Hyperion::getEffects() const
|
|
{
|
|
return _effectEngine->getEffects();
|
|
}
|
|
|
|
const std::list<ActiveEffectDefinition> & Hyperion::getActiveEffects()
|
|
{
|
|
return _effectEngine->getActiveEffects();
|
|
}
|
|
|
|
int Hyperion::setEffect(const std::string &effectName, int priority, int timeout)
|
|
{
|
|
return _effectEngine->runEffect(effectName, priority, timeout);
|
|
}
|
|
|
|
int Hyperion::setEffect(const std::string &effectName, const Json::Value &args, int priority, int timeout)
|
|
{
|
|
return _effectEngine->runEffect(effectName, args, priority, timeout);
|
|
}
|
|
|
|
void Hyperion::update()
|
|
{
|
|
// Update the muxer, cleaning obsolete priorities
|
|
_muxer.setCurrentTime(QDateTime::currentMSecsSinceEpoch());
|
|
|
|
// Obtain the current priority channel
|
|
int priority = _sourceAutoSelectEnabled || !_muxer.hasPriority(_currentSourcePriority) ? _muxer.getCurrentPriority() : _currentSourcePriority;
|
|
const PriorityMuxer::InputInfo & priorityInfo = _muxer.getInputInfo(priority);
|
|
|
|
// copy ledcolors to local buffer
|
|
_ledBuffer.reserve(_hwLedCount);
|
|
_ledBuffer = priorityInfo.ledColors;
|
|
|
|
// Apply the correction and the transform to each led and color-channel
|
|
// Avoid applying correction, the same task is performed by adjustment
|
|
if (_transformEnabled) _raw2ledTransform->applyTransform(_ledBuffer);
|
|
if (_adjustmentEnabled) _raw2ledAdjustment->applyAdjustment(_ledBuffer);
|
|
if (_temperatureEnabled) _raw2ledTemperature->applyCorrection(_ledBuffer);
|
|
|
|
// init colororder vector, if nempty
|
|
if (_ledStringColorOrder.empty())
|
|
{
|
|
for (Led& led : _ledString.leds())
|
|
{
|
|
_ledStringColorOrder.push_back(led.colorOrder);
|
|
}
|
|
for (Led& led : _ledStringClone.leds())
|
|
{
|
|
_ledStringColorOrder.insert(_ledStringColorOrder.begin() + led.index, led.colorOrder);
|
|
}
|
|
}
|
|
|
|
// insert cloned leds into buffer
|
|
for (Led& led : _ledStringClone.leds())
|
|
{
|
|
_ledBuffer.insert(_ledBuffer.begin() + led.index, _ledBuffer.at(led.clone));
|
|
}
|
|
|
|
int i = 0;
|
|
for (ColorRgb& color : _ledBuffer)
|
|
{
|
|
//const ColorOrder ledColorOrder = leds.at(i).colorOrder;
|
|
|
|
// correct the color byte order
|
|
switch (_ledStringColorOrder.at(i))
|
|
{
|
|
case ORDER_RGB:
|
|
// leave as it is
|
|
break;
|
|
case ORDER_BGR:
|
|
std::swap(color.red, color.blue);
|
|
break;
|
|
case ORDER_RBG:
|
|
std::swap(color.green, color.blue);
|
|
break;
|
|
case ORDER_GRB:
|
|
std::swap(color.red, color.green);
|
|
break;
|
|
case ORDER_GBR:
|
|
{
|
|
std::swap(color.red, color.green);
|
|
std::swap(color.green, color.blue);
|
|
break;
|
|
}
|
|
case ORDER_BRG:
|
|
{
|
|
std::swap(color.red, color.blue);
|
|
std::swap(color.green, color.blue);
|
|
break;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if ( _hwLedCount > _ledBuffer.size() )
|
|
{
|
|
_ledBuffer.resize(_hwLedCount, ColorRgb::BLACK);
|
|
}
|
|
|
|
// Write the data to the device
|
|
if (_deviceSmooth->enabled())
|
|
_deviceSmooth->write(_ledBuffer);
|
|
else
|
|
_device->write(_ledBuffer);
|
|
|
|
// Start the timeout-timer
|
|
if (priorityInfo.timeoutTime_ms == -1)
|
|
{
|
|
_timer.stop();
|
|
}
|
|
else
|
|
{
|
|
int timeout_ms = std::max(0, int(priorityInfo.timeoutTime_ms - QDateTime::currentMSecsSinceEpoch()));
|
|
_timer.start(timeout_ms);
|
|
}
|
|
}
|