• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mcallegari / qlcplus / 19144422256

06 Nov 2025 05:33PM UTC coverage: 34.256% (-0.1%) from 34.358%
19144422256

push

github

mcallegari
Back to 5.1.0 debug

17718 of 51723 relevant lines covered (34.26%)

19528.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.33
/engine/src/genericfader.cpp
1
/*
2
  Q Light Controller
3
  genericfader.cpp
4

5
  Copyright (c) Heikki Junnila
6

7
  Licensed under the Apache License, Version 2.0 (the "License");
8
  you may not use this file except in compliance with the License.
9
  You may obtain a copy of the License at
10

11
      http://www.apache.org/licenses/LICENSE-2.0.txt
12

13
  Unless required by applicable law or agreed to in writing, software
14
  distributed under the License is distributed on an "AS IS" BASIS,
15
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
  See the License for the specific language governing permissions and
17
  limitations under the License.
18
*/
19

20
#include <QDebug>
21

22
#include "genericfader.h"
23
#include "fadechannel.h"
24
#include "doc.h"
25

26
GenericFader::GenericFader(QObject *parent)
152✔
27
    : QObject(parent)
28
    , m_fid(Function::invalidId())
152✔
29
    , m_priority(Universe::Auto)
152✔
30
    , m_handleSecondary(false)
152✔
31
    , m_intensity(1.0)
152✔
32
    , m_parentIntensity(1.0)
152✔
33
    , m_paused(false)
152✔
34
    , m_enabled(true)
152✔
35
    , m_fadeOut(false)
152✔
36
    , m_deleteRequest(false)
152✔
37
    , m_blendMode(Universe::NormalBlend)
152✔
38
    , m_monitoring(false)
304✔
39
{
40
}
152✔
41

42
GenericFader::~GenericFader()
302✔
43
{
44
}
302✔
45

46
QString GenericFader::name() const
×
47
{
48
    return m_name;
×
49
}
50

51
void GenericFader::setName(QString name)
123✔
52
{
53
    m_name = name;
123✔
54
}
123✔
55

56
quint32 GenericFader::parentFunctionID() const
9✔
57
{
58
    return m_fid;
9✔
59
}
60

61
void GenericFader::setParentFunctionID(quint32 fid)
123✔
62
{
63
    m_fid = fid;
123✔
64
}
123✔
65

66
int GenericFader::priority() const
409✔
67
{
68
    return m_priority;
409✔
69
}
70

71
void GenericFader::setPriority(int priority)
151✔
72
{
73
    m_priority = priority;
151✔
74
}
151✔
75

76
bool GenericFader::handleSecondary()
1,589,840✔
77
{
78
    return m_handleSecondary;
1,589,840✔
79
}
80

81
void GenericFader::setHandleSecondary(bool enable)
123✔
82
{
83
    m_handleSecondary = enable;
123✔
84
}
123✔
85

86
quint32 GenericFader::channelHash(quint32 fixtureID, quint32 channel)
795,257✔
87
{
88
    return ((fixtureID & 0x0000FFFF) << 16) | (channel & 0x0000FFFF);
795,257✔
89
}
90

91
void GenericFader::add(const FadeChannel& ch)
17✔
92
{
93
    quint32 hash = channelHash(ch.fixture(), ch.channel());
17✔
94

95
    QWriteLocker l(&m_channelsLock);
17✔
96
    QHash<quint32,FadeChannel>::iterator channelIterator = m_channels.find(hash);
17✔
97
    if (channelIterator != m_channels.end())
17✔
98
    {
99
        // perform a HTP check
100
        if (channelIterator.value().current() <= ch.current())
3✔
101
            channelIterator.value() = ch;
3✔
102
    }
103
    else
104
    {
105
        m_channels.insert(hash, ch);
14✔
106
        qDebug() << "Added new fader with hash" << hash;
14✔
107
    }
108
}
17✔
109

