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

mcallegari / qlcplus / 5893179548

17 Aug 2023 04:15PM UTC coverage: 28.055% (+0.001%) from 28.054%
5893179548

push

github

web-flow
Merge pull request #1447 from Wazzledi/master

Fix random order chaser always starts with first step

3 of 3 new or added lines in 1 file covered. (100.0%)

15331 of 54646 relevant lines covered (28.06%)

20218.01 hits per line

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

70.72
/engine/src/chaserrunner.cpp
1
/*
2
  Q Light Controller Plus
3
  chaserrunner.cpp
4

5
  Copyright (c) Heikki Junnila
6
                Massimo Callegari
7
                Jano Svitok
8

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

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

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

22
#include <QElapsedTimer>
23
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
24
#include <QRandomGenerator>
25
#endif
26
#include <QDebug>
27

28
#include "chaserrunner.h"
29
#include "genericfader.h"
30
#include "mastertimer.h"
31
#include "fadechannel.h"
32
#include "chaserstep.h"
33
#include "qlcmacros.h"
34
#include "fixture.h"
35
#include "chaser.h"
36
#include "scene.h"
37
#include "doc.h"
38

39
ChaserRunner::ChaserRunner(const Doc *doc, const Chaser *chaser, quint32 startTime)
30✔
40
    : QObject(NULL)
41
    , m_doc(doc)
42
    , m_chaser(chaser)
43
    , m_updateOverrideSpeeds(false)
44
    , m_startOffset(0)
45
    , m_lastRunStepIdx(-1)
46
    , m_lastFunctionID(Function::invalidId())
60✔
47
    , m_roundTime(new QElapsedTimer())
30✔
48
    , m_order()
60✔
49
{
50
    Q_ASSERT(chaser != NULL);
30✔
51

52
    m_pendingAction.m_action = ChaserNoAction;
30✔
53
    m_pendingAction.m_masterIntensity = 1.0;
30✔
54
    m_pendingAction.m_stepIntensity = 1.0;
30✔
55
    m_pendingAction.m_fadeMode = Chaser::FromFunction;
30✔
56
    m_pendingAction.m_stepIndex = -1;
30✔
57

58
    if (m_chaser->type() == Function::SequenceType && startTime > 0)
30✔
59
    {
60
        qDebug() << "[ChaserRunner] startTime:" << startTime;
×
61
        int idx = 0;
×
62
        quint32 stepsTime = 0;
×
63
        foreach(ChaserStep step, chaser->steps())
×
64
        {
65
            uint duration = m_chaser->durationMode() == Chaser::Common ? m_chaser->duration() : step.duration;
×
66

67
            if (startTime < stepsTime + duration)
×
68
            {
69
                m_pendingAction.m_action = ChaserSetStepIndex;
×
70
                m_pendingAction.m_stepIndex = idx;
×
71
                m_startOffset = startTime - stepsTime;
×
72
                qDebug() << "[ChaserRunner] Starting from step:" << idx;
×
73
                break;
×
74
            }
75
            idx++;
×
76
            stepsTime += duration;
×
77
        }
78
    }
79

80
    m_direction = m_chaser->direction();
30✔
81
    connect(chaser, SIGNAL(changed(quint32)), this, SLOT(slotChaserChanged()));
30✔
82
    m_roundTime->restart();
30✔
83

84
    fillOrder();
30✔
85
}
30✔
86

87
ChaserRunner::~ChaserRunner()
40✔
88
{
89
    clearRunningList();
30✔
90
    delete m_roundTime;
30✔
91
}
40✔
92

93
/****************************************************************************
94
 * Speed
95
 ****************************************************************************/
96

97
void ChaserRunner::slotChaserChanged()
38✔
98
{
99
    // Handle (possible) speed change on the next write() pass
100
    m_updateOverrideSpeeds = true;
38✔
101
    QList<ChaserRunnerStep*> delList;
76✔
102
    foreach(ChaserRunnerStep *step, m_runnerSteps)
54✔
103
    {
104
        if (!m_chaser->steps().contains(ChaserStep(step->m_function->id())))
8✔
105
        {
106
            // Disappearing function: remove step
107
            delList.append(step);
5✔
108
        }
109
        else
110
        {
111
            // Recalculate the speed of each running step
112
            step->m_fadeIn = stepFadeIn(step->m_index);
3✔
113
            step->m_fadeOut = stepFadeOut(step->m_index);
3✔
114
            step->m_duration = stepDuration(step->m_index);
3✔
115
        }
116
    }
117
    foreach(ChaserRunnerStep *step, delList)
48✔
118
    {
119
        step->m_function->stop(functionParent());
5✔
120
        delete step;
5✔
121
        m_runnerSteps.removeAll(step);
5✔
122
    }
123
}
38✔
124

