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

mcallegari / qlcplus / 13633248611

03 Mar 2025 02:31PM UTC coverage: 31.871% (+0.4%) from 31.5%
13633248611

push

github

web-flow
actions: add chrpath to profile

14689 of 46089 relevant lines covered (31.87%)

26426.11 hits per line

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

83.31
/engine/src/function.cpp
1
/*
2
  Q Light Controller Plus
3
  function.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 <QElapsedTimer>
24
#include <QString>
25
#include <QDebug>
26
#include <math.h>
27

28
#include "qlcmacros.h"
29
#include "qlcfile.h"
30

31
#include "scriptwrapper.h"
32
#include "mastertimer.h"
33
#include "collection.h"
34
#include "rgbmatrix.h"
35
#include "function.h"
36
#include "sequence.h"
37
#include "chaser.h"
38
#include "audio.h"
39
#include "video.h"
40
#include "scene.h"
41
#include "show.h"
42
#include "efx.h"
43
#include "doc.h"
44

45
#define OVERRIDE_ATTRIBUTE_START_ID     128
46

47
const QString KSceneString      (      "Scene" );
48
const QString KChaserString     (     "Chaser" );
49
const QString KEFXString        (        "EFX" );
50
const QString KCollectionString ( "Collection" );
51
const QString KScriptString     (     "Script" );
52
const QString KRGBMatrixString  (  "RGBMatrix" );
53
const QString KShowString       (       "Show" );
54
const QString KSequenceString   (   "Sequence" );
55
const QString KAudioString      (      "Audio" );
56
const QString KVideoString      (      "Video" );
57
const QString KUndefinedString  (  "Undefined" );
58

59
const QString KLoopString       (       "Loop" );
60
const QString KPingPongString   (   "PingPong" );
61
const QString KSingleShotString ( "SingleShot" );
62
const QString KRandomString     (     "Random" );
63

64
const QString KBackwardString   (   "Backward" );
65
const QString KForwardString    (    "Forward" );
66

67
const QString KTimeTypeString   (       "Time" );
68
const QString KBeatsTypeString  (      "Beats" );
69

70
/*****************************************************************************
71
 * Initialization
72
 *****************************************************************************/
73

74
Function::Function(QObject *parent)
×
75
    : QObject(parent)
76
    , m_id(Function::invalidId())
×
77
    , m_type(Undefined)
×
78
    , m_path(QString())
×
79
    , m_visible(true)
×
80
    , m_runOrder(Loop)
×
81
    , m_direction(Forward)
×
82
    , m_tempoType(Time)
×
83
    , m_overrideTempoType(Original)
×
84
    , m_beatResyncNeeded(false)
×
85
    , m_fadeInSpeed(0)
×
86
    , m_fadeOutSpeed(0)
×
87
    , m_duration(0)
×
88
    , m_overrideFadeInSpeed(defaultSpeed())
×
89
    , m_overrideFadeOutSpeed(defaultSpeed())
×
90
    , m_overrideDuration(defaultSpeed())
×
91
    , m_flashing(false)
×
92
    , m_elapsed(0)
×
93
    , m_elapsedBeats(0)
×
94
    , m_stop(true)
×
95
    , m_running(false)
×
96
    , m_paused(false)
×
97
    , m_lastOverrideAttributeId(OVERRIDE_ATTRIBUTE_START_ID)
×
98
    , m_preserveAttributes(false)
×
99
    , m_blendMode(Universe::NormalBlend)
×
100
{
101

102
}
×
103

104
Function::Function(Doc* doc, Type t)
443✔
105
    : QObject(doc)
106
    , m_id(Function::invalidId())
886✔
107
    , m_type(t)
443✔
108
    , m_path(QString())
443✔
109
    , m_visible(true)
443✔
110
    , m_runOrder(Loop)
443✔
111
    , m_direction(Forward)
443✔
112
    , m_tempoType(Time)
443✔
113
    , m_overrideTempoType(Original)
443✔
114
    , m_beatResyncNeeded(false)
443✔
115
    , m_fadeInSpeed(0)
443✔
116
    , m_fadeOutSpeed(0)
443✔
117
    , m_duration(0)
443✔
118
    , m_overrideFadeInSpeed(defaultSpeed())
443✔
119
    , m_overrideFadeOutSpeed(defaultSpeed())
443✔
120
    , m_overrideDuration(defaultSpeed())
443✔
121
    , m_flashing(false)
443✔
122
    , m_elapsed(0)
443✔
123
    , m_elapsedBeats(0)
443✔
124
    , m_stop(true)
443✔
125
    , m_running(false)
443✔
126
    , m_paused(false)
443✔
127
    , m_lastOverrideAttributeId(OVERRIDE_ATTRIBUTE_START_ID)
443✔
128
    , m_preserveAttributes(false)
443✔
129
    , m_blendMode(Universe::NormalBlend)
886✔
130
{
131
    Q_ASSERT(doc != NULL);
132
    registerAttribute(tr("Intensity"), Multiply | Single);
443✔
133
}
443✔
134

135
Function::~Function()
443✔
136
{
137
}
443✔
138

139
Doc* Function::doc() const
2,079✔
140
{
141
    return qobject_cast<Doc*>(parent());
2,079✔
142
}
143

144
/*****************************************************************************
145
 * Copying
146
 *****************************************************************************/
147
Function *Function::createCopy(Doc *doc, bool addToDoc)
×
148
{
149
    Q_ASSERT(doc != NULL);
150

151
    Function* copy = new Function(doc, type());
×
152
    if (copy->copyFrom(this) == false)
×
153
    {
154
        delete copy;
×
155
        copy = NULL;
156
    }
157
    if (addToDoc == true && doc->addFunction(copy) == false)
×
158
    {
159
        delete copy;
×
160
        copy = NULL;
161
    }
162

163
    return copy;
×
164
}
165