110
void GenericFader::replace(const FadeChannel &ch)
1✔
111
{
112
    quint32 hash = channelHash(ch.fixture(), ch.channel());
1✔
113
    QWriteLocker l(&m_channelsLock);
1✔
114
    m_channels.insert(hash, ch);
1✔
115
}
1✔
116

117
void GenericFader::remove(FadeChannel *ch)
2✔
118
{
119
    if (ch == NULL)
2✔
120
        return;
×
121

122
    quint32 hash = channelHash(ch->fixture(), ch->channel());
2✔
123
    QWriteLocker l(&m_channelsLock);
2✔
124
    if (m_channels.remove(hash) == 0)
2✔
125
        qDebug() << "No FadeChannel found with hash" << hash;
1✔
126
}
2✔
127

128
void GenericFader::removeAll()
5✔
129
{
130
    QWriteLocker l(&m_channelsLock);
5✔
131
    m_channels.clear();
5✔
132
}
5✔
133

134
bool GenericFader::deleteRequested()
927,101✔
135
{
136
    return m_deleteRequest;
927,101✔
137
}
138

139
void GenericFader::requestDelete()
103✔
140
{
141
    m_deleteRequest = true;
103✔
142
}
103✔
143

144
FadeChannel *GenericFader::getChannelFader(const Doc *doc, Universe *universe, quint32 fixtureID, quint32 channel)
795,212✔
145
{
146
    FadeChannel fc(doc, fixtureID, channel);
795,212✔
147
    quint32 primary = fc.primaryChannel();
795,212✔
148
    quint32 hash;
149

150
    // calculate hash depending on primary channel presence
151
    if (handleSecondary() && primary != QLCChannel::invalid())
795,212✔
152
        hash = channelHash(fc.fixture(), primary);
×
153
    else
154
        hash = channelHash(fc.fixture(), fc.channel());
795,212✔
155

156
    m_channelsLock.lockForRead();
795,212✔
157
    // search for existing FadeChannel
158
    QHash<quint32,FadeChannel>::iterator channelIterator = m_channels.find(hash);
795,212✔
159
    if (channelIterator != m_channels.end())
795,212✔
160
    {
161
        FadeChannel *fcFound = &channelIterator.value();
794,626✔
162
        m_channelsLock.unlock();
794,626✔
163

164
        if (handleSecondary() &&
794,626✔
165
            fcFound->channelCount() == 1 &&
794,626✔
166
            primary != QLCChannel::invalid())
×
167
        {
168
            qDebug() << "Adding channel to primary" << channel;
×
169
            fcFound->addChannel(channel);
×
170
            if (universe)
×
171
                fcFound->setCurrent(universe->preGMValue(fcFound->address() + 1), 1);
×
172
        }
173
        return fcFound;
794,626✔
174
    }
175
    m_channelsLock.unlock();
586✔
176

177
    // set current universe value
178
    if (universe)
586✔
179
        fc.setCurrent(universe->preGMValue(fc.address()));
586✔
180

181
    // new channel. Add to GenericFader
182
    QWriteLocker l(&m_channelsLock);
586✔
183
    channelIterator = m_channels.insert(hash, fc);
586✔
184
    //qDebug() << "Added new fader with hash" << hash;
185

186
    return &channelIterator.value();
586✔
187
}
795,212✔
188

189
QHash<quint32, FadeChannel> GenericFader::channels() const
74✔
190
{
191
    QReadLocker l(&m_channelsLock);
74✔
192
    return m_channels;
148✔
193
}
74✔
194

195
int GenericFader::channelsCount() const
×
196
{
197
    QReadLocker l(&m_channelsLock);
×
198
    return m_channels.count();
×
199
}
×
200