125
uint ChaserRunner::stepFadeIn(int stepIdx) const
129✔
126
{
127
    uint speed = 0;
129✔
128
    if (m_chaser->overrideFadeInSpeed() != Function::defaultSpeed())
129✔
129
    {
130
        // Override speed is used when another function has started the chaser,
131
        // i.e. chaser inside a chaser that wants to impose its own fade in speed
132
        // to its members.
133
        speed = m_chaser->overrideFadeInSpeed();
10✔
134
    }
135
    else
136
    {
137
        switch (m_chaser->fadeInMode())
119✔
138
        {
139
            case Chaser::Common:
3✔
140
                // All steps' fade in speed is dictated by the chaser
141
                speed = m_chaser->fadeInSpeed();
3✔
142
            break;
3✔
143
            case Chaser::PerStep:
4✔
144
                // Each step specifies its own fade in speed
145
                if (stepIdx >= 0 && stepIdx < m_chaser->stepsCount())
4✔
146
                    speed = m_chaser->steps().at(stepIdx).fadeIn;
3✔
147
                else
148
                    speed = Function::defaultSpeed();
1✔
149
            break;
4✔
150
            default:
112✔
151
            case Chaser::Default:
152
                // Don't touch members' fade in speed at all
153
                speed = Function::defaultSpeed();
112✔
154
            break;
112✔
155
        }
156
    }
157

158
    return speed;
129✔
159
}
160

161
uint ChaserRunner::stepFadeOut(int stepIdx) const
162✔
162
{
163
    uint speed = 0;
162✔
164
    if (m_chaser->overrideFadeOutSpeed() != Function::defaultSpeed())
162✔
165
    {
166
        // Override speed is used when another function has started the chaser,
167
        // i.e. chaser inside a chaser that wants to impose its own fade out speed
168
        // to its members.
169
        speed = m_chaser->overrideFadeOutSpeed();
10✔
170
    }
171
    else
172
    {
173
        switch (m_chaser->fadeOutMode())
152✔
174
        {
175
            case Chaser::Common:
3✔
176
                // All steps' fade out speed is dictated by the chaser
177
                speed = m_chaser->fadeOutSpeed();
3✔
178
            break;
3✔
179
            case Chaser::PerStep:
4✔
180
                // Each step specifies its own fade out speed
181
                if (stepIdx >= 0 && stepIdx < m_chaser->stepsCount())
4✔
182
                    speed = m_chaser->steps().at(stepIdx).fadeOut;
3✔
183
                else
184
                    speed = Function::defaultSpeed();
1✔
185
            break;
4✔
186
            default:
145✔
187
            case Chaser::Default:
188
                // Don't touch members' fade out speed at all
189
                speed = Function::defaultSpeed();
145✔
190
            break;
145✔
191
        }
192
    }
193

194
    return speed;
162✔
195
}
196

197
uint ChaserRunner::stepDuration(int stepIdx) const
137✔
198
{
199
    uint speed = 0;
137✔
200
    if (m_chaser->overrideDuration() != Function::defaultSpeed())
137✔
201
    {
202
        // Override speed is used when another function has started the chaser,
203
        // i.e. chaser inside a chaser that wants to impose its own duration
204
        // to its members.
205
        speed = m_chaser->overrideDuration();
10✔
206
    }
207
    else
208
    {
209
        switch (m_chaser->durationMode())
127✔
210
        {
211
            default:
123✔
212
            case Chaser::Default:
213
            case Chaser::Common:
214
                // All steps' duration is dictated by the chaser
215
                speed = m_chaser->duration();
123✔
216
            break;
123✔
217
            case Chaser::PerStep:
4✔
218
                // Each step specifies its own duration
219
                if (stepIdx >= 0 && stepIdx < m_chaser->stepsCount())
4✔
220
                    speed = m_chaser->steps().at(stepIdx).duration;
3✔
221
                else
222
                    speed = m_chaser->duration();
1✔
223
            break;
4✔
224
        }
225
    }
226

227
    return speed;
137✔
228
}
229

230
/****************************************************************************
231
 * Step control
232
 ****************************************************************************/
233

234
void ChaserRunner::setAction(ChaserAction &action)
38✔
235
{
236
    // apply the actions that can be applied immediately
237
    switch (action.m_action)
38✔
238
    {
239
        case ChaserNoAction:
6✔
240
            m_pendingAction.m_masterIntensity = action.m_masterIntensity;
6✔
241
            m_pendingAction.m_stepIntensity = action.m_stepIntensity;
6✔
242
        break;
6✔
243

244
        case ChaserStopStep:
×
245
        {
246
            bool stopped = false;
×
247

248
            foreach(ChaserRunnerStep *step, m_runnerSteps)
×
249
            {
250
                if (action.m_stepIndex == step->m_index)
×
251
                {
252
                    qDebug() << "[ChaserRunner] Stopping step idx:" << action.m_stepIndex << "(running:" << m_runnerSteps.count() << ")";
×
253
                    m_lastFunctionID = step->m_function->type() == Function::SceneType ? step->m_function->id() : Function::invalidId();
×
254
                    step->m_function->stop(functionParent());
×
255
                    m_runnerSteps.removeOne(step);
×
256
                    delete step;
×
257
                    stopped = true;
×
258
                }
259
            }
260

261
            if (stopped && m_runnerSteps.size() == 1)
×
262
            {
263
                ChaserRunnerStep *lastStep = m_runnerSteps.at(0);
×
264
                m_lastRunStepIdx = lastStep->m_index;
×
265
                emit currentStepChanged(m_lastRunStepIdx);
×
266
            }
267
        }
268
        break;
×
269

270
        // copy to pending action. Will be processed at the next write call
271
        default:
32✔
272
            m_pendingAction.m_stepIndex = action.m_stepIndex;
32✔
273
            m_pendingAction.m_masterIntensity = action.m_masterIntensity;
32✔
274
            m_pendingAction.m_stepIntensity = action.m_stepIntensity;
32✔
275
            m_pendingAction.m_fadeMode = action.m_fadeMode;
32✔
276
            m_pendingAction.m_action = action.m_action;
32✔
277
        break;
32✔
278
    }
279
}
38✔
280

281
void ChaserRunner::tap()
2✔
282
{
283
    if (uint(m_roundTime->elapsed()) >= (stepDuration(m_lastRunStepIdx) / 4))
2✔
284
        m_pendingAction.m_action = ChaserNextStep;
2✔
285
}
2✔
286

287
int ChaserRunner::currentStepIndex() const
65✔
288
{
289
    return m_lastRunStepIdx;
65✔
290
}
291

292
int ChaserRunner::runningStepsNumber() const
×
293
{
294
    return m_runnerSteps.count();
×
295
}
296

297
ChaserRunnerStep *ChaserRunner::currentRunningStep() const
×
298
{
299
    if (m_runnerSteps.count() > 0)
×
300
        return m_runnerSteps.at(0);
×
301
    return NULL;
×
302
}
303

304
int ChaserRunner::computeNextStep(int currentStep) const
19✔
305
{
306
    int nextStep = currentStep;
19✔
307

308
    if (m_chaser->runOrder() == Function::Random)
19✔
309
    {
310
        nextStep = m_order.indexOf(nextStep);
×
311
        if (nextStep == -1)
×
312
        {
313
            qDebug() << "[ChaserRunner] steps order not found";
×
314
            nextStep = currentStep;
×
315
        }
316
    }
317

318
    // Next step
319
    if (m_direction == Function::Forward)
19✔
320
    {
321
        nextStep++;
19✔
322
    }
323
    else
324
    {
325
        nextStep--;
×
326
    }
327

328
    if (nextStep < m_chaser->stepsCount() && nextStep >= 0)
19✔
329
    {
330
        if (m_chaser->runOrder() == Function::Random)
16✔
331
        {
332
            nextStep = randomStepIndex(nextStep);
×
333
        }
334
        return nextStep; // In the middle of steps. No need to go any further.
16✔
335
    }
336

337
    if (m_chaser->runOrder() == Function::SingleShot)
3✔
338
    {
339
        return -1; // Forward or Backward SingleShot has been completed.
×
340
    }
341
    else if (m_chaser->runOrder() == Function::Loop)
3✔
342
    {
343
        if (m_direction == Function::Forward)
3✔
344
        {
345
            if (nextStep >= m_chaser->stepsCount())
3✔
346
                nextStep = 0;
3✔
347
            else
348
                nextStep = m_chaser->stepsCount() - 1; // Used by CueList with manual prev
×
349
        }
350
        else // Backward
351
        {
352
            if (nextStep < 0)
×
353
                nextStep = m_chaser->stepsCount() - 1;
×
354
            else
355
                nextStep = 0;
×
356
        }
357
    }
358
    else if (m_chaser->runOrder() == Function::Random)
×
359
    {
360
        nextStep = randomStepIndex(nextStep);
×
361
    }
362
    else // Ping Pong
363
    {
364
        // Change direction, but don't run the first/last step twice.
365
        if (m_direction == Function::Forward)
×
366
        {
367
            nextStep = m_chaser->stepsCount() - 2;
×
368
        }
369
        else // Backwards
370
        {
371
            nextStep = 1;
×
372
        }
373

374
        // Make sure we don't go beyond limits.
375
        nextStep = CLAMP(nextStep, 0, m_chaser->stepsCount() - 1);
×
376
    }
377

378
    return nextStep;
3✔
379
}
380

381
void ChaserRunner::shuffle(QVector<int> & data)
30✔
382
{
383
   int n = data.size();
30✔
384
   for (int i = n - 1; i > 0; --i)
85✔
385
   {
386
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
387
      qSwap(data[i], data[qrand() % (i + 1)]);
388
#else
389
      qSwap(data[i], data[QRandomGenerator::global()->generate() % (i + 1)]);
55✔
390
#endif
391
   }
392
}
30✔
393

394
int ChaserRunner::randomStepIndex(int step) const
×
395
{
396
   if (m_chaser->runOrder() == Function::Random && step >= 0 && step < m_order.size())
×
397
       return m_order[step];
×
398

399
   return step;
×
400
}
401

402
void ChaserRunner::fillOrder()
30✔
403
{
404
    fillOrder(m_chaser->stepsCount());
30✔
405
}
30✔
406

407
void ChaserRunner::fillOrder(int size)
30✔
408
{
409
   m_order.resize(size);
30✔
410
   for (int i = 0; i < size; ++i)
111✔
411
       m_order[i] = i;
81✔
412

413
   shuffle(m_order);
30✔
414
}
30✔
415

416
/****************************************************************************
417
 * Intensity
418
 ****************************************************************************/
419

420
void ChaserRunner::adjustStepIntensity(qreal fraction, int requestedStepIndex, int fadeControl)
7✔
421
{
422
    fraction = CLAMP(fraction, qreal(0.0), qreal(1.0));
7✔
423

424
    //qDebug() << "Adjust intensity" << fraction << "step:" << requestedStepIndex << "fade:" << fadeControl;
425

426
    int stepIndex = requestedStepIndex;
7✔
427
    if (stepIndex == -1)
7✔
428
    {
429
        stepIndex = m_lastRunStepIdx;
7✔
430
        // store the intensity to be applied at the next step startup
431
        m_pendingAction.m_masterIntensity = fraction;
7✔
432
    }
433

434
    foreach(ChaserRunnerStep *step, m_runnerSteps)
7✔
435
    {
436
        if (stepIndex == step->m_index && step->m_function != NULL)
2✔
437
        {
438
            if (requestedStepIndex == -1 && step->m_function->type() == Function::SceneType)
2✔
439
            {
440
                Scene *scene = qobject_cast<Scene *>(step->m_function);
2✔
441
                scene->adjustAttribute(fraction, step->m_pIntensityOverrideId);
2✔
442
            }
443
            else
444
            {
445
                step->m_function->adjustAttribute(fraction, step->m_intensityOverrideId);
×
446
            }
447
            return;
2✔
448
        }
449
    }
450

451
    // No need to start a new step if it is not wanted
452
    if (requestedStepIndex == -1)
5✔
453
        return;
5✔
454

455
    // Don't start a step with an intensity of zero
456
    if (fraction == qreal(0.0))
×
457
        return;
×
458

459
    // not found ? It means we need to start a new step and crossfade kicks in !
460
    startNewStep(stepIndex, m_doc->masterTimer(), m_pendingAction.m_masterIntensity, fraction, fadeControl);
×
461
}
462

463
/****************************************************************************
464
 * Running
465
 ****************************************************************************/
466

467
void ChaserRunner::clearRunningList()
63✔
468
{
469
    // empty the running queue
470
    foreach(ChaserRunnerStep *step, m_runnerSteps)
129✔
471
    {
472
        if (step->m_function)
33✔
473
        {
474
            // restore the original Function fade out time
475
            step->m_function->setOverrideFadeOutSpeed(stepFadeOut(step->m_index));
33✔
476
            step->m_function->stop(functionParent(), m_chaser->type() == Function::SequenceType);
33✔
477
            m_lastFunctionID = step->m_function->type() == Function::SceneType ? step->m_function->id() : Function::invalidId();
33✔
478
        }
479
        delete step;
33✔
480
    }
481
    m_runnerSteps.clear();
63✔
482
}
63✔
483

484
void ChaserRunner::startNewStep(int index, MasterTimer *timer, qreal mIntensity, qreal sIntensity,
112✔
485
                                int fadeControl, quint32 elapsed)
486
{
487
    if (m_chaser == NULL || m_chaser->stepsCount() == 0)
112✔
488
        return;
×
489

490
    if (index < 0 || index >= m_chaser->stepsCount())
112✔
491
        index = 0; // fallback to the first step
×
492

493
    ChaserStep step(m_chaser->steps().at(index));
112✔
494
    Function *func = m_doc->function(step.fid);
112✔
495
    if (func == NULL)
112✔
496
        return;
×
497

498
    ChaserRunnerStep *newStep = new ChaserRunnerStep();
112✔
499
    newStep->m_index = index;
112✔
500

501
    // check if blending between Scenes is needed
502
    if (m_lastFunctionID != Function::invalidId() &&
203✔
503
        func->type() == Function::SceneType)
91✔
504
    {
505
        Scene *scene = qobject_cast<Scene *>(func);
91✔
506
        scene->setBlendFunctionID(m_lastFunctionID);
91✔
507
    }
508

509
    // this happens only during crossfades
510
    if (m_runnerSteps.count())
112✔
511
    {
512
        ChaserRunnerStep *lastStep = m_runnerSteps.last();
×
513
        if (lastStep->m_function &&
×
514
            lastStep->m_function->type() == Function::SceneType &&
×
515
            func->type() == Function::SceneType)
×
516
        {
517
            Scene *lastScene = qobject_cast<Scene *>(lastStep->m_function);
×
518
            lastScene->setBlendFunctionID(Function::invalidId());
×
519
            Scene *scene = qobject_cast<Scene *>(func);
×
520
            scene->setBlendFunctionID(lastStep->m_function->id());
×
521
        }
522
    }
523

524
    switch (fadeControl)
112✔
525
    {
526
        case Chaser::FromFunction:
106✔
527
            newStep->m_fadeIn = stepFadeIn(index);
106✔
528
            newStep->m_fadeOut = stepFadeOut(index);
106✔
529
        break;
106✔
530
        case Chaser::Blended:
×
531
            newStep->m_fadeIn = stepFadeIn(index);
×
532
            newStep->m_fadeOut = stepFadeOut(index);
×
533
        break;
×
534
        case Chaser::Crossfade:
×
535
            newStep->m_fadeIn = 0;
×
536
            newStep->m_fadeOut = 0;
×
537
        break;
×
538
        case Chaser::BlendedCrossfade:
×
539
            newStep->m_fadeIn = 0;
×
540
            newStep->m_fadeOut = 0;
×
541
        break;
×
542
    }
543

544
    newStep->m_duration = stepDuration(index);
112✔
545

546
    if (m_startOffset != 0)
112✔
547
        newStep->m_elapsed = m_startOffset + MasterTimer::tick();
×
548
    else
549
        newStep->m_elapsed = MasterTimer::tick() + elapsed;
112✔
550
    newStep->m_elapsedBeats = 0; //(newStep->m_elapsed / timer->beatTimeDuration()) * 1000;
112✔
551

552
    m_startOffset = 0;
112✔
553

554
    newStep->m_function = func;
112✔
555

556
    if (m_chaser->type() == Function::SequenceType)
112✔
557
    {
558
        Scene *s = qobject_cast<Scene*>(func);
×
559
        // blind == true is a workaround to reuse the same scene
560
        // without messing up the previous values
561
        for (int i = 0; i < step.values.count(); i++)
×
562
            s->setValue(step.values.at(i), true);
×
563
    }
564

565
    qDebug() << "[ChaserRunner] Starting step" << index << "fade in" << newStep->m_fadeIn
224✔
566
             << "fade out" << newStep->m_fadeOut << "intensity" << mIntensity
112✔
567
             << "fadeMode" << fadeControl;
112✔
568

569
    // Set intensity before starting the function. Otherwise the intensity
570
    // might momentarily jump too high.
571
    if (func->type() == Function::SceneType)
112✔
572
    {
573
        Scene *scene = qobject_cast<Scene *>(func);
112✔
574
        newStep->m_intensityOverrideId = func->requestAttributeOverride(Function::Intensity, sIntensity);
112✔
575
        newStep->m_pIntensityOverrideId = scene->requestAttributeOverride(Scene::ParentIntensity, mIntensity);
112✔
576
        qDebug() << "[ChaserRunner] Set step intensity:" << sIntensity << ", master:" << mIntensity;
112✔
577
    }
578
    else
579
    {
580
        newStep->m_intensityOverrideId = func->requestAttributeOverride(Function::Intensity, mIntensity * sIntensity);
×
581
    }
582

583
    // Start the fire up !
584
    func->start(timer, functionParent(), 0, newStep->m_fadeIn, newStep->m_fadeOut,
112✔
585
                func->defaultSpeed(), m_chaser->tempoType());
112✔
586
    m_runnerSteps.append(newStep);
112✔
587
    m_roundTime->restart();
112✔
588
}
589