166
bool Function::copyFrom(const Function* function)
18✔
167
{
168
    if (function == NULL)
18✔
169
        return false;
170

171
    m_name = function->name();
17✔
172
    m_runOrder = function->runOrder();
17✔
173
    m_direction = function->direction();
17✔
174
    m_tempoType = function->tempoType();
17✔
175
    m_fadeInSpeed = function->fadeInSpeed();
17✔
176
    m_fadeOutSpeed = function->fadeOutSpeed();
17✔
177
    m_duration = function->duration();
17✔
178
    m_path = function->path(true);
17✔
179
    m_visible = function->isVisible();
17✔
180
    m_blendMode = function->blendMode();
17✔
181
    m_uiState = function->uiStateMap();
34✔
182

183
    emit changed(m_id);
17✔
184

185
    return true;
17✔
186
}
187

188
/*****************************************************************************
189
 * ID
190
 *****************************************************************************/
191

192
void Function::setID(quint32 id)
264✔
193
{
194
    /* Don't set doc modified status or emit changed signal, because this
195
       function is called only once during function creation. */
196
    m_id = id;
264✔
197
}
264✔
198

199
quint32 Function::id() const
3,374✔
200
{
201
    return m_id;
3,374✔
202
}
203

204
quint32 Function::invalidId()
2,663✔
205
{
206
    return UINT_MAX;
2,663✔
207
}
208

209
/*****************************************************************************
210
 * Name
211
 *****************************************************************************/
212

213
void Function::setName(const QString& name)
601✔
214
{
215
    if (m_name == name)
601✔
216
        return;
217

218
    m_name = QString(name);
601✔
219

220
    emit nameChanged(m_id);
601✔
221
}
222

223
QString Function::name() const
255✔
224
{
225
    return m_name;
255✔
226
}
227

228
/*****************************************************************************
229
 * Type
230
 *****************************************************************************/
231

232
Function::Type Function::type() const
668✔
233
{
234
    return m_type;
668✔
235
}
236

237
QString Function::typeString() const
3✔
238
{
239
    return Function::typeToString(type());
3✔
240
}
241

242
QString Function::typeToString(Type type)
65✔
243
{
244
    switch (type)
65✔
245
    {
246
        case SceneType:      return KSceneString;
247
        case ChaserType:     return KChaserString;
248
        case EFXType:        return KEFXString;
249
        case CollectionType: return KCollectionString;
250
        case ScriptType:     return KScriptString;
251
        case RGBMatrixType:  return KRGBMatrixString;
252
        case ShowType:       return KShowString;
253
        case SequenceType:   return KSequenceString;
254
        case AudioType:      return KAudioString;
255
        case VideoType:      return KVideoString;
256
        case Undefined:
257
        default:
258
            return KUndefinedString;
259
    }
260
}
261

262
Function::Type Function::stringToType(const QString& string)
23✔
263
{
264
    if (string == KSceneString)
23✔
265
        return SceneType;
266
    else if (string == KChaserString)
21✔
267
        return ChaserType;
268
    else if (string == KEFXString)
19✔
269
        return EFXType;
270
    else if (string == KCollectionString)
17✔
271
        return CollectionType;
272
    else if (string == KScriptString)
11✔
273
        return ScriptType;
274
    else if (string == KRGBMatrixString)
10✔
275
        return RGBMatrixType;
276
    else if (string == KShowString)
9✔
277
        return ShowType;
278
    else if (string == KSequenceString)
8✔
279
        return SequenceType;
280
    else if (string == KAudioString)
7✔
281
        return AudioType;
282
    else if (string == KVideoString)
6✔
283
        return VideoType;
284
    else
285
        return Undefined;
5✔
286
}
287

288
QIcon Function::getIcon() const
×
289
{
290
    return QIcon(":/function.png");
×
291
}
292

293
/*********************************************************************
294
 * Path
295
 *********************************************************************/
296
void Function::setPath(QString path)
8✔
297
{
298
    if (path.contains(typeToString(type())))
16✔
299
        path.remove(typeToString(type()) + "/");
×
300
    //qDebug() << "Function " << name() << "path set to:" << path;
301
    m_path = path;
8✔
302
}
8✔
303

304
QString Function::path(bool simplified) const
28✔
305
{
306
    if (simplified == true)
28✔
307
        return m_path;
308
    else
309
        return QString("%1/%2").arg(typeToString(type())).arg(m_path);
×
310
}
311

312
/*********************************************************************
313
 * Visibility
314
 *********************************************************************/
315

316
void Function::setVisible(bool visible)
9✔
317
{
318
    m_visible = visible;
9✔
319
}
9✔
320

321
bool Function::isVisible() const
34✔
322
{
323
    return m_visible;
34✔
324
}
325

326
/*********************************************************************
327
 * Common
328
 *********************************************************************/
329

330
bool Function::saveXMLCommon(QXmlStreamWriter *doc) const
11✔
331
{
332
    Q_ASSERT(doc != NULL);
333

334
    doc->writeAttribute(KXMLQLCFunctionID, QString::number(id()));
11✔
335
    doc->writeAttribute(KXMLQLCFunctionType, Function::typeToString(type()));
11✔
336
    doc->writeAttribute(KXMLQLCFunctionName, name());
11✔
337
    if (isVisible() == false)
11✔
338
        doc->writeAttribute(KXMLQLCFunctionHidden, KXMLQLCTrue);
×
339
    if (path(true).isEmpty() == false)
11✔
340
        doc->writeAttribute(KXMLQLCFunctionPath, path(true));
×
341
    if (blendMode() != Universe::NormalBlend)
11✔
342
        doc->writeAttribute(KXMLQLCFunctionBlendMode, Universe::blendModeToString(blendMode()));
×
343

344
    return true;
11✔
345
}
346

347
/*****************************************************************************
348
 * Running order
349
 *****************************************************************************/
350

351
void Function::setRunOrder(const Function::RunOrder& order)
56✔
352
{
353
    if (order == Loop || order == SingleShot || order == PingPong || order == Random)
56✔
354
        m_runOrder = order;
55✔
355
    else
356
        m_runOrder = Loop;
1✔
357
    emit runOrderChanged();
56✔
358
    emit changed(m_id);
56✔
359
}
56✔
360

361
Function::RunOrder Function::runOrder() const
485✔
362
{
363
    return m_runOrder;
485✔
364
}
365

366
QString Function::runOrderToString(const RunOrder& order)
15✔
367
{
368
    switch (order)
15✔
369
    {
370
    default:
371
    case Loop:
372
        return KLoopString;
373
    case PingPong:
374
        return KPingPongString;
375
    case SingleShot:
376
        return KSingleShotString;
377
    case Random:
378
        return KRandomString;
379
    }
380
}
381