201
void GenericFader::write(Universe *universe)
927,198✔
202
{
203
    if (m_monitoring)
927,198✔
204
        emit preWriteData(universe->id(), universe->preGMValues());
×
205

206
    qreal compIntensity = intensity() * parentIntensity();
927,198✔
207

208
    //qDebug() << "[GenericFader] writing channels: " << this << m_channels.count();
209

210
    // iterate through all the channels handled by this fader
211
    QWriteLocker l(&m_channelsLock);
927,198✔
212
    QMutableHashIterator <quint32,FadeChannel> it(m_channels);
927,198✔
213
    while (it.hasNext() == true)
2,649,599✔
214
    {
215
        FadeChannel& fc(it.next().value());
1,722,401✔
216
        int flags = fc.flags();
1,722,401✔
217
        quint32 address = fc.addressInUniverse();
1,722,401✔
218
        int channelCount = fc.channelCount();
1,722,401✔
219

220
        if (address == QLCChannel::invalid())
1,722,401✔
221
        {
222
            qWarning() << "Invalid channel found";
×
223
            continue;
8✔
224
        }
225

226
        if (flags & FadeChannel::SetTarget)
1,722,401✔
227
        {
228
            fc.removeFlag(FadeChannel::SetTarget);
2✔
229
            fc.addFlag(FadeChannel::AutoRemove);
2✔
230
            for (int i = 0; i < channelCount; i++)
4✔
231
                fc.setTarget(universe->preGMValue(address + i), i);
2✔
232
        }
233

234
        // Calculate the next step
235
        if (m_paused == false)
1,722,401✔
236
            fc.nextStep(MasterTimer::tick());
1,722,401✔
237

238
        quint32 value = fc.current();
1,722,401✔
239

240
        // Apply intensity to channels that can fade
241
        if (fc.canFade())
1,722,401✔
242
        {
243
            if ((flags & FadeChannel::CrossFade) && fc.fadeTime() == 0)
1,722,401✔
244
            {
245
                // morph start <-> target depending on intensities
246
                bool rampUp = fc.target() > fc.start() ? true : false;
20✔
247
                value = rampUp ? fc.target() - fc.start() : fc.start() - fc.target();
20✔
248
                value = qreal(value) * intensity();
20✔
249
                value = qreal(rampUp ? fc.start() + value : fc.start() - value) * parentIntensity();
20✔
250
            }
251
            else if (flags & FadeChannel::Intensity)
1,722,381✔
252
            {
253
                value = fc.current(compIntensity);
126✔
254
            }
255
        }
256

257
        //qDebug() << "[GenericFader] >>> uni:" << universe->id() << ", address:" << address << ", value:" << value << "int:" << compIntensity;
258
        if (flags & FadeChannel::Override)
1,722,401✔
259
        {
260
            universe->write(address, value, true);
×
261
            continue;
×
262
        }
263
        else if (flags & FadeChannel::Relative)
1,722,401✔
264
        {
265
            universe->writeRelative(address, value, channelCount);
×
266
        }
267
        else if (flags & FadeChannel::Flashing)
1,722,401✔
268
        {
269
            for (int i = 0; i < channelCount; i++)
16✔
270
                universe->write(address + i, ((uchar *)&value)[channelCount - 1 - i],
8✔
271
                                flags & FadeChannel::ForceLTP ? true : false);
8✔
272
            continue;
8✔
273
        }
8✔
274
        else
275
        {
276
            // treat value as a whole, so do this just once per FadeChannel
277
            universe->writeBlended(address, value, channelCount, m_blendMode);
1,722,393✔
278
        }
279

280
        if (((flags & FadeChannel::Intensity) &&
1,722,393✔
281
            (flags & FadeChannel::HTP) &&
118✔
282
            m_blendMode == Universe::NormalBlend) || m_fadeOut)
1,722,393✔
283
        {
284
            // Remove all channels that reach their target _zero_ value.
285
            // They have no effect either way so removing them saves a bit of CPU.
286
            if (fc.current() == 0 && fc.target() == 0 && fc.isReady())
122✔
287
                it.remove();
3✔
288
        }
289

290
        if (flags & FadeChannel::AutoRemove && value == fc.target())
1,722,393✔
291
            it.remove();
2✔
292
    }
293

294
    // self-request deletion when fadeout is complete
295
    if (m_fadeOut && m_channels.isEmpty())
927,198✔
296
    {
297
        m_fadeOut = false;
2✔
298
        requestDelete();
2✔
299
    }
300
}
927,198✔
301

