mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
253 lines
6.5 KiB
C++
253 lines
6.5 KiB
C++
// Qt includes
|
|
#include <QDateTime>
|
|
#include <QTimer>
|
|
|
|
#include "LinearColorSmoothing.h"
|
|
#include <hyperion/Hyperion.h>
|
|
|
|
#include <cmath>
|
|
|
|
using namespace hyperion;
|
|
|
|
LinearColorSmoothing::LinearColorSmoothing( LedDevice * ledDevice, const QJsonDocument& config, Hyperion* hyperion)
|
|
: LedDevice()
|
|
, _ledDevice(ledDevice)
|
|
, _log(Logger::getInstance("SMOOTHING"))
|
|
, _hyperion(hyperion)
|
|
, _updateInterval(1000)
|
|
, _settlingTime(200)
|
|
, _timer(new QTimer(this))
|
|
, _outputDelay(0)
|
|
, _writeToLedsEnable(true)
|
|
, _continuousOutput(false)
|
|
, _pause(false)
|
|
, _currentConfigId(0)
|
|
{
|
|
// set initial state to true, as LedDevice::enabled() is true by default
|
|
_hyperion->getComponentRegister().componentStateChanged(hyperion::COMP_SMOOTHING, true);
|
|
|
|
// init cfg 0 (default)
|
|
_cfgList.append({false, 200, 25, 0});
|
|
handleSettingsUpdate(settings::SMOOTHING, config);
|
|
|
|
// add pause on cfg 1
|
|
SMOOTHING_CFG cfg = {true};
|
|
_cfgList.append(cfg);
|
|
|
|
// listen for comp changes
|
|
connect(_hyperion, &Hyperion::componentStateChanged, this, &LinearColorSmoothing::componentStateChange);
|
|
// timer
|
|
connect(_timer, SIGNAL(timeout()), this, SLOT(updateLeds()));
|
|
}
|
|
|
|
LinearColorSmoothing::~LinearColorSmoothing()
|
|
{
|
|
// Make sure to switch off the underlying led-device (because switchOff is no longer forwarded)
|
|
_ledDevice->switchOff();
|
|
}
|
|
|
|
void LinearColorSmoothing::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config)
|
|
{
|
|
if(type == settings::SMOOTHING)
|
|
{
|
|
QJsonObject obj = config.object();
|
|
_continuousOutput = obj["continuousOutput"].toBool(true);
|
|
SMOOTHING_CFG cfg = {false, obj["time_ms"].toInt(200), unsigned(1000.0/obj["updateFrequency"].toDouble(25.0)), unsigned(obj["updateDelay"].toInt(0))};
|
|
_cfgList[0] = cfg;
|
|
// if current id is 0, we need to apply the settings (forced)
|
|
if(!_currentConfigId)
|
|
selectConfig(0, true);
|
|
|
|
if(enabled() != obj["enable"].toBool(true))
|
|
setEnable(obj["enable"].toBool(true));
|
|
}
|
|
}
|
|
|
|
int LinearColorSmoothing::write(const std::vector<ColorRgb> &ledValues)
|
|
{
|
|
// received a new target color
|
|
if (_previousValues.empty())
|
|
{
|
|
// not initialized yet
|
|
_targetTime = QDateTime::currentMSecsSinceEpoch() + _settlingTime;
|
|
_targetValues = ledValues;
|
|
|
|
_previousTime = QDateTime::currentMSecsSinceEpoch();
|
|
_previousValues = ledValues;
|
|
_timer->start();
|
|
}
|
|
else
|
|
{
|
|
_targetTime = QDateTime::currentMSecsSinceEpoch() + _settlingTime;
|
|
memcpy(_targetValues.data(), ledValues.data(), ledValues.size() * sizeof(ColorRgb));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int LinearColorSmoothing::switchOff()
|
|
{
|
|
// We will keep updating the leds (but with pure-black)
|
|
|
|
// Clear the smoothing parameters
|
|
std::fill(_targetValues.begin(), _targetValues.end(), ColorRgb::BLACK);
|
|
_targetTime = 0;
|
|
|
|
// Erase the output-queue
|
|
for (unsigned i=0; i<_outputQueue.size(); ++i)
|
|
{
|
|
_outputQueue.push_back(_targetValues);
|
|
_outputQueue.pop_front();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void LinearColorSmoothing::updateLeds()
|
|
{
|
|
int64_t now = QDateTime::currentMSecsSinceEpoch();
|
|
int deltaTime = _targetTime - now;
|
|
|
|
if (deltaTime < 0)
|
|
{
|
|
memcpy(_previousValues.data(), _targetValues.data(), _targetValues.size() * sizeof(ColorRgb));
|
|
_previousTime = now;
|
|
|
|
queueColors(_previousValues);
|
|
_writeToLedsEnable = _continuousOutput;
|
|
}
|
|
else
|
|
{
|
|
_writeToLedsEnable = true;
|
|
float k = 1.0f - 1.0f * deltaTime / (_targetTime - _previousTime);
|
|
|
|
int reddif = 0, greendif = 0, bluedif = 0;
|
|
|
|
for (size_t i = 0; i < _previousValues.size(); ++i)
|
|
{
|
|
ColorRgb & prev = _previousValues[i];
|
|
ColorRgb & target = _targetValues[i];
|
|
|
|
reddif = target.red - prev.red;
|
|
greendif = target.green - prev.green;
|
|
bluedif = target.blue - prev.blue;
|
|
|
|
prev.red += (reddif < 0 ? -1:1) * std::ceil(k * std::abs(reddif));
|
|
prev.green += (greendif < 0 ? -1:1) * std::ceil(k * std::abs(greendif));
|
|
prev.blue += (bluedif < 0 ? -1:1) * std::ceil(k * std::abs(bluedif));
|
|
}
|
|
_previousTime = now;
|
|
|
|
queueColors(_previousValues);
|
|
}
|
|
}
|
|
|
|
void LinearColorSmoothing::queueColors(const std::vector<ColorRgb> & ledColors)
|
|
{
|
|
if (_outputDelay == 0)
|
|
{
|
|
// No output delay => immediate write
|
|
if ( _writeToLedsEnable && !_pause)
|
|
_ledDevice->setLedValues(ledColors);
|
|
}
|
|
else
|
|
{
|
|
// Push new colors in the delay-buffer
|
|
if ( _writeToLedsEnable )
|
|
_outputQueue.push_back(ledColors);
|
|
|
|
// If the delay-buffer is filled pop the front and write to device
|
|
if (_outputQueue.size() > 0 )
|
|
{
|
|
if ( _outputQueue.size() > _outputDelay || !_writeToLedsEnable )
|
|
{
|
|
if (!_pause)
|
|
{
|
|
_ledDevice->setLedValues(_outputQueue.front());
|
|
}
|
|
_outputQueue.pop_front();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LinearColorSmoothing::componentStateChange(const hyperion::Components component, const bool state)
|
|
{
|
|
if(component == hyperion::COMP_SMOOTHING)
|
|
setEnable(state);
|
|
}
|
|
|
|
void LinearColorSmoothing::setEnable(bool enable)
|
|
{
|
|
LedDevice::setEnable(enable);
|
|
|
|
if (!enable)
|
|
{
|
|
_timer->stop();
|
|
_previousValues.clear();
|
|
}
|
|
// update comp register
|
|
_hyperion->getComponentRegister().componentStateChanged(hyperion::COMP_SMOOTHING, enable);
|
|
}
|
|
|
|
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);
|
|
|
|
//Debug( _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, const bool& force)
|
|
{
|
|
if (_currentConfigId == cfg && !force)
|
|
{
|
|
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;
|
|
//DebugIf( enabled() && !_pause, _log, "set smoothing cfg: %d, interval: %d ms, settlingTime: %d ms, updateDelay: %d frames", _currentConfigId, _updateInterval, _settlingTime, _outputDelay );
|
|
DebugIf( _pause, _log, "set smoothing cfg: %d, pause", _currentConfigId );
|
|
|
|
return true;
|
|
}
|
|
|
|
// reset to default
|
|
_currentConfigId = 0;
|
|
return false;
|
|
}
|
|
|
|
void LinearColorSmoothing::startTimerDelayed()
|
|
{
|
|
QTimer::singleShot(500, this, SLOT(delayStartTimer()));
|
|
}
|
|
|
|
void LinearColorSmoothing::stopTimer()
|
|
{
|
|
_timer->stop();
|
|
}
|
|
|
|
void LinearColorSmoothing::delayStartTimer()
|
|
{
|
|
_timer->start();
|
|
}
|