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

mcallegari / qlcplus / 14492056933

16 Apr 2025 11:53AM UTC coverage: 31.851% (-0.002%) from 31.853%
14492056933

push

github

web-flow
Merge pull request #1725 from Jurrie/bugfix/Correctly_name_SonicPulse_LED_Bar_10

Correctly name 'SonicPulse LED Bar 10'

14673 of 46068 relevant lines covered (31.85%)

26455.74 hits per line

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

95.33
/engine/src/cuestack.cpp
1
/*
2
  Q Light Controller Plus
3
  cuestack.cpp
4

5
  Copyright (c) Heikki Junnila
6
                Massimo Callegari
7

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

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

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

21
#include <QXmlStreamReader>
22
#include <QXmlStreamWriter>
23
#include <qmath.h>
24
#include <QDebug>
25

26
#include "genericfader.h"
27
#include "fadechannel.h"
28
#include "mastertimer.h"
29
#include "qlcmacros.h"
30
#include "cuestack.h"
31
#include "universe.h"
32
#include "cue.h"
33
#include "doc.h"
34

35
/****************************************************************************
36
 * Initialization
37
 ****************************************************************************/
38

39
CueStack::CueStack(Doc* doc)
21✔
40
    : QObject(doc)
41
    , m_fadeInSpeed(0)
21✔
42
    , m_fadeOutSpeed(0)
21✔
43
    , m_duration(UINT_MAX)
21✔
44
    , m_running(false)
21✔
45
    , m_intensity(1.0)
21✔
46
    , m_currentIndex(-1)
21✔
47
    , m_flashing(false)
21✔
48
    , m_elapsed(0)
21✔
49
    , m_previous(false)
21✔
50
    , m_next(false)
21✔
51
{
52
    //qDebug() << Q_FUNC_INFO << (void*) this;
53
    Q_ASSERT(doc != NULL);
54
}
21✔
55

56
CueStack::~CueStack()
21✔
57
{
58
    //qDebug() << Q_FUNC_INFO << (void*) this;
59
    Q_ASSERT(isStarted() == false);
60
    Q_ASSERT(isFlashing() == false);
61
    m_cues.clear(); // Crashes without this, WTF?!
21✔
62
}
21✔
63

64
Doc* CueStack::doc() const
31✔
65
{
66
    return qobject_cast<Doc*> (parent());
31✔
67
}
68

69
/****************************************************************************
70
 * Name
71
 ****************************************************************************/
72

73
void CueStack::setName(const QString& name, int index)
1✔
74
{
75
    if (index < 0)
1✔
76
        m_name = name;
1✔
77
    else
78
        m_cues[index].setName(name);
×
79
    emit changed(index);
1✔
80
}
1✔
81

82
QString CueStack::name(int index) const
2✔
83
{
84
    if (index < 0)
2✔
85
        return m_name;
86
    else
87
        return m_cues[index].name();
×
88
}
89

90
/****************************************************************************
91
 * Speed
92
 ****************************************************************************/
93

94
void CueStack::setFadeInSpeed(uint ms, int index)
5✔
95
{
96
    if (index < 0)
5✔
97
        m_fadeInSpeed = ms;
5✔
98
    else
99
        m_cues[index].setFadeInSpeed(ms);
×
100
    emit changed(index);
5✔
101
}
5✔
102

103
uint CueStack::fadeInSpeed(int index) const
3✔
104
{
105
    if (index < 0)
3✔
106
        return m_fadeInSpeed;
3✔
107
    else
108
        return m_cues[index].fadeInSpeed();
×
109
}
110

111
void CueStack::setFadeOutSpeed(uint ms, int index)
5✔
112
{
113
    if (index < 0)
5✔
114
        m_fadeOutSpeed = ms;
5✔
115
    else
116
        m_cues[index].setFadeOutSpeed(ms);
×
117
    emit changed(index);
5✔
118
}
5✔
119

120
uint CueStack::fadeOutSpeed(int index) const
10✔
121
{
122
    if (index < 0)
10✔
123
        return m_fadeOutSpeed;
10✔
124
    else
125
        return m_cues[index].fadeOutSpeed();
×
126
}
127

128
void CueStack::setDuration(uint ms, int index)
5✔
129
{
130
    if (index < 0)
5✔
131
        m_duration = ms;
5✔
132
    else
133
        m_cues[index].setDuration(ms);
×
134
    emit changed(index);
5✔
135
}
5✔
136

137
uint CueStack::duration(int index) const
3✔
138
{
139
    if (index < 0)
3✔
140
        return m_duration;
3✔
141
    else
142
        return m_cues[index].duration();
×
143
}
144

145
/****************************************************************************
146
 * Cues
147
 ****************************************************************************/
148

149
void CueStack::appendCue(const Cue& cue)
46✔
150
{
151
    qDebug() << Q_FUNC_INFO;
152

153
    int index = 0;
154
    {
155
        QMutexLocker locker(&m_mutex);
46✔
156
        m_cues.append(cue);
46✔
157
        index = m_cues.size() - 1;
46✔
158
    }
159

160
    emit added(index);
46✔
161
}
46✔
162

163
void CueStack::insertCue(int index, const Cue& cue)
6✔
164
{
165
    qDebug() << Q_FUNC_INFO;
166

167
    bool cueAdded = false;
168

169
    {
170
        QMutexLocker locker(&m_mutex);
6✔
171

172
        if (index >= 0 && index < m_cues.size())
6✔
173
        {
174
            m_cues.insert(index, cue);
2✔
175
            cueAdded = true;
176
            emit added(index);
2✔
177

178
            if (m_currentIndex >= index)
2✔
179
            {
180
                m_currentIndex++;
1✔
181
                emit currentCueChanged(m_currentIndex);
1✔
182
            }
183
        }
184
    }
185

186
    if (!cueAdded)
6✔
187
        appendCue(cue);
4✔
188
}
6✔
189

190
void CueStack::replaceCue(int index, const Cue& cue)
4✔
191
{
192
    qDebug() << Q_FUNC_INFO;
193

194
    bool cueChanged = false;
195
    {
196
        QMutexLocker locker(&m_mutex);
4✔
197

198
        if (index >= 0 && index < m_cues.size())
4✔
199
        {
200
           m_cues[index] = cue;
2✔
201
           cueChanged = true;
202
        }
203
    }
204

205
    if (cueChanged)
4✔
206
    {
207
        emit changed(index);
2✔
208
    }
209
    else
210
    {
211
        appendCue(cue);
2✔
212
    }
213
}
4✔
214

215
void CueStack::removeCue(int index)
9✔
216
{
217
    qDebug() << Q_FUNC_INFO;
218

219
    QMutexLocker locker(&m_mutex);
9✔
220
    if (index >= 0 && index < m_cues.size())
9✔
221
    {
222
        m_cues.removeAt(index);
5✔
223
        emit removed(index);
5✔
224

225
        if (index < m_currentIndex)
5✔
226
        {
227
            m_currentIndex--;
2✔
228
            emit currentCueChanged(m_currentIndex);
2✔
229
        }
230
    }
231
}
9✔
232

233
void CueStack::removeCues(const QList <int>& indexes)
6✔
234
{
235
    qDebug() << Q_FUNC_INFO;
236

237
    // Sort the list so that the items can be removed in reverse order.
238
    // This way, the indices are always correct.
239
    QList <int> indexList = indexes;
6✔
240
    std::sort(indexList.begin(), indexList.end());
6✔
241

242
    QListIterator <int> it(indexList);
6✔
243
    it.toBack();
244

245
    QMutexLocker locker(&m_mutex);
6✔
246

247
    while (it.hasPrevious() == true)
15✔
248
    {
249
        int index(it.previous());
9✔
250
        if (index >= 0 && index < m_cues.size())
9✔
251
        {
252
            m_cues.removeAt(index);
5✔
253
            emit removed(index);
5✔
254

255
            if (index < m_currentIndex)
5✔
256
            {
257
                m_currentIndex--;
4✔
258
                emit currentCueChanged(m_currentIndex);
4✔
259
            }
260
        }
261
    }
262
}
6✔
263

264
QList <Cue> CueStack::cues() const
117✔
265
{
266
    return m_cues;
117✔
267
}
268

269
void CueStack::setCurrentIndex(int index)
14✔
270
{
271
    qDebug() << Q_FUNC_INFO;
272

273
    QMutexLocker locker(&m_mutex);
14✔
274
    m_currentIndex = CLAMP(index, -1, m_cues.size() - 1);
14✔
275
}
14✔
276

277
int CueStack::currentIndex() const
37✔
278
{
279
    return m_currentIndex;
37✔
280
}
281

282
void CueStack::previousCue()
3✔
283
{
284
    qDebug() << Q_FUNC_INFO;
285
    m_previous = true;
3✔
286
    if (isRunning() == false)
3✔
287
        start();
1✔
288
}
3✔
289

290
void CueStack::nextCue()
3✔
291
{
292
    qDebug() << Q_FUNC_INFO;
293
    m_next = true;
3✔
294
    if (isRunning() == false)
3✔
295
        start();
1✔
296
}
3✔
297

298
/****************************************************************************
299
 * Save & Load
300
 ****************************************************************************/
301

302
uint CueStack::loadXMLID(QXmlStreamReader &root)
3✔
303
{
304
    qDebug() << Q_FUNC_INFO;
305

306
    if (root.name() != KXMLQLCCueStack)
6✔
307
    {
308
        qWarning() << Q_FUNC_INFO << "CueStack node not found";
×
309
        return UINT_MAX;
×
310
    }
311

312
    QXmlStreamAttributes attrs = root.attributes();
3✔
313

314
    if (attrs.hasAttribute(KXMLQLCCueStackID) == true)
3✔
315
        return attrs.value(KXMLQLCCueStackID).toString().toUInt();
2✔
316
    else
317
        return UINT_MAX;
318
}
319

320
bool CueStack::loadXML(QXmlStreamReader &root)
3✔
321
{
322
    qDebug() << Q_FUNC_INFO;
323

324
    m_cues.clear();
3✔
325

326
    if (root.name() != KXMLQLCCueStack)
6✔
327
    {
328
        qWarning() << Q_FUNC_INFO << "CueStack node not found";
×
329
        return false;
×
330
    }
331

332
    while (root.readNextStartElement())
8✔
333
    {
334
        if (root.name() == KXMLQLCCue)
10✔
335
        {
336
            Cue cue;
3✔
337
            if (cue.loadXML(root) == true)
3✔
338
                appendCue(cue);
3✔
339
        }
3✔
340
        else if (root.name() == KXMLQLCCueStackSpeed)
4✔
341
        {
342
            setFadeInSpeed(root.attributes().value(KXMLQLCCueStackSpeedFadeIn).toString().toUInt());
1✔
343
            setFadeOutSpeed(root.attributes().value(KXMLQLCCueStackSpeedFadeOut).toString().toUInt());
1✔
344
            setDuration(root.attributes().value(KXMLQLCCueStackSpeedDuration).toString().toUInt());
1✔
345
            root.skipCurrentElement();
1✔
346
        }
347
        else
348
        {
349
            qWarning() << Q_FUNC_INFO << "Unrecognized CueStack tag:" << root.name();
1✔
350
            root.skipCurrentElement();
1✔
351
        }
352
    }
353

354
    return true;
355
}
356

357
bool CueStack::saveXML(QXmlStreamWriter *doc, uint id) const
1✔
358
{
359
    qDebug() << Q_FUNC_INFO;
360
    Q_ASSERT(doc != NULL);
361

362
    doc->writeStartElement(KXMLQLCCueStack);
1✔
363
    doc->writeAttribute(KXMLQLCCueStackID, QString::number(id));
1✔
364

365
    doc->writeStartElement(KXMLQLCCueStackSpeed);
1✔
366
    doc->writeAttribute(KXMLQLCCueStackSpeedFadeIn, QString::number(fadeInSpeed()));
1✔
367
    doc->writeAttribute(KXMLQLCCueStackSpeedFadeOut, QString::number(fadeOutSpeed()));
1✔
368
    doc->writeAttribute(KXMLQLCCueStackSpeedDuration, QString::number(duration()));
1✔
369
    doc->writeEndElement();
1✔
370

371
    foreach (Cue cue, cues())
5✔
372
        cue.saveXML(doc);
3✔
373

374
    /* End the <CueStack> tag */
375
    doc->writeEndElement();
1✔
376

377
    return true;
1✔
378
}
379

380
/****************************************************************************
381
 * Running
382
 ****************************************************************************/
383

384
void CueStack::start()
5✔
385
{
386
    qDebug() << Q_FUNC_INFO;
387
    m_running = true;
5✔
388
}
5✔
389

390
void CueStack::stop()
1✔
391
{
392
    qDebug() << Q_FUNC_INFO;
393
    m_running = false;
1✔
394
}
1✔
395

396
bool CueStack::isRunning() const
19✔
397
{
398
    return m_running;
19✔
399
}
400

401
void CueStack::adjustIntensity(qreal fraction)
1✔
402
{
403
    m_intensity = fraction;
1✔
404

405
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
2✔
406
    {
407
        if (!fader.isNull())
×
408
            fader->adjustIntensity(fraction);
×
409
    }
410
}
1✔
411

412
qreal CueStack::intensity() const
6✔
413
{
414
    return m_intensity;
6✔
415
}
416

417
/****************************************************************************
418
 * Flashing
419
 ****************************************************************************/
420

421
void CueStack::setFlashing(bool enable)
5✔
422
{
423
    qDebug() << Q_FUNC_INFO;
424
    if (m_flashing == enable || m_cues.isEmpty())
5✔
425
        return;
426

427
    m_flashing = enable;
2✔
428
    if (m_flashing == true)
2✔
429
        doc()->masterTimer()->registerDMXSource(this);
1✔
430
}
431

432
bool CueStack::isFlashing() const
9✔
433
{
434
    return m_flashing;
9✔
435
}
436

437
void CueStack::writeDMX(MasterTimer *timer, QList<Universe*> ua)
2✔
438
{
439
    Q_UNUSED(timer);
440
    if (m_cues.isEmpty())
2✔
441
        return;
442

443
    if (isFlashing())
2✔
444
    {
445
        if (m_fadersMap.isEmpty())
1✔
446
        {
447
            QMapIterator <uint,uchar> it(m_cues.first().values());
1✔
448
            while (it.hasNext() == true)
3✔
449
            {
450
                it.next();
2✔
451
                FadeChannel fc(doc(), Fixture::invalidId(), it.key());
2✔
452
                quint32 universe = fc.universe();
2✔
453
                if (universe == Universe::invalid())
2✔
454
                    continue;
455

456
                QSharedPointer<GenericFader> fader = m_fadersMap.value(universe, QSharedPointer<GenericFader>());
4✔
457
                if (fader.isNull())
2✔
458
                {
459
                    fader = ua[universe]->requestFader();
1✔
460
                    m_fadersMap[universe] = fader;
1✔
461
                }
462

463
                fc.setTarget(it.value());
2✔
464
                fc.addFlag(FadeChannel::Flashing);
2✔
465
                fader->add(fc);
2✔
466
            }
2✔
467
        }
468
    }
469
    else
470
    {
471
        QMapIterator <quint32, QSharedPointer<GenericFader> > it(m_fadersMap);
1✔
472
        while (it.hasNext() == true)
2✔
473
        {
474
            it.next();
1✔
475
            quint32 universe = it.key();
1✔
476
            QSharedPointer<GenericFader> fader = it.value();
477
            if (!fader.isNull())
1✔
478
                ua[universe]->dismissFader(fader);
3✔
479
        }
480

481
        m_fadersMap.clear();
1✔
482
        doc()->masterTimer()->unregisterDMXSource(this);
1✔
483
    }
484
}
485

486
/****************************************************************************
487
 * Writing
488
 ****************************************************************************/
489

490
bool CueStack::isStarted() const
6✔
491
{
492
    return m_fadersMap.isEmpty() ? false : true;
6✔
493
}
494

495
void CueStack::preRun()
5✔
496
{
497
    qDebug() << Q_FUNC_INFO;
498

499
    m_elapsed = 0;
5✔
500
    emit started();
5✔
501
}
5✔
502

503
void CueStack::write(QList<Universe*> ua)
4✔
504
{
505
    if (m_cues.size() == 0 || isRunning() == false)
4✔
506
        return;
1✔
507

508
    if (m_previous == true)
3✔
509
    {
510
        // previousCue() was requested by user
511
        m_elapsed = 0;
1✔
512
        int from = m_currentIndex;
1✔
513
        int to = previous();
1✔
514
        switchCue(from, to, ua);
1✔
515
        m_previous = false;
1✔
516
        emit currentCueChanged(m_currentIndex);
1✔
517
    }
518
    else if (m_next == true)
2✔
519
    {
520
        // nextCue() was requested by user
521
        m_elapsed = 0;
1✔
522
        int from = m_currentIndex;
1✔
523
        int to = next();
1✔
524
        switchCue(from, to, ua);
1✔
525
        m_next = false;
1✔
526
        emit currentCueChanged(m_currentIndex);
1✔
527
    }
528
/*
529
    else if (m_elapsed >= duration())
530
    {
531
        // Duration expired
532
        m_elapsed = 0;
533
        switchCue(next(), ua);
534
        emit currentCueChanged(m_currentIndex);
535
    }
536
*/
537
    //m_fader->write(ua);
538

539
    m_elapsed += MasterTimer::tick();
3✔
540
}
541

542
void CueStack::postRun(MasterTimer* timer, QList<Universe *> ua)
5✔
543
{
544
    qDebug() << Q_FUNC_INFO;
545

546
    Q_UNUSED(timer);
547

548
    /* If no fade out is needed, dismiss all the requested faders.
549
     * Otherwise, set all the faders to fade out and let Universe dismiss them
550
     * when done */
551
    if (fadeOutSpeed() == 0)
5✔
552
    {
553
        QMapIterator <quint32, QSharedPointer<GenericFader> > it(m_fadersMap);
3✔
554
        while (it.hasNext() == true)
4✔
555
        {
556
            it.next();
1✔
557
            quint32 universe = it.key();
1✔
558
            QSharedPointer<GenericFader> fader = it.value();
559
            if (!fader.isNull())
1✔
560
                ua[universe]->dismissFader(fader);
3✔
561
        }
562
    }
563
    else
564
    {
565
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
6✔
566
        {
567
            if (!fader.isNull())
2✔
568
                fader->setFadeOut(true, fadeOutSpeed());
2✔
569
        }
570
    }
571

572
    m_fadersMap.clear();
5✔
573

574
    m_currentIndex = -1;
5✔
575

576
    emit currentCueChanged(m_currentIndex);
5✔
577
    emit stopped();
5✔
578
}
5✔
579

580
int CueStack::previous()
9✔
581
{
582
    qDebug() << Q_FUNC_INFO;
583

584
    if (m_cues.size() == 0)
9✔
585
        return -1;
586

587
    QMutexLocker locker(&m_mutex);
8✔
588

589
    m_currentIndex--;
8✔
590
    if (m_currentIndex < 0)
8✔
591
        m_currentIndex = m_cues.size() - 1;
5✔
592

593
    return m_currentIndex;
8✔
594
}
595

596
FadeChannel *CueStack::getFader(QList<Universe *> universes, quint32 universeID, quint32 fixtureID, quint32 channel)
26✔
597
{
598
    // get the universe Fader first. If doesn't exist, create it
599
    QSharedPointer<GenericFader> fader = m_fadersMap.value(universeID, QSharedPointer<GenericFader>());
52✔
600
    if (fader.isNull())
26✔
601
    {
602
        fader = universes[universeID]->requestFader();
6✔
603
        fader->adjustIntensity(intensity());
3✔
604
        m_fadersMap[universeID] = fader;
3✔
605
    }
606

607
    return fader->getChannelFader(doc(), universes[universeID], fixtureID, channel);
52✔
608
}
609

610
void CueStack::updateFaderValues(FadeChannel *fc, uchar value, uint fadeTime)
23✔
611
{
612
    fc->setStart(fc->current());
23✔
613
    fc->setTarget(value);
23✔
614
    fc->setElapsed(0);
23✔
615
    fc->setReady(false);
23✔
616
    fc->setFadeTime(fadeTime);
23✔
617
}
23✔
618

619
int CueStack::next()
9✔
620
{
621
    qDebug() << Q_FUNC_INFO;
622

623
    if (m_cues.size() == 0)
9✔
624
        return -1;
625

626
    QMutexLocker locker(&m_mutex);
8✔
627
    m_currentIndex++;
8✔
628
    if (m_currentIndex >= m_cues.size())
8✔
629
        m_currentIndex = 0;
4✔
630

631
    return m_currentIndex;
8✔
632
}
633

634
void CueStack::switchCue(int from, int to, const QList<Universe *> ua)
8✔
635
{
636
    qDebug() << Q_FUNC_INFO;
637

638
    Cue newCue;
16✔
639
    Cue oldCue;
8✔
640

641
    {
642
        QMutexLocker locker(&m_mutex);
8✔
643

644
        if (to >= 0 && to < m_cues.size())
8✔
645
            newCue = m_cues[to];
5✔
646
        if (from >= 0 && from < m_cues.size())
8✔
647
            oldCue = m_cues[from];
3✔
648
    }
649

650
    // Fade out the HTP channels of the previous cue
651
    QMapIterator <uint,uchar> oldit(oldCue.values());
8✔
652
    while (oldit.hasNext() == true)
26✔
653
    {
654
        oldit.next();
10✔
655
        uint absChannel = oldit.key();
10✔
656
        quint32 universe = (absChannel >> 9);
10✔
657
        FadeChannel *fc = getFader(ua, universe, Fixture::invalidId(), absChannel);
10✔
658

659
        if (fc->flags() & FadeChannel::Intensity)
10✔
660
            updateFaderValues(fc, 0, oldCue.fadeOutSpeed());
7✔
661
    }
662

663
    // Fade in all channels of the new cue
664
    QMapIterator <uint,uchar> newit(newCue.values());
8✔
665
    while (newit.hasNext() == true)
24✔
666
    {
667
        newit.next();
16✔
668
        uint absChannel = newit.key();
16✔
669
        quint32 universe = (absChannel >> 9);
16✔
670
        FadeChannel *fc = getFader(ua, universe, Fixture::invalidId(), absChannel);
16✔
671
        updateFaderValues(fc, newit.value(), newCue.fadeInSpeed());
16✔
672
    }
673
}
8✔
674

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