382
Function::RunOrder Function::stringToRunOrder(const QString& str)
19✔
383
{
384
    if (str == KPingPongString)
19✔
385
        return PingPong;
386
    else if (str == KSingleShotString)
16✔
387
        return SingleShot;
388
    else if (str == KRandomString)
8✔
389
        return Random;
390
    else
391
        return Loop;
7✔
392
}
393

394
bool Function::saveXMLRunOrder(QXmlStreamWriter *doc) const
9✔
395
{
396
    Q_ASSERT(doc != NULL);
397

398
    doc->writeTextElement(KXMLQLCFunctionRunOrder, runOrderToString(runOrder()));
9✔
399

400
    return true;
9✔
401
}
402

403
bool Function::loadXMLRunOrder(QXmlStreamReader &root)
13✔
404
{
405
    if (root.name() != KXMLQLCFunctionRunOrder)
26✔
406
    {
407
        qWarning() << Q_FUNC_INFO << "RunOrder node not found";
×
408
        return false;
×
409
    }
410
    QString str = root.readElementText();
13✔
411
    if (str.isEmpty())
13✔
412
        return false;
413

414
    setRunOrder(stringToRunOrder(str));
13✔
415

416
    return true;
13✔
417
}
13✔
418

419
/*****************************************************************************
420
 * Direction
421
 *****************************************************************************/
422

423
void Function::setDirection(const Function::Direction& dir)
60✔
424
{
425
    if (dir == Forward || dir == Backward)
60✔
426
        m_direction = dir;
59✔
427
    else
428
        m_direction = Forward;
1✔
429
    emit changed(m_id);
60✔
430
}
60✔
431

432
Function::Direction Function::direction() const
4,736✔
433
{
434
    return m_direction;
4,736✔
435
}
436

437
QString Function::directionToString(const Direction& dir)
16✔
438
{
439
    switch (dir)
16✔
440
    {
441
        default:
442
        case Forward:
443
            return KForwardString;
444
        case Backward:
445
            return KBackwardString;
446
    }
447
}
448

449
Function::Direction Function::stringToDirection(const QString& str)
28✔
450
{
451
    if (str == KBackwardString)
28✔
452
        return Backward;
453
    else
454
        return Forward;
12✔
455
}
456

457
bool Function::saveXMLDirection(QXmlStreamWriter *doc) const
8✔
458
{
459
    Q_ASSERT(doc != NULL);
460

461
    doc->writeTextElement(KXMLQLCFunctionDirection, directionToString(direction()));
8✔
462

463
    return true;
8✔
464
}
465

466
bool Function::loadXMLDirection(QXmlStreamReader &root)
12✔
467
{
468
    if (root.name() != KXMLQLCFunctionDirection)
24✔
469
    {
470
        qWarning() << Q_FUNC_INFO << "Direction node not found";
×
471
        return false;
×
472
    }
473

474
    QString str = root.readElementText();
12✔
475
    if (str.isEmpty())
12✔
476
        return false;
477

478
    setDirection(stringToDirection(str));
12✔
479

480
    return true;
12✔
481
}
12✔
482

483
/*********************************************************************
484
 * Speed type
485
 *********************************************************************/
486

487
void Function::setTempoType(const Function::TempoType &type)
3✔
488
{
489
    if (type == m_tempoType)
3✔
490
        return;
491

492
    m_tempoType = type;
2✔
493

494
    /* Retrieve the current BPM value known by the Master Timer */
495
    float bpmNum = doc()->masterTimer()->bpmNumber();
2✔
496
    /* Calculate the duration in ms of a single beat */
497
    float beatTime = 60000.0 / bpmNum;
2✔
498

499
    switch (type)
2✔
500
    {
501
        /* Beats -> Time */
502
        case Time:
1✔
503
            setFadeInSpeed(beatsToTime(fadeInSpeed(), beatTime));
1✔
504
            setDuration(beatsToTime(duration(), beatTime));
1✔
505
            setFadeOutSpeed(beatsToTime(fadeOutSpeed(), beatTime));
1✔
506
            disconnect(doc()->masterTimer(), SIGNAL(bpmNumberChanged(int)),
1✔
507
                       this, SLOT(slotBPMChanged(int)));
508
        break;
1✔
509

510
        /* Time -> Beats */
511
        case Beats:
1✔
512
            setFadeInSpeed(timeToBeats(fadeInSpeed(), beatTime));
1✔
513
            setDuration(timeToBeats(duration(), beatTime));
1✔
514
            setFadeOutSpeed(timeToBeats(fadeOutSpeed(), beatTime));
1✔
515
            connect(doc()->masterTimer(), SIGNAL(bpmNumberChanged(int)),
1✔
516
                    this, SLOT(slotBPMChanged(int)));
517
        break;
1✔
518
        default:
519
            qDebug() << "Error. Unhandled tempo type" << type;
520
        break;
521
    }
522

523
    emit changed(m_id);
2✔
524
    emit tempoTypeChanged();
2✔
525
}
526

527
Function::TempoType Function::tempoType() const
1,463✔
528
{
529
    return m_tempoType;
1,463✔
530
}
531

532
QString Function::tempoTypeToString(const Function::TempoType &type)
2✔
533
{
534
    switch (type)
2✔
535
    {
536
        default:
537
        case Time:
538
            return KTimeTypeString;
539
        case Beats:
540
            return KBeatsTypeString;
541
    }
542
}
543

544
Function::TempoType Function::stringToTempoType(const QString &str)
2✔
545
{
546
    if (str == KTimeTypeString)
2✔
547
        return Time;
548
    else
549
        return Beats;
1✔
550
}
551

552
uint Function::timeToBeats(uint time, int beatDuration)
5✔
553
{
554
    if (time == 0 || time == infiniteSpeed())
5✔
555
        return time;
1✔
556

557
    uint value = 0;
558

559
    float beats = (float)time / (float)beatDuration;
4✔
560
    value = floor(beats) * 1000;
4✔
561

562
    beats -= floor(beats);
4✔
563
    beats = floor((beats * 1000) / 125) * 125;
4✔
564

565
    return value + beats;
4✔
566
}
567

568
uint Function::beatsToTime(uint beats, int beatDuration)
5✔
569
{
570
    if (beats == 0 || beats == infiniteSpeed())
5✔
571
        return beats;
1✔
572

573
    return ((float)beats / 1000.0) * beatDuration;
4✔
574
}
575

576
Function::TempoType Function::overrideTempoType() const
2✔
577
{
578
    return m_overrideTempoType;
2✔
579
}
580

581
void Function::setOverrideTempoType(Function::TempoType type)
1✔
582
{
583
    m_overrideTempoType = type;
1✔
584
}
1✔
585

586
void Function::slotBPMChanged(int bpmNumber)
×
587
{
588
    Q_UNUSED(bpmNumber)
589
    m_beatResyncNeeded = true;
×
590
}
×
591

592
/****************************************************************************
593
 * Speed
594
 ****************************************************************************/
595

596
void Function::setFadeInSpeed(uint ms)
33✔
597
{
598
    m_fadeInSpeed = ms;
33✔
599
    emit changed(m_id);
33✔
600
}
33✔
601

602
uint Function::fadeInSpeed() const
986✔
603
{
604
    return m_fadeInSpeed;
986✔
605
}
606

607
void Function::setFadeOutSpeed(uint ms)
33✔
608
{
609
    m_fadeOutSpeed = ms;
33✔
610
    emit changed(m_id);
33✔
611
}
33✔
612

613
uint Function::fadeOutSpeed() const
165✔
614
{
615
    return m_fadeOutSpeed;
165✔
616
}
617

618
void Function::setDuration(uint ms)
131✔
619
{
620
    m_duration = ms;
131✔
621
    emit changed(m_id);
131✔
622
}
131✔
623

624
uint Function::duration() const
950✔
625
{
626
    return m_duration;
950✔
627
}
628

629
quint32 Function::totalDuration()
1✔
630
{
631
    // fall back to duration in case a
632
    // subclass doesn't provide this method
633
    return duration();
1✔
634
}
635

636
void Function::setTotalDuration(quint32 msec)
×
637
{
638
    Q_UNUSED(msec)
639
}
×
640

641
void Function::setOverrideFadeInSpeed(uint ms)
1✔
642
{
643
    m_overrideFadeInSpeed = ms;
1✔
644
}
1✔
645

646
uint Function::overrideFadeInSpeed() const
1,091✔
647
{
648
    return m_overrideFadeInSpeed;
1,091✔
649
}
650

651
void Function::setOverrideFadeOutSpeed(uint ms)
35✔
652
{
653
    m_overrideFadeOutSpeed = ms;
35✔
654
}
35✔
655

656
uint Function::overrideFadeOutSpeed() const
292✔
657
{
658
    return m_overrideFadeOutSpeed;
292✔
659
}
660

661
void Function::setOverrideDuration(uint ms)
1✔
662
{
663
    m_overrideDuration = ms;
1✔
664
}
1✔
665

666
uint Function::overrideDuration() const
155✔
667
{
668
    return m_overrideDuration;
155✔
669
}
670

671
QString Function::speedToString(uint ms)
60✔
672
{
673
    QString str;
674
    if (ms == infiniteSpeed())
60✔
675
    {
676
        str = QChar(0x221E); // Infinity symbol
17✔
677
    }
678
    else
679
    {
680
        uint h, m, s;
681

682
        h = ms / MS_PER_HOUR;
43✔
683
        ms -= (h * MS_PER_HOUR);
684

685
        m = ms / MS_PER_MINUTE;
43✔
686
        ms -= (m * MS_PER_MINUTE);
687

688
        s = ms / MS_PER_SECOND;
43✔
689
        ms -= (s * MS_PER_SECOND);
690

691
        if (h != 0)
43✔
692
            str += QString("%1h").arg(h, 1, 10, QChar('0'));
8✔
693
        if (m != 0)
43✔
694
            str += QString("%1m").arg(m, str.size() ? 2 : 1, 10, QChar('0'));
10✔
695
        if (s != 0)
43✔
696
            str += QString("%1s").arg(s, str.size() ? 2 : 1, 10, QChar('0'));
12✔
697
        if (ms != 0 || str.size() == 0)
43✔
698
            str += QString("%1ms").arg(ms, str.size() ? 3 : 1, 10, QChar('0'));
80✔
699
    }
700

701
    return str;
60✔
702
}
×
703

704
static uint speedSplit(QString& speedString, QString splitNeedle)
78✔
705
{
706
    QStringList splitResult;
707
    // Filter out "ms" because "m" and "s" may wrongly use it
708
    splitResult = speedString.split("ms");
156✔
709
    if (splitResult.count() > 1)
78✔
710
        splitResult = splitResult.at(0).split(splitNeedle);
24✔
711
    else
712
        splitResult = speedString.split(splitNeedle);
132✔
713

714
    if (splitResult.count() > 1)
78✔
715
    {
716
        speedString.remove(0, speedString.indexOf(splitNeedle) + 1);
13✔
717
        return splitResult.at(0).toUInt();
13✔
718
    }
719
    return 0;
720
}
721

722
uint Function::stringToSpeed(QString speed)
27✔
723
{
724
    uint value = 0;
725

726
    if (speed == QChar(0x221E)) // Infinity symbol
727
        return infiniteSpeed();
1✔
728

729
    value += speedSplit(speed, "h") * 1000 * 60 * 60;
26✔
730
    value += speedSplit(speed, "m") * 1000 * 60;
26✔
731
    value += speedSplit(speed, "s") * 1000;
26✔
732

733
    if (speed.contains("."))
52✔
734
    {
735
        // lround avoids toDouble precison issues (.03 transforms to .029)
736
        value += lround(speed.toDouble() * 1000.0);
20✔
737
    }
738
    else
739
    {
740
        if (speed.contains("ms"))
12✔
741
            speed = speed.split("ms").at(0);
6✔
742
        value += speed.toUInt();
6✔
743
    }
744

745
    return speedNormalize(value);
26✔
746
}
747

748
uint Function::speedNormalize(uint speed)
81✔
749
{
750
    if ((int)speed < 0)
81✔
751
        return infiniteSpeed();
6✔
752
    return speed;
753
}
754

755
uint Function::speedAdd(uint left, uint right)
14✔
756
{
757
    if (speedNormalize(left) == infiniteSpeed()
14✔
758
        || speedNormalize(right) == infiniteSpeed())
14✔
759
        return infiniteSpeed();
3✔
760

761
    return speedNormalize(left + right);
11✔
762
}
763

764
uint Function::speedSubtract(uint left, uint right)
7✔
765
{
766
    if (right >= left)
7✔
767
        return 0;
768
    if (speedNormalize(right) == infiniteSpeed())
3✔
769
        return 0;
770
    if (speedNormalize(left) == infiniteSpeed())
3✔
771
        return infiniteSpeed();
1✔
772

773
    return speedNormalize(left - right);
2✔
774
}
775

776
void Function::tap()
×
777
{
778
}
×
779

780
bool Function::loadXMLSpeed(QXmlStreamReader &speedRoot)
7✔
781
{
782
    if (speedRoot.name() != KXMLQLCFunctionSpeed)
14✔
783
        return false;
784

785
    QXmlStreamAttributes attrs = speedRoot.attributes();
7✔
786

787
    m_fadeInSpeed = attrs.value(KXMLQLCFunctionSpeedFadeIn).toString().toUInt();
7✔
788
    m_fadeOutSpeed = attrs.value(KXMLQLCFunctionSpeedFadeOut).toString().toUInt();
7✔
789
    m_duration = attrs.value(KXMLQLCFunctionSpeedDuration).toString().toUInt();
7✔
790

791
    speedRoot.skipCurrentElement();
7✔
792

793
    return true;
794
}
795

796
bool Function::saveXMLSpeed(QXmlStreamWriter *doc) const
9✔
797
{
798
    doc->writeStartElement(KXMLQLCFunctionSpeed);
9✔
799
    doc->writeAttribute(KXMLQLCFunctionSpeedFadeIn, QString::number(fadeInSpeed()));
9✔
800
    doc->writeAttribute(KXMLQLCFunctionSpeedFadeOut, QString::number(fadeOutSpeed()));
9✔
801
    doc->writeAttribute(KXMLQLCFunctionSpeedDuration, QString::number(duration()));
9✔
802
    doc->writeEndElement();
9✔
803

804
    return true;
9✔
805
}
806

807
uint Function::infiniteSpeed()
480✔
808
{
809
    return (uint) -2;
480✔
810
}
811

812
uint Function::defaultSpeed()
3,313✔
813
{
814
    return (uint) -1;
3,313✔
815
}
816

817
/*****************************************************************************
818
 * UI State
819
 *****************************************************************************/
820

821
QVariant Function::uiStateValue(QString property)
2✔
822
{
823
    return m_uiState.value(property, QVariant());
2✔
824
}
825

826
void Function::setUiStateValue(QString property, QVariant value)
1✔
827
{
828
    m_uiState[property] = value;
1✔
829
}
1✔
830

831
QMap<QString, QVariant> Function::uiStateMap() const
19✔
832
{
833
    return m_uiState;
19✔
834
}
835

836
/*****************************************************************************
837
 * Fixtures
838
 *****************************************************************************/
839

840
void Function::slotFixtureRemoved(quint32 fid)
1✔
841
{
842
    Q_UNUSED(fid);
843
}
1✔
844

845
/*****************************************************************************
846
 * Load & Save
847
 *****************************************************************************/
848
bool Function::saveXML(QXmlStreamWriter *doc)
×
849
{
850
    Q_UNUSED(doc)
851
    return false;
×
852
}
853

854
bool Function::loadXML(QXmlStreamReader &root)
×
855
{
856
    Q_UNUSED(root)
857
    return false;
×
858
}
859

860
bool Function::loader(QXmlStreamReader &root, Doc* doc)
12✔
861
{
862
    if (root.name() != KXMLQLCFunction)
24✔
863
    {
864
        qWarning("Function node not found!");
2✔
865
        return false;
2✔
866
    }
867

868
    QXmlStreamAttributes attrs = root.attributes();
10✔
869

870
    /* Get common information from the tag's attributes */
871
    quint32 id = attrs.value(KXMLQLCFunctionID).toString().toUInt();
10✔
872
    QString name = attrs.value(KXMLQLCFunctionName).toString();
10✔
873
    Type type = Function::stringToType(attrs.value(KXMLQLCFunctionType).toString());
10✔
874
    QString path;
875
    bool visible = true;
876
    Universe::BlendMode blendMode = Universe::NormalBlend;
877

878
    if (attrs.hasAttribute(KXMLQLCFunctionPath))
10✔
879
        path = attrs.value(KXMLQLCFunctionPath).toString();
×
880
    if (attrs.hasAttribute(KXMLQLCFunctionHidden))
10✔
881
        visible = false;
882
    if (attrs.hasAttribute(KXMLQLCFunctionBlendMode))
10✔
883
        blendMode = Universe::stringToBlendMode(attrs.value(KXMLQLCFunctionBlendMode).toString());
×
884

885
    /* Check for ID validity before creating the function */
886
    if (id == Function::invalidId())
10✔
887
    {
888
        qWarning() << Q_FUNC_INFO << "Function ID" << id << "is not allowed.";
×
889
        return false;
×
890
    }
891

892
    /* Create a new function according to the type */
893
    Function* function = NULL;
894
    if (type == Function::SceneType)
895
        function = new class Scene(doc);
1✔
896
    else if (type == Function::ChaserType)
897
        function = new class Chaser(doc);
1✔
898
    else if (type == Function::CollectionType)
899
        function = new class Collection(doc);
5✔
900
    else if (type == Function::EFXType)
901
        function = new class EFX(doc);
1✔
902
    else if (type == Function::ScriptType)
903
        function = new class Script(doc);
×
904
    else if (type == Function::RGBMatrixType)
905
        function = new class RGBMatrix(doc);
×
906
    else if (type == Function::ShowType)
907
        function = new class Show(doc);
×
908
    else if (type == Function::SequenceType)
909
        function = new class Sequence(doc);
×
910
    else if (type == Function::AudioType)
911
        function = new class Audio(doc);
×
912
    else if (type == Function::VideoType)
913
        function = new class Video(doc);
×
914
    else
915
        return false;
916

917
    function->setName(name);
8✔
918
    function->setPath(path);
8✔
919
    function->setVisible(visible);
8✔
920
    function->setBlendMode(blendMode);
8✔
921
    if (function->loadXML(root) == true)
8✔
922
    {
923
        if (doc->addFunction(function, id) == true)
8✔
924
        {
925
            /* Success */
926
            return true;
927
        }
928
        else
929
        {
930
            qWarning() << "Function" << name << "cannot be created.";
×
931
            delete function;
×
932
            return false;
×
933
        }
934
    }
935
    else
936
    {
937
        qWarning() << "Function" << name << "cannot be loaded.";
×
938
        delete function;
×
939
        return false;
×
940
    }
941
}
10✔
942

943
void Function::postLoad()
×
944
{
945
    /* NOP */
946
}
×
947

948
bool Function::contains(quint32 functionId)
16✔
949
{
950
    Q_UNUSED(functionId);
951
    return false;
16✔
952
}
953

954
QList<quint32> Function::components()
×
955
{
956
    return QList<quint32>();
×
957
}
958

959
/*****************************************************************************
960
 * Flash
961
 *****************************************************************************/
962

963
void Function::flash(MasterTimer *timer, bool shouldOverride, bool forceLTP)
7✔
964
{
965
    Q_UNUSED(timer);
966
    Q_UNUSED(shouldOverride);
967
    Q_UNUSED(forceLTP);
968

969
    if (m_flashing == false)
7✔
970
    {
971
        emit flashing(m_id, true);
6✔
972
    }
973

974
    m_flashing = true;
7✔
975
}
7✔
976

977
void Function::unFlash(MasterTimer *timer)
6✔
978
{
979
    Q_UNUSED(timer);
980

981
    if (m_flashing == true)
6✔
982
        emit flashing(m_id, false);
6✔
983

984
    m_flashing = false;
6✔
985
}
6✔
986

987
bool Function::flashing() const
23✔
988
{
989
    return m_flashing;
23✔
990
}
991

992
/*****************************************************************************
993
 * Running
994
 *****************************************************************************/
995

996
void Function::preRun(MasterTimer *timer)
169✔
997
{
998
    Q_UNUSED(timer);
999

1000
    qDebug() << "Function preRun. Name:" << m_name << "ID:" << m_id << "type:" << typeToString(type());
1001
    m_running = true;
169✔
1002

1003
    emit running(m_id);
169✔
1004
}
169✔
1005

1006
void Function::write(MasterTimer *timer, QList<Universe *> universes)
×
1007
{
1008
    Q_UNUSED(timer);
1009
    Q_UNUSED(universes);
1010
}
×
1011

1012
void Function::postRun(MasterTimer *timer, QList<Universe *> universes)
139✔
1013
{
1014
    Q_UNUSED(timer);
1015
    Q_UNUSED(universes);
1016

1017
    qDebug() << "Function postRun. Name:" << m_name << "ID: " << m_id;
1018

1019
    {
1020
        QMutexLocker locker(&m_stopMutex);
139✔
1021

1022
        resetElapsed();
139✔
1023
        if (m_preserveAttributes == false)
139✔
1024
            resetAttributes();
139✔
1025

1026
        m_functionStopped.wakeAll();
139✔
1027
    }
1028

1029
    m_paused = false;
139✔
1030
    m_running = false;
139✔
1031
    emit stopped(m_id);
139✔
1032
}
139✔
1033

1034
void Function::dismissAllFaders()
112✔
1035
{
1036
    QMapIterator <quint32, QSharedPointer<GenericFader> > it(m_fadersMap);
112✔
1037
    while (it.hasNext() == true)
213✔
1038
    {
1039
        it.next();
101✔
1040
        QSharedPointer<GenericFader> fader = it.value();
1041
        if (!fader.isNull())
101✔
1042
            fader->requestDelete();
101✔
1043
    }
1044

1045
    m_fadersMap.clear();
112✔
1046
}
112✔
1047

1048
bool Function::isRunning() const
5,138✔
1049
{
1050
    return m_running;
5,138✔
1051
}
1052

1053
bool Function::isPaused() const
546✔
1054
{
1055
    return m_paused;
546✔
1056
}
1057

1058
/*****************************************************************************
1059
 * Elapsed ticks while running
1060
 *****************************************************************************/
1061

1062
quint32 Function::elapsed() const
20✔
1063
{
1064
    return m_elapsed;
20✔
1065
}
1066

1067
quint32 Function::elapsedBeats() const
1✔
1068
{
1069
    return m_elapsedBeats;
1✔
1070
}
1071

1072
void Function::resetElapsed()
140✔
1073
{
1074
    qDebug() << Q_FUNC_INFO;
1075
    m_elapsed = 0;
140✔
1076
    m_elapsedBeats = 0;
140✔
1077
}
140✔
1078

1079
void Function::incrementElapsed()
644✔
1080
{
1081
    // Don't wrap around. UINT_MAX is the maximum fade/hold time.
1082
    if (m_elapsed < UINT_MAX - MasterTimer::tick())
644✔
1083
        m_elapsed += MasterTimer::tick();
642✔
1084
    else
1085
        m_elapsed = UINT_MAX;
2✔
1086
}
644✔
1087

1088
void Function::incrementElapsedBeats()
×
1089
{
1090
    m_elapsedBeats += 1000;
×
1091
}
×
1092

1093
void Function::roundElapsed(quint32 roundTime)
×
1094
{
1095
    if (roundTime == 0)
×
1096
        m_elapsed = 0;
×
1097
    else
1098
        m_elapsed %= roundTime;
×
1099
}
×
1100

1101
/*****************************************************************************
1102
 * Start & Stop
1103
 *****************************************************************************/
1104

1105
void Function::start(MasterTimer* timer, FunctionParent source, quint32 startTime,
154✔
1106
                     uint overrideFadeIn, uint overrideFadeOut, uint overrideDuration, TempoType overrideTempoType)