302
qreal GenericFader::intensity() const
927,219✔
303
{
304
    return m_intensity;
927,219✔
305
}
306

307
void GenericFader::adjustIntensity(qreal fraction)
130✔
308
{
309
    //qDebug() << name() << "I FADER intensity" << fraction << ", PARENT:" << m_parentIntensity;
310
    m_intensity = fraction;
130✔
311
}
130✔
312

313
qreal GenericFader::parentIntensity() const
927,218✔
314
{
315
    return m_parentIntensity;
927,218✔
316
}
317

318
void GenericFader::setParentIntensity(qreal fraction)
125✔
319
{
320
    //qDebug() << name() << "P FADER intensity" << m_intensity << ", PARENT:" << fraction;
321
    m_parentIntensity = fraction;
125✔
322
}
125✔
323

324
bool GenericFader::isPaused() const
×
325
{
326
    return m_paused;
×
327
}
328

329
void GenericFader::setPaused(bool paused)
5✔
330
{
331
    m_paused = paused;
5✔
332
}
5✔
333

334
bool GenericFader::isEnabled() const
927,097✔
335
{
336
    return m_enabled;
927,097✔
337
}
338

339
void GenericFader::setEnabled(bool enable)
×
340
{
341
    m_enabled = enable;
×
342
}
×
343

344
bool GenericFader::isFadingOut() const
4✔
345
{
346
    return m_fadeOut;
4✔
347
}
348

349
void GenericFader::setFadeOut(bool enable, uint fadeTime)
6✔
350
{
351
    m_fadeOut = enable;
6✔
352

353
    if (fadeTime == 0)
6✔
354
        return;
×
355

356
    QReadLocker l(&m_channelsLock);
6✔
357
    QMutableHashIterator <quint32,FadeChannel> it(m_channels);
6✔
358
    while (it.hasNext() == true)
24✔
359
    {
360
        FadeChannel& fc(it.next().value());
18✔
361

362
        fc.setStart(fc.current());
18✔
363
        // if not HTP and/or flashing, request channels
364
        // to target the current universe value
365
        // (will be handled in the write method)
366
        if (((fc.flags() & FadeChannel::Flashing) == 0) &&
36✔
367
            ((fc.flags() & FadeChannel::Intensity) == 0))
18✔
368
            fc.addFlag(FadeChannel::SetTarget);
6✔
369
        fc.setTarget(0);
18✔
370
        fc.setElapsed(0);
18✔
371
        fc.setReady(false);
18✔
372
        fc.setFadeTime(fc.canFade() ? fadeTime : 0);
18✔
373
        // if flashing, remove the flag and treat
374
        // it like a regular fade out to target
375
        fc.removeFlag(FadeChannel::Flashing);
18✔
376
    }
377
}
6✔
378

379
void GenericFader::setBlendMode(Universe::BlendMode mode)
123✔
380
{
381
    m_blendMode = mode;
123✔
382
}
123✔
383

384
void GenericFader::setMonitoring(bool enable)
×
385
{
386
    m_monitoring = enable;
×
387
}
×
388

389
void GenericFader::resetCrossfade()
×
390
{
391
    qDebug() << name() << "resetting crossfade channels";
×
392
    QReadLocker l(&m_channelsLock);
×
393
    QMutableHashIterator <quint32,FadeChannel> it(m_channels);
×
394
    while (it.hasNext() == true)
×
395
    {
396
        FadeChannel& fc(it.next().value());
×
397
        fc.removeFlag(FadeChannel::CrossFade);
×
398
    }
399
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc