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:
@@ -1,10 +1,8 @@
|
||||
|
||||
// STL includes
|
||||
#include <cassert>
|
||||
#include <exception>
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
|
||||
// QT includes
|
||||
#include <QDateTime>
|
||||
@@ -208,10 +206,10 @@ LedString Hyperion::createLedString(const QJsonValue& ledsConfig, const ColorOrd
|
||||
{
|
||||
const QJsonObject& hscanConfig = ledConfigArray[i].toObject()["hscan"].toObject();
|
||||
const QJsonObject& vscanConfig = ledConfigArray[i].toObject()["vscan"].toObject();
|
||||
led.minX_frac = std::max(0.0, std::min(1.0, hscanConfig["minimum"].toDouble()));
|
||||
led.maxX_frac = std::max(0.0, std::min(1.0, hscanConfig["maximum"].toDouble()));
|
||||
led.minY_frac = std::max(0.0, std::min(1.0, vscanConfig["minimum"].toDouble()));
|
||||
led.maxY_frac = std::max(0.0, std::min(1.0, vscanConfig["maximum"].toDouble()));
|
||||
led.minX_frac = qMax(0.0, qMin(1.0, hscanConfig["minimum"].toDouble()));
|
||||
led.maxX_frac = qMax(0.0, qMin(1.0, hscanConfig["maximum"].toDouble()));
|
||||
led.minY_frac = qMax(0.0, qMin(1.0, vscanConfig["minimum"].toDouble()));
|
||||
led.maxY_frac = qMax(0.0, qMin(1.0, vscanConfig["maximum"].toDouble()));
|
||||
// Fix if the user swapped min and max
|
||||
if (led.minX_frac > led.maxX_frac)
|
||||
{
|
||||
@@ -287,10 +285,10 @@ QSize Hyperion::getLedLayoutGridSize(const QJsonValue& ledsConfig)
|
||||
{
|
||||
const QJsonObject& hscanConfig = ledConfigArray[i].toObject()["hscan"].toObject();
|
||||
const QJsonObject& vscanConfig = ledConfigArray[i].toObject()["vscan"].toObject();
|
||||
double minX_frac = std::max(0.0, std::min(1.0, hscanConfig["minimum"].toDouble()));
|
||||
double maxX_frac = std::max(0.0, std::min(1.0, hscanConfig["maximum"].toDouble()));
|
||||
double minY_frac = std::max(0.0, std::min(1.0, vscanConfig["minimum"].toDouble()));
|
||||
double maxY_frac = std::max(0.0, std::min(1.0, vscanConfig["maximum"].toDouble()));
|
||||
double minX_frac = qMax(0.0, qMin(1.0, hscanConfig["minimum"].toDouble()));
|
||||
double maxX_frac = qMax(0.0, qMin(1.0, hscanConfig["maximum"].toDouble()));
|
||||
double minY_frac = qMax(0.0, qMin(1.0, vscanConfig["minimum"].toDouble()));
|
||||
double maxY_frac = qMax(0.0, qMin(1.0, vscanConfig["maximum"].toDouble()));
|
||||
// Fix if the user swapped min and max
|
||||
if (minX_frac > maxX_frac)
|
||||
{
|
||||
@@ -345,7 +343,7 @@ LinearColorSmoothing * Hyperion::createColorSmoothing(const QJsonObject & smooth
|
||||
device->setEnable(smoothingConfig["enable"].toBool(true));
|
||||
InfoIf(!device->enabled(), CORE_LOGGER,"Smoothing disabled");
|
||||
|
||||
assert(device != nullptr);
|
||||
Q_ASSERT(device != nullptr);
|
||||
return device;
|
||||
}
|
||||
|
||||
@@ -427,6 +425,8 @@ Hyperion::Hyperion(const QJsonObject &qjsonConfig, const QString configFile)
|
||||
getComponentRegister().componentStateChanged(hyperion::COMP_SMOOTHING, _deviceSmooth->componentState());
|
||||
getComponentRegister().componentStateChanged(hyperion::COMP_LEDDEVICE, _device->componentState());
|
||||
|
||||
_deviceSmooth->addConfig(true); // add pause to config 1
|
||||
|
||||
// setup the timer
|
||||
_timer.setSingleShot(true);
|
||||
QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(update()));
|
||||
@@ -436,12 +436,12 @@ Hyperion::Hyperion(const QJsonObject &qjsonConfig, const QString configFile)
|
||||
QObject::connect(&_timerBonjourResolver, SIGNAL(timeout()), this, SLOT(bonjourResolve()));
|
||||
_timerBonjourResolver.start();
|
||||
|
||||
// create the effect engine
|
||||
// create the effect engine, must be initialized after smoothing!
|
||||
_effectEngine = new EffectEngine(this,qjsonConfig["effects"].toObject() );
|
||||
|
||||
const QJsonObject& device = qjsonConfig["device"].toObject();
|
||||
unsigned int hwLedCount = device["ledCount"].toInt(getLedCount());
|
||||
_hwLedCount = std::max(hwLedCount, getLedCount());
|
||||
_hwLedCount = qMax(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");
|
||||
|
||||
@@ -478,6 +478,11 @@ int Hyperion::getLatchTime() const
|
||||
return _device->getLatchTime();
|
||||
}
|
||||
|
||||
unsigned Hyperion::addSmoothingConfig(int settlingTime_ms, double ledUpdateFrequency_hz, unsigned updateDelay)
|
||||
{
|
||||
return _deviceSmooth->addConfig(settlingTime_ms, ledUpdateFrequency_hz, updateDelay);
|
||||
}
|
||||
|
||||
void Hyperion::freeObjects(bool emitCloseSignal)
|
||||
{
|
||||
if (emitCloseSignal)
|
||||
@@ -664,7 +669,7 @@ void Hyperion::setColor(int priority, const ColorRgb &color, const int timeout_m
|
||||
setColors(priority, ledColors, timeout_ms, clearEffects, hyperion::COMP_COLOR);
|
||||
}
|
||||
|
||||
void Hyperion::setColors(int priority, const std::vector<ColorRgb>& ledColors, const int timeout_ms, bool clearEffects, hyperion::Components component, const QString origin)
|
||||
void Hyperion::setColors(int priority, const std::vector<ColorRgb>& ledColors, const int timeout_ms, bool clearEffects, hyperion::Components component, const QString origin, unsigned smoothCfg)
|
||||
{
|
||||
// clear effects if this call does not come from an effect
|
||||
if (clearEffects)
|
||||
@@ -675,11 +680,11 @@ void Hyperion::setColors(int priority, const std::vector<ColorRgb>& ledColors, c
|
||||
if (timeout_ms > 0)
|
||||
{
|
||||
const uint64_t timeoutTime = QDateTime::currentMSecsSinceEpoch() + timeout_ms;
|
||||
_muxer.setInput(priority, ledColors, timeoutTime, component, origin);
|
||||
_muxer.setInput(priority, ledColors, timeoutTime, component, origin, smoothCfg);
|
||||
}
|
||||
else
|
||||
{
|
||||
_muxer.setInput(priority, ledColors, -1, component, origin);
|
||||
_muxer.setInput(priority, ledColors, -1, component, origin, smoothCfg);
|
||||
}
|
||||
|
||||
if (! _sourceAutoSelectEnabled || priority == _muxer.getCurrentPriority())
|
||||
@@ -891,7 +896,8 @@ void Hyperion::update()
|
||||
// Write the data to the device
|
||||
if (_device->enabled())
|
||||
{
|
||||
_deviceSmooth->setPause(priorityInfo.componentId == hyperion::COMP_EFFECT);
|
||||
_deviceSmooth->selectConfig(priorityInfo.smooth_cfg);
|
||||
|
||||
// feed smoothing in pause mode to maintain a smooth transistion back to smoth mode
|
||||
if (_deviceSmooth->enabled() || _deviceSmooth->pause())
|
||||
_deviceSmooth->setLedValues(_ledBuffer);
|
||||
@@ -907,9 +913,9 @@ void Hyperion::update()
|
||||
}
|
||||
else
|
||||
{
|
||||
int timeout_ms = std::max(0, int(priorityInfo.timeoutTime_ms - QDateTime::currentMSecsSinceEpoch()));
|
||||
// std::min() 200ms forced refresh if color is active to update priorityMuxer properly for forced serverinfo push
|
||||
_timer.start(std::min(timeout_ms, 200));
|
||||
int timeout_ms = qMax(0, int(priorityInfo.timeoutTime_ms - QDateTime::currentMSecsSinceEpoch()));
|
||||
// qMin() 200ms forced refresh if color is active to update priorityMuxer properly for forced serverinfo push
|
||||
_timer.start(qMin(timeout_ms, 200));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,9 +1,3 @@
|
||||
// STL includes
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
|
||||
// hyperion includes
|
||||
#include <hyperion/ImageToLedsMap.h>
|
||||
|
||||
using namespace hyperion;
|
||||
@@ -21,8 +15,8 @@ ImageToLedsMap::ImageToLedsMap(
|
||||
, _colorsMap()
|
||||
{
|
||||
// Sanity check of the size of the borders (and width and height)
|
||||
assert(_width > 2*_verticalBorder);
|
||||
assert(_height > 2*_horizontalBorder);
|
||||
Q_ASSERT(_width > 2*_verticalBorder);
|
||||
Q_ASSERT(_height > 2*_horizontalBorder);
|
||||
|
||||
// Reserve enough space in the map for the leds
|
||||
_colorsMap.reserve(leds.size());
|
||||
@@ -42,18 +36,18 @@ ImageToLedsMap::ImageToLedsMap(
|
||||
}
|
||||
|
||||
// Compute the index boundaries for this led
|
||||
unsigned minX_idx = xOffset + unsigned(std::round(actualWidth * led.minX_frac));
|
||||
unsigned maxX_idx = xOffset + unsigned(std::round(actualWidth * led.maxX_frac));
|
||||
unsigned minY_idx = yOffset + unsigned(std::round(actualHeight * led.minY_frac));
|
||||
unsigned maxY_idx = yOffset + unsigned(std::round(actualHeight * led.maxY_frac));
|
||||
unsigned minX_idx = xOffset + unsigned(qRound(actualWidth * led.minX_frac));
|
||||
unsigned maxX_idx = xOffset + unsigned(qRound(actualWidth * led.maxX_frac));
|
||||
unsigned minY_idx = yOffset + unsigned(qRound(actualHeight * led.minY_frac));
|
||||
unsigned maxY_idx = yOffset + unsigned(qRound(actualHeight * led.maxY_frac));
|
||||
|
||||
// make sure that the area is at least a single led large
|
||||
minX_idx = std::min(minX_idx, xOffset + actualWidth - 1);
|
||||
minX_idx = qMin(minX_idx, xOffset + actualWidth - 1);
|
||||
if (minX_idx == maxX_idx)
|
||||
{
|
||||
maxX_idx = minX_idx + 1;
|
||||
}
|
||||
minY_idx = std::min(minY_idx, yOffset + actualHeight - 1);
|
||||
minY_idx = qMin(minY_idx, yOffset + actualHeight - 1);
|
||||
if (minY_idx == maxY_idx)
|
||||
{
|
||||
maxY_idx = minY_idx + 1;
|
||||
|
@@ -8,6 +8,7 @@
|
||||
|
||||
using namespace hyperion;
|
||||
|
||||
// ledUpdateFrequency_hz = 0 > cause divide by zero!
|
||||
LinearColorSmoothing::LinearColorSmoothing( LedDevice * ledDevice, double ledUpdateFrequency_hz, int settlingTime_ms, unsigned updateDelay, bool continuousOutput)
|
||||
: LedDevice()
|
||||
, _ledDevice(ledDevice)
|
||||
@@ -18,14 +19,20 @@ LinearColorSmoothing::LinearColorSmoothing( LedDevice * ledDevice, double ledUpd
|
||||
, _writeToLedsEnable(true)
|
||||
, _continuousOutput(continuousOutput)
|
||||
, _pause(false)
|
||||
, _currentConfigId(0)
|
||||
{
|
||||
_log = Logger::getInstance("Smoothing");
|
||||
_timer.setSingleShot(false);
|
||||
_timer.setInterval(_updateInterval);
|
||||
|
||||
selectConfig( addConfig(_settlingTime, ledUpdateFrequency_hz, updateDelay) );
|
||||
|
||||
// add pause on cfg 1
|
||||
SMOOTHING_CFG cfg = {true, 100, 50, 0};
|
||||
_cfgList.append(cfg);
|
||||
Info( _log, "smoothing cfg %d: pause", _cfgList.count()-1);
|
||||
|
||||
connect(&_timer, SIGNAL(timeout()), this, SLOT(updateLeds()));
|
||||
Info( _log, "Created linear-smoothing with interval: %d ms, settlingTime: %d ms, updateDelay: %d frames",
|
||||
_updateInterval, settlingTime_ms, _outputDelay );
|
||||
}
|
||||
|
||||
LinearColorSmoothing::~LinearColorSmoothing()
|
||||
@@ -160,3 +167,44 @@ void LinearColorSmoothing::setPause(bool pause)
|
||||
_pause = pause;
|
||||
}
|
||||
|
||||
unsigned LinearColorSmoothing::addConfig(int settlingTime_ms, double ledUpdateFrequency_hz, unsigned updateDelay)
|
||||
{
|
||||
SMOOTHING_CFG cfg = {false, settlingTime_ms, int64_t(1000.0/ledUpdateFrequency_hz), updateDelay};
|
||||
_cfgList.append(cfg);
|
||||
|
||||
Info( _log, "smoothing cfg %d: interval: %d ms, settlingTime: %d ms, updateDelay: %d frames", _cfgList.count()-1, cfg.updateInterval, cfg.settlingTime, cfg.outputDelay );
|
||||
return _cfgList.count() - 1;
|
||||
}
|
||||
|
||||
bool LinearColorSmoothing::selectConfig(unsigned cfg)
|
||||
{
|
||||
if (_currentConfigId == cfg)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( cfg < (unsigned)_cfgList.count())
|
||||
{
|
||||
_settlingTime = _cfgList[cfg].settlingTime;
|
||||
_outputDelay = _cfgList[cfg].outputDelay;
|
||||
_pause = _cfgList[cfg].pause;
|
||||
|
||||
if (_cfgList[cfg].updateInterval != _updateInterval)
|
||||
{
|
||||
_timer.stop();
|
||||
_updateInterval = _cfgList[cfg].updateInterval;
|
||||
_timer.setInterval(_updateInterval);
|
||||
_timer.start();
|
||||
}
|
||||
_currentConfigId = cfg;
|
||||
InfoIf( enabled() && !_pause, _log, "set smoothing cfg: %d, interval: %d ms, settlingTime: %d ms, updateDelay: %d frames", _currentConfigId, _updateInterval, _settlingTime, _outputDelay );
|
||||
InfoIf( _pause, _log, "set smoothing cfg: %d, pause", _currentConfigId );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// reset to default
|
||||
_currentConfigId = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -6,6 +6,7 @@
|
||||
|
||||
// Qt includes
|
||||
#include <QTimer>
|
||||
#include <QVector>
|
||||
|
||||
// hyperion incluse
|
||||
#include <leddevice/LedDevice.h>
|
||||
@@ -33,7 +34,7 @@ public:
|
||||
/// write updated values as input for the smoothing filter
|
||||
///
|
||||
/// @param ledValues The color-value per led
|
||||
/// @return Zero on succes else negative
|
||||
/// @return Zero on success else negative
|
||||
///
|
||||
virtual int write(const std::vector<ColorRgb> &ledValues);
|
||||
|
||||
@@ -45,6 +46,9 @@ public:
|
||||
bool pause() { return _pause; } ;
|
||||
bool enabled() { return LedDevice::enabled() && !_pause; };
|
||||
|
||||
unsigned addConfig(int settlingTime_ms, double ledUpdateFrequency_hz=25.0, unsigned updateDelay=0);
|
||||
bool selectConfig(unsigned cfg);
|
||||
|
||||
private slots:
|
||||
/// Timer callback which writes updated led values to the led device
|
||||
void updateLeds();
|
||||
@@ -61,10 +65,10 @@ private:
|
||||
LedDevice * _ledDevice;
|
||||
|
||||
/// The interval at which to update the leds (msec)
|
||||
const int64_t _updateInterval;
|
||||
int64_t _updateInterval;
|
||||
|
||||
/// The time after which the updated led values have been fully applied (msec)
|
||||
const int64_t _settlingTime;
|
||||
int64_t _settlingTime;
|
||||
|
||||
/// The Qt timer object
|
||||
QTimer _timer;
|
||||
@@ -82,7 +86,7 @@ private:
|
||||
std::vector<ColorRgb> _previousValues;
|
||||
|
||||
/// The number of updates to keep in the output queue (delayed) before being output
|
||||
const unsigned _outputDelay;
|
||||
unsigned _outputDelay;
|
||||
/// The output queue
|
||||
std::list<std::vector<ColorRgb> > _outputQueue;
|
||||
|
||||
@@ -94,4 +98,17 @@ private:
|
||||
|
||||
/// Flag for pausing
|
||||
bool _pause;
|
||||
|
||||
struct SMOOTHING_CFG
|
||||
{
|
||||
bool pause;
|
||||
int64_t settlingTime;
|
||||
int64_t updateInterval;
|
||||
unsigned outputDelay;
|
||||
};
|
||||
|
||||
/// config list
|
||||
QVector<SMOOTHING_CFG> _cfgList;
|
||||
|
||||
unsigned _currentConfigId;
|
||||
};
|
||||
|
@@ -1,7 +1,3 @@
|
||||
|
||||
// STL includes
|
||||
#include <cassert>
|
||||
|
||||
// Hyperion includes
|
||||
#include <utils/Logger.h>
|
||||
#include "MultiColorAdjustment.h"
|
||||
@@ -29,8 +25,8 @@ void MultiColorAdjustment::addAdjustment(ColorAdjustment * adjustment)
|
||||
|
||||
void MultiColorAdjustment::setAdjustmentForLed(const QString& id, const unsigned startLed, const unsigned endLed)
|
||||
{
|
||||
assert(startLed <= endLed);
|
||||
assert(endLed < _ledAdjustments.size());
|
||||
Q_ASSERT(startLed <= endLed);
|
||||
Q_ASSERT(endLed < _ledAdjustments.size());
|
||||
|
||||
// Get the identified adjustment (don't care if is nullptr)
|
||||
ColorAdjustment * adjustment = getAdjustment(id);
|
||||
@@ -83,10 +79,9 @@ void MultiColorAdjustment::setBacklightEnabled(bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MultiColorAdjustment::applyAdjustment(std::vector<ColorRgb>& ledColors)
|
||||
{
|
||||
const size_t itCnt = std::min(_ledAdjustments.size(), ledColors.size());
|
||||
const size_t itCnt = qMin(_ledAdjustments.size(), ledColors.size());
|
||||
for (size_t i=0; i<itCnt; ++i)
|
||||
{
|
||||
ColorAdjustment* adjustment = _ledAdjustments[i];
|
||||
|
@@ -1,4 +1,3 @@
|
||||
#include <iostream>
|
||||
// STL includes
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
@@ -27,7 +26,6 @@ PriorityMuxer::PriorityMuxer(int ledCount)
|
||||
|
||||
PriorityMuxer::~PriorityMuxer()
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
int PriorityMuxer::getCurrentPriority() const
|
||||
@@ -55,7 +53,7 @@ const PriorityMuxer::InputInfo& PriorityMuxer::getInputInfo(const int priority)
|
||||
return elemIt.value();
|
||||
}
|
||||
|
||||
void PriorityMuxer::setInput(const int priority, const std::vector<ColorRgb>& ledColors, const int64_t timeoutTime_ms, hyperion::Components component, const QString origin)
|
||||
void PriorityMuxer::setInput(const int priority, const std::vector<ColorRgb>& ledColors, const int64_t timeoutTime_ms, hyperion::Components component, const QString origin, unsigned smooth_cfg)
|
||||
{
|
||||
InputInfo& input = _activeInputs[priority];
|
||||
input.priority = priority;
|
||||
@@ -63,7 +61,8 @@ void PriorityMuxer::setInput(const int priority, const std::vector<ColorRgb>& le
|
||||
input.ledColors = ledColors;
|
||||
input.componentId = component;
|
||||
input.origin = origin;
|
||||
_currentPriority = std::min(_currentPriority, priority);
|
||||
input.smooth_cfg = smooth_cfg;
|
||||
_currentPriority = qMin(_currentPriority, priority);
|
||||
}
|
||||
|
||||
void PriorityMuxer::clearInput(const int priority)
|
||||
@@ -101,8 +100,8 @@ void PriorityMuxer::setCurrentTime(const int64_t& now)
|
||||
infoIt = _activeInputs.erase(infoIt);
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentPriority = std::min(_currentPriority, infoIt->priority);
|
||||
{
|
||||
_currentPriority = qMin(_currentPriority, infoIt->priority);
|
||||
|
||||
// call emitReq when effect or color is running with timeout > -1, blacklist prio 255
|
||||
if(infoIt->priority < 254 && infoIt->timeoutTime_ms > -1 && (infoIt->componentId == hyperion::COMP_EFFECT || infoIt->componentId == hyperion::COMP_COLOR))
|
||||
|
Reference in New Issue
Block a user