1107
{
1108
    qDebug() << "Function start(). Name:" << m_name << "ID: " << m_id << "source:" << source.type() << source.id() << ", startTime:" << startTime;
1109

1110
    Q_ASSERT(timer != NULL);
1111

1112
    {
1113
        QMutexLocker sourcesLocker(&m_sourcesMutex);
154✔
1114
        if (m_sources.contains(source))
154✔
1115
            return;
1116
        m_sources.append(source);
154✔
1117
        if (m_sources.size() > 1)
154✔
1118
            return;
1119
    }
1120

1121
    /** If we're in a paused state, then just return to the running state
1122
     *  to let subclasses resuming what they were doing. */
1123
    if (m_paused == true)
154✔
1124
    {
1125
        m_paused = false;
×
1126
        return;
×
1127
    }
1128

1129
    m_elapsed = startTime;
154✔
1130
    m_elapsedBeats = 0;
154✔
1131
    m_overrideFadeInSpeed = overrideFadeIn;
154✔
1132
    m_overrideFadeOutSpeed = overrideFadeOut;
154✔
1133
    m_overrideDuration = overrideDuration;
154✔
1134
    m_overrideTempoType = overrideTempoType == Original ? tempoType() : overrideTempoType;
154✔
1135

1136
    m_stop = false;
154✔
1137
    timer->startFunction(this);
154✔
1138
}
1139

1140
void Function::setPause(bool enable)
10✔
1141
{
1142
    if (enable && isRunning() == false)
10✔
1143
        return;
1144

1145
    m_paused = enable;
10✔
1146
}
1147

1148
void Function::stop(FunctionParent source, bool preserveAttributes)
151✔
1149
{
1150
    qDebug() << "Function stop(). Name:" << m_name << "ID: " << m_id << "source:" << source.type() << source.id();
1151

1152
    QMutexLocker sourcesLocker(&m_sourcesMutex);
151✔
1153

1154
    if ((source.id() == id() && source.type() == FunctionParent::Function) ||
302✔
1155
        (source.type() == FunctionParent::Master) ||
270✔
1156
        (source.type() == FunctionParent::ManualVCWidget))
1157
    {
1158
        m_sources.clear();
35✔
1159
    }
1160
    else
1161
    {
1162
        m_sources.removeAll(source);
116✔
1163
    }
1164

1165
    if (m_sources.size() == 0)
151✔
1166
    {
1167
        m_stop = true;
151✔
1168
        m_preserveAttributes = preserveAttributes;
151✔
1169
    }
1170
}
151✔
1171

1172
bool Function::stopped() const
770✔
1173
{
1174
    return m_stop;
770✔
1175
}
1176

1177
bool Function::startedAsChild() const
×
1178
{
1179
    QMutexLocker sourcesLocker(const_cast<QMutex*>(&m_sourcesMutex));
×
1180
    foreach (FunctionParent source, m_sources)
×
1181
    {
1182
        if (source.type() == FunctionParent::Function && source.id() != id())
×
1183
            return true;
1184
    }
1185
    return false;
×
1186
}
1187

1188
int Function::invalidAttributeId()
730✔
1189
{
1190
    return -1;
730✔
1191
}
1192

1193
bool Function::stopAndWait()
1✔
1194
{
1195
    QMutexLocker locker(&m_stopMutex);
1✔
1196

1197
    stop(FunctionParent::master());
1✔
1198

1199
    QElapsedTimer watchdog;
1✔
1200
    watchdog.restart();
1✔
1201

1202
    // block thread for maximum 2 seconds
1203
    while (m_running == true)
21✔
1204
    {
1205
        if (watchdog.elapsed() > 2000)
21✔
1206
            return false;
1207

1208
        // wait until the function has stopped
1209
        m_functionStopped.wait(&m_stopMutex, 100);
20✔
1210
    }
1211

1212
    return true;
1213
}
1214

1215
/*****************************************************************************
1216
 * Attributes
1217
 *****************************************************************************/
1218

1219
int Function::registerAttribute(QString name, int flags, qreal min, qreal max, qreal value)
1,109✔
1220
{
1221
    for (int i = 0; i < m_attributes.count(); i++)
2,879✔
1222
    {
1223
        if (m_attributes[i].m_name == name)
1,770✔
1224
        {
1225
            m_attributes[i].m_min = min;
×
1226
            m_attributes[i].m_max = max;
×
1227
            m_attributes[i].m_value = value;
×
1228
            m_attributes[i].m_flags = flags;
×
1229
            m_attributes[i].m_isOverridden = false;
×
1230
            m_attributes[i].m_overrideValue = 0.0;
×
1231
            return i;
×
1232
        }
1233
    }
1234
    Attribute newAttr;
1235
    newAttr.m_name = name;
1,109✔
1236
    newAttr.m_min = min;
1,109✔
1237
    newAttr.m_max = max;
1,109✔
1238
    newAttr.m_value = value;
1,109✔
1239
    newAttr.m_flags = flags;
1,109✔
1240
    newAttr.m_isOverridden = false;
1,109✔
1241
    newAttr.m_overrideValue = 0.0;
1,109✔
1242
    m_attributes.append(newAttr);
1,109✔
1243

1244
    return m_attributes.count() - 1;
1,109✔
1245
}
1246

1247
int Function::requestAttributeOverride(int attributeIndex, qreal value)
247✔
1248
{
1249
    if (attributeIndex < 0 || attributeIndex >= m_attributes.count())
247✔
1250
        return -1;
1251

1252
    int attributeID = invalidAttributeId();
247✔
1253

1254
    if (m_attributes.at(attributeIndex).m_flags & Single)
247✔
1255
    {
1256
        foreach (int id, m_overrideMap.keys())
607✔
1257
        {
1258
            if (m_overrideMap[id].m_attrIndex == attributeIndex)
120✔
1259
            {
1260
                attributeID = id;
5✔
1261
                break;
5✔
1262
            }
1263
        }
1264
    }
1265

1266
    if (attributeID == invalidAttributeId())
247✔
1267
    {
1268
        AttributeOverride override;
1269
        override.m_attrIndex = attributeIndex;
1270
        override.m_value = 0.0;
1271

1272
        attributeID = m_lastOverrideAttributeId;
242✔
1273
        m_overrideMap[attributeID] = override;
242✔
1274

1275
        qDebug() << name() << "Override requested for new attribute" << attributeIndex << "value" << value << "new ID" << attributeID;
1276

1277
        calculateOverrideValue(attributeIndex);
242✔
1278

1279
        m_lastOverrideAttributeId++;
242✔
1280
    }
1281
    else
1282
    {
1283
        qDebug() << name() << "Override requested for existing attribute" << attributeIndex << "value" << value << "single ID" << attributeID;
1284
    }
1285

1286
    // actually apply the new override value
1287
    adjustAttribute(value, attributeID);
247✔
1288

1289
    return attributeID;
247✔
1290
}
1291