590
int ChaserRunner::getNextStepIndex()
110✔
591
{
592
    int currentStepIndex = m_lastRunStepIdx;
110✔
593

594
    if (m_chaser->runOrder() == Function::Random)
110✔
595
    {
596
        currentStepIndex = m_order.indexOf(currentStepIndex);
×
597
        if (currentStepIndex == -1)
×
598
        {
599
            qDebug() << "[ChaserRunner] steps order not found";
×
600
            currentStepIndex = m_lastRunStepIdx;
×
601
        }
602
    }
603

604
    if (currentStepIndex == -1 &&
127✔
605
        m_chaser->direction() == Function::Backward)
17✔
606
            currentStepIndex = m_chaser->stepsCount();
6✔
607

608
    // Handle reverse Ping Pong at boundaries
609
    if (m_chaser->runOrder() == Function::PingPong &&
138✔
610
        m_pendingAction.m_action == ChaserPreviousStep)
28✔
611
    {
612
        if (currentStepIndex == 0)
×
613
            m_direction = Function::Backward;
×
614
        else if (currentStepIndex == m_chaser->stepsCount() - 1)
×
615
            m_direction = Function::Forward;
×
616
    }
617

618
    // Next step
619
    if (m_direction == Function::Forward)
110✔
620
    {
621
        // "Previous" for a forward chaser is -1
622
        if (m_pendingAction.m_action == ChaserPreviousStep)
76✔
623
            currentStepIndex--;
10✔
624
        else
625
            currentStepIndex++;
66✔
626
    }
627
    else
628
    {
629
        // "Previous" for a backward scene is +1
630
        if (m_pendingAction.m_action == ChaserPreviousStep)
34✔
631
            currentStepIndex++;
×
632
        else
633
            currentStepIndex--;
34✔
634
    }
635

636
    if (currentStepIndex < m_chaser->stepsCount() && currentStepIndex >= 0)
110✔
637
    {
638
        if (m_chaser->runOrder() == Function::Random)
80✔
639
        {
640
            currentStepIndex = randomStepIndex(currentStepIndex);
×
641
        }
642
        return currentStepIndex; // In the middle of steps. No need to go any further.
80✔
643
    }
644

645
    if (m_chaser->runOrder() == Function::SingleShot)
30✔
646
    {
647
        return -1; // Forward or Backward SingleShot has been completed.
4✔
648
    }
649
    else if (m_chaser->runOrder() == Function::Loop)
26✔
650
    {
651
        if (m_direction == Function::Forward)
18✔
652
        {
653
            if (currentStepIndex >= m_chaser->stepsCount())
16✔
654
                currentStepIndex = 0;
12✔
655
            else
656
                currentStepIndex = m_chaser->stepsCount() - 1; // Used by CueList with manual prev
4✔
657
        }
658
        else // Backward
659
        {
660
            if (currentStepIndex < 0)
2✔
661
                currentStepIndex = m_chaser->stepsCount() - 1;
2✔
662
            else
663
                currentStepIndex = 0;
×
664
        }
665
    }
666
    else if (m_chaser->runOrder() == Function::Random)
8✔
667
    {
668
        fillOrder();
×
669
        if (m_direction == Function::Forward)
×
670
        {
671
            if (currentStepIndex >= m_chaser->stepsCount())
×
672
                currentStepIndex = 0;
×
673
            else
674
                currentStepIndex = m_chaser->stepsCount() - 1; // Used by CueList with manual prev
×
675
        }
676
        else // Backward
677
        {
678
            if (currentStepIndex < 0)
×
679
                currentStepIndex = m_chaser->stepsCount() - 1;
×
680
            else
681
                currentStepIndex = 0;
×
682
        }
683
        // Don't run the same function 2 times in a row
684
        while (currentStepIndex < m_chaser->stepsCount()
×
685
                && randomStepIndex(currentStepIndex) == m_lastRunStepIdx)
×
686
            ++currentStepIndex;
×
687
        currentStepIndex = randomStepIndex(currentStepIndex);
×
688
    }
689
    else // Ping Pong
690
    {
691
        // Change direction, but don't run the first/last step twice.
692
        if (m_direction == Function::Forward)
8✔
693
        {
694
            currentStepIndex = m_chaser->stepsCount() - 2;
4✔
695
            m_direction = Function::Backward;
4✔
696
        }
697
        else // Backwards
698
        {
699
            currentStepIndex = 1;
4✔
700
            m_direction = Function::Forward;
4✔
701
        }
702

703
        // Make sure we don't go beyond limits.
704
        currentStepIndex = CLAMP(currentStepIndex, 0, m_chaser->stepsCount() - 1);
8✔
705
    }
706

707
    return currentStepIndex;
26✔
708
}
709