1292
void Function::releaseAttributeOverride(int attributeId)
1✔
1293
{
1294
    if (m_overrideMap.contains(attributeId) == false)
1✔
1295
        return;
1296

1297
    int attributeIndex = m_overrideMap[attributeId].m_attrIndex;
1✔
1298

1299
    m_overrideMap.remove(attributeId);
1✔
1300

1301
    calculateOverrideValue(attributeIndex);
1✔
1302

1303
    qDebug() << name() << "Attribute override released" << attributeId;
1✔
1304
}
1305

1306
bool Function::unregisterAttribute(QString name)
11✔
1307
{
1308
    for (int i = 0; i < m_attributes.count(); i++)
12✔
1309
    {
1310
        if (m_attributes[i].m_name == name)
12✔
1311
        {
1312
            m_attributes.removeAt(i);
11✔
1313
            return true;
11✔
1314
        }
1315
    }
1316
    return false;
1317
}
1318

1319
bool Function::renameAttribute(int idx, QString newName)
2✔
1320
{
1321
    if (idx < 0 || idx >= m_attributes.count())
2✔
1322
        return false;
1323
    m_attributes[idx].m_name = newName;
1✔
1324

1325
    return true;
1✔
1326
}
1327

1328
int Function::adjustAttribute(qreal value, int attributeId)
431✔
1329
{
1330
    if (attributeId < 0)
431✔
1331
        return -1;
1332

1333
    int attrIndex;
1334

1335
    //qDebug() << name() << "Attribute ID:" << attributeId << ", val:" << value;
1336

1337
    if (attributeId < OVERRIDE_ATTRIBUTE_START_ID)
431✔
1338
    {
1339
        if (attributeId >= m_attributes.count() || m_attributes[attributeId].m_value == value)
179✔
1340
            return -1;
60✔
1341

1342
        // Adjust the original value of an attribute. Only Function editors should do this!
1343
        m_attributes[attributeId].m_value = CLAMP(value, m_attributes[attributeId].m_min, m_attributes[attributeId].m_max);
119✔
1344
        attrIndex = attributeId;
119✔
1345
    }
1346
    else
1347
    {
1348
        if (m_overrideMap.contains(attributeId) == false)
252✔
1349
            return -1;
1350

1351
        // Adjust an attribute override value and recalculate the final overridden value
1352
        m_overrideMap[attributeId].m_value = value;
252✔
1353
        attrIndex = m_overrideMap[attributeId].m_attrIndex;
252✔
1354
        calculateOverrideValue(attrIndex);
252✔
1355
    }
1356

1357
    emit attributeChanged(attrIndex, m_attributes[attrIndex].m_isOverridden ?
742✔
1358
                                     m_attributes[attrIndex].m_overrideValue :
252✔
1359
                                     m_attributes[attrIndex].m_value);
119✔
1360

1361
    return attrIndex;
371✔
1362
}
1363

1364
void Function::resetAttributes()
141✔
1365
{
1366
    for (int i = 0; i < m_attributes.count(); i++)
421✔
1367
    {
1368
        m_attributes[i].m_isOverridden = false;
280✔
1369
        m_attributes[i].m_overrideValue = 0.0;
280✔
1370
    }
1371
    m_overrideMap.clear();
141✔
1372
    m_lastOverrideAttributeId = OVERRIDE_ATTRIBUTE_START_ID;
141✔
1373
}
141✔
1374

1375
qreal Function::getAttributeValue(int attributeIndex) const
23,739✔
1376
{
1377
    if (attributeIndex >= m_attributes.count())
23,739✔
1378
        return 0.0;
1379

1380
    return m_attributes[attributeIndex].m_isOverridden ?
23,664✔
1381
                m_attributes[attributeIndex].m_overrideValue :
1382
                m_attributes[attributeIndex].m_value;
1383
}
1384

1385
int Function::getAttributeIndex(QString name) const
3✔
1386
{
1387
    for (int i = 0; i < m_attributes.count(); i++)
7✔
1388
    {
1389
        Attribute attr = m_attributes.at(i);
6✔
1390
        if (attr.m_name == name)
6✔
1391
            return i;
1392
    }
1393
    return -1;
1394
}
1395

1396
QList<Attribute> Function::attributes() const
121✔
1397
{
1398
    return m_attributes;
121✔
1399
}
1400

1401
void Function::calculateOverrideValue(int attributeIndex)
495✔
1402
{
1403
    if (attributeIndex >= m_attributes.count())
495✔
1404
        return;
×
1405

1406
    qreal finalValue = 0.0;
1407
    bool found = false;
1408
    Attribute origAttr = m_attributes.at(attributeIndex);
495✔
1409

1410
    if (origAttr.m_flags & Multiply)
495✔
1411
        finalValue = origAttr.m_value;
493✔
1412

1413
    foreach (AttributeOverride attr, m_overrideMap.values())
1,716✔
1414
    {
1415
        if (attr.m_attrIndex != attributeIndex)
726✔
1416
            continue;
1417

1418
        found = true;
1419

1420
        if (origAttr.m_flags & Multiply)
494✔
1421
            finalValue = finalValue * attr.m_value;
492✔
1422
        else if (origAttr.m_flags & LastWins)
2✔
1423
            finalValue = attr.m_value;
1424
    }
1425

1426
    m_attributes[attributeIndex].m_overrideValue = finalValue;
495✔
1427
    m_attributes[attributeIndex].m_isOverridden = found;
495✔
1428
}
1429

1430
/*************************************************************************
1431
 * Blend mode
1432
 *************************************************************************/
1433

1434
void Function::setBlendMode(Universe::BlendMode mode)
5✔
1435
{
1436
    m_blendMode = mode;
5✔
1437
}
5✔
1438

1439
Universe::BlendMode Function::blendMode() const
166✔
1440
{
1441
    return m_blendMode;
166✔
1442
}
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

© 2025 Coveralls, Inc