710
void ChaserRunner::setPause(bool enable, QList<Universe *> universes)
2✔
711
{
712
    // Nothing to do
713
    if (m_chaser->stepsCount() == 0)
2✔
714
        return;
×
715

716
    qDebug() << "[ChaserRunner] processing pause request:" << enable;
2✔
717

718
    foreach(ChaserRunnerStep *step, m_runnerSteps)
6✔
719
        step->m_function->setPause(enable);
2✔
720

721
    // there might be a Scene fading out, so request pause
722
    // to faders bound to the Scene ID running on universes
723
    Function *f = m_doc->function(m_lastFunctionID);
2✔
724
    if (f != NULL && f->type() == Function::SceneType)
2✔
725
    {
726
        foreach (Universe *universe, universes)
18✔
727
            universe->setFaderPause(m_lastFunctionID, enable);
8✔
728
    }
729
}
730

731
FunctionParent ChaserRunner::functionParent() const
224✔
732
{
733
    return FunctionParent(FunctionParent::Function, m_chaser->id());
224✔
734
}
735

736
bool ChaserRunner::write(MasterTimer *timer, QList<Universe *> universes)
356✔
737
{
738
    // Nothing to do
739
    if (m_chaser->stepsCount() == 0)
356✔
740
        return false;
1✔
741

742
    switch (m_pendingAction.m_action)
355✔
743
    {
744
        case ChaserNextStep:
19✔
745
        case ChaserPreviousStep:
746
            clearRunningList();
19✔
747
            // the actual action will be performed below, on startNewStep
748
        break;
19✔
749
        case ChaserSetStepIndex:
7✔
750
            if (m_pendingAction.m_stepIndex != -1)
7✔
751
            {
752
                clearRunningList();
6✔
753
                if(m_chaser->runOrder() == Function::Random)
6✔
754
                {
755
                    m_lastRunStepIdx = randomStepIndex(m_pendingAction.m_stepIndex);
×
756
                }
757
                else
758
                {
759
                    m_lastRunStepIdx = m_pendingAction.m_stepIndex;
6✔
760
                }
761
                qDebug() << "[ChaserRunner] Starting from step" << m_lastRunStepIdx << "@ offset" << m_startOffset;
6✔
762
                startNewStep(m_lastRunStepIdx, timer, m_pendingAction.m_masterIntensity,
6✔
763
                             m_pendingAction.m_stepIntensity, m_pendingAction.m_fadeMode);
764
                emit currentStepChanged(m_lastRunStepIdx);
6✔
765
            }
766
        break;
7✔
767
        case ChaserPauseRequest:
2✔
768
            setPause(m_pendingAction.m_fadeMode ? true : false, universes);
2✔
769
        break;
2✔
770
        default:
327✔
771
        break;
327✔
772
    }
773

774
    quint32 prevStepRoundElapsed = 0;
355✔
775

776
    foreach(ChaserRunnerStep *step, m_runnerSteps)
993✔
777
    {
778
        if (m_chaser->tempoType() == Function::Beats && timer->isBeat())
319✔
779
        {
780
            step->m_elapsedBeats += 1000;
×
781
            qDebug() << "[ChaserRunner] Function" << step->m_function->name() << "duration:" << step->m_duration << "beats:" << step->m_elapsedBeats;
×
782
        }
783

784
        if (step->m_duration != Function::infiniteSpeed() &&
539✔
785
            ((m_chaser->tempoType() == Function::Time && step->m_elapsed >= step->m_duration) ||
220✔
786
             (m_chaser->tempoType() == Function::Beats && step->m_elapsedBeats >= step->m_duration)))
146✔
787
        {
788
            if (step->m_duration != 0)
74✔
789
                prevStepRoundElapsed = step->m_elapsed % step->m_duration;
29✔
790

791
            m_lastFunctionID = step->m_function->type() == Function::SceneType ? step->m_function->id() : Function::invalidId();
74✔
792
            step->m_function->stop(functionParent(), m_chaser->type() == Function::SequenceType);
74✔
793
            m_runnerSteps.removeOne(step);
74✔
794
            delete step;
74✔
795
        }
796
        else
797
        {
798
            if (step->m_elapsed < UINT_MAX)
245✔
799
                step->m_elapsed += MasterTimer::tick();
245✔
800

801
            // When the speeds of the chaser change, they need to be updated to the lower
802
            // level (only current function) as well. Otherwise the new speeds would take
803
            // effect only on the next step change.
804
            if (m_updateOverrideSpeeds == true)
245✔
805
            {
806
                m_updateOverrideSpeeds = false;
×
807
                if (step->m_function != NULL)
×
808
                {
809
                    step->m_function->setOverrideFadeInSpeed(step->m_fadeIn);
×
810
                    step->m_function->setOverrideFadeOutSpeed(step->m_fadeOut);
×
811
                }
812
            }
813
        }
814
    }
815

816
    if (m_runnerSteps.isEmpty())
355✔
817
    {
818
        m_lastRunStepIdx = getNextStepIndex();
110✔
819
        if (m_lastRunStepIdx != -1)
110✔
820
        {
821
            int blend = m_pendingAction.m_action == ChaserNoAction ? Chaser::FromFunction : m_pendingAction.m_fadeMode;
106✔
822

823
            startNewStep(m_lastRunStepIdx, timer, m_pendingAction.m_masterIntensity,
106✔
824
                         m_pendingAction.m_stepIntensity, blend, prevStepRoundElapsed);
825
            emit currentStepChanged(m_lastRunStepIdx);
106✔
826
        }
827
        else
828
        {
829
            m_pendingAction.m_action = ChaserNoAction;
4✔
830
            return false;
4✔
831
        }
832
    }
833

834
    m_pendingAction.m_action = ChaserNoAction;
351✔
835
    return true;
351✔
836
}
837

838
void ChaserRunner::postRun(MasterTimer *timer, QList<Universe*> universes)
8✔
839
{
840
    Q_UNUSED(universes);
841
    Q_UNUSED(timer);
842

843
    qDebug() << Q_FUNC_INFO;
8✔
844
    clearRunningList();
8✔
845
}
8✔
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