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

mcallegari / qlcplus / 7025156636

28 Nov 2023 10:11PM UTC coverage: 31.979% (-0.02%) from 32.001%
7025156636

push

github

mcallegari
qmlui: start to handle real beat-based Show

15114 of 47262 relevant lines covered (31.98%)

23507.96 hits per line

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

68.7
/engine/src/show.cpp
1
/*
2
  Q Light Controller Plus
3
  show.cpp
4

5
  Copyright (c) Massimo Callegari
6

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

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

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

20
#include <QXmlStreamReader>
21
#include <QXmlStreamWriter>
22
#include <QString>
23
#include <QDebug>
24
#include <QFile>
25
#include <QList>
26

27
#include "showrunner.h"
28
#include "function.h"
29
#include "show.h"
30
#include "doc.h"
31

32
#define KXMLQLCShowTimeDivision QString("TimeDivision")
33
#define KXMLQLCShowTimeType     QString("Type")
34
#define KXMLQLCShowTimeBPM      QString("BPM")
35

36
/*****************************************************************************
37
 * Initialization
38
 *****************************************************************************/
39

40
Show::Show(Doc* doc) : Function(doc, Function::ShowType)
8✔
41
  , m_timeDivisionType(Time)
42
  , m_timeDivisionBPM(120)
43
  , m_latestTrackId(0)
44
  , m_runner(NULL)
8✔
45
{
46
    setName(tr("New Show"));
8✔
47

48
    // Clear attributes here. I want attributes to be mapped
49
    // exactly like the Show tracks
50
    unregisterAttribute(tr("Intensity"));
8✔
51
}
8✔
52

53
Show::~Show()
8✔
54
{
55
    m_tracks.clear();
8✔
56
}
8✔
57

58
QIcon Show::getIcon() const
×
59
{
60
    return QIcon(":/show.png");
×
61
}
62

63
quint32 Show::totalDuration()
2✔
64
{
65
    quint32 totalDuration = 0;
2✔
66

67
    foreach(Track *track, tracks())
8✔
68
    {
69
        foreach(ShowFunction *sf, track->showFunctions())
8✔
70
        {
71
            if (sf->startTime() + sf->duration(doc()) > totalDuration)
2✔
72
                totalDuration = sf->startTime() + sf->duration(doc());
2✔
73
        }
74
    }
75

76
    return totalDuration;
2✔
77
}
78

79
/*****************************************************************************
80
 * Copying
81
 *****************************************************************************/
82

83
Function* Show::createCopy(Doc* doc, bool addToDoc)
×
84
{
85
    Q_ASSERT(doc != NULL);
×
86

87
    Function* copy = new Show(doc);
×
88
    if (copy->copyFrom(this) == false)
×
89
    {
90
        delete copy;
×
91
        copy = NULL;
×
92
    }
93
    if (addToDoc == true && doc->addFunction(copy) == false)
×
94
    {
95
        delete copy;
×
96
        copy = NULL;
×
97
    }
98

99
    return copy;
×
100
}
101

102
bool Show::copyFrom(const Function* function)
1✔
103
{
104
    const Show* show = qobject_cast<const Show*> (function);
1✔
105
    if (show == NULL)
1✔
106
        return false;
×
107

108
    m_timeDivisionType = show->m_timeDivisionType;
1✔
109
    m_timeDivisionBPM = show->m_timeDivisionBPM;
1✔
110
    m_latestTrackId = show->m_latestTrackId;
1✔
111

112
    // create a copy of each track
113
    foreach(Track *track, show->tracks())
4✔
114
    {
115
        quint32 sceneID = track->getSceneID();
1✔
116
        Track* newTrack = new Track(sceneID);
1✔
117
        newTrack->setName(track->name());
1✔
118
        addTrack(newTrack);
1✔
119

120
        // create a copy of each sequence/audio in a track
121
        foreach(ShowFunction *sfunc, track->showFunctions())
4✔
122
        {
123
            Function* function = doc()->function(sfunc->functionID());
1✔
124
            if (function == NULL)
1✔
125
                continue;
×
126

127
            /* Attempt to create a copy of the function to Doc */
128
            Function* copy = function->createCopy(doc());
1✔
129
            if (copy != NULL)
1✔
130
            {
131
                copy->setName(tr("Copy of %1").arg(function->name()));
1✔
132
                ShowFunction *showFunc = newTrack->createShowFunction(copy->id());
1✔
133
                showFunc->setStartTime(sfunc->startTime());
1✔
134
                showFunc->setDuration(sfunc->duration());
1✔
135
                showFunc->setColor(sfunc->color());
1✔
136
                showFunc->setLocked(sfunc->isLocked());
1✔
137
            }
138
        }
139
    }
140

141
    return Function::copyFrom(function);
1✔
142
}
143

144
/*********************************************************************
145
 * Time division
146
 *********************************************************************/
147

148
void Show::setTimeDivision(Show::TimeDivision type, int BPM)
4✔
149
{
150
    qDebug() << "[setTimeDivision] type:" << type << ", BPM:" << BPM;
4✔
151
    m_timeDivisionType = type;
4✔
152
    m_timeDivisionBPM = BPM;
4✔
153
}
4✔
154

155
Show::TimeDivision Show::timeDivisionType()
4✔
156
{
157
    return m_timeDivisionType;
4✔
158
}
159

160
int Show::beatsDivision()
4✔
161
{
162
    switch(m_timeDivisionType)
4✔
163
    {
164
        case BPM_2_4: return 2;
1✔
165
        case BPM_3_4: return 3;
1✔
166
        case BPM_4_4: return 4;
1✔
167
        default: return 0;
1✔
168
    }
169
}
170

171
void Show::setTimeDivisionType(TimeDivision type)
3✔
172
{
173
    m_timeDivisionType = type;
3✔
174
}
3✔
175

176
int Show::timeDivisionBPM()
4✔
177
{
178
    return m_timeDivisionBPM;
4✔
179
}
180

181
void Show::setTimeDivisionBPM(int BPM)
×
182
{
183
    m_timeDivisionBPM = BPM;
×
184
}
×
185

186
QString Show::tempoToString(Show::TimeDivision type)
5✔
187
{
188
    switch(type)
5✔
189
    {
190
        case Time: return QString("Time"); break;
1✔
191
        case BPM_4_4: return QString("BPM_4_4"); break;
1✔
192
        case BPM_3_4: return QString("BPM_3_4"); break;
2✔
193
        case BPM_2_4: return QString("BPM_2_4"); break;
1✔
194
        case Invalid:
×
195
        default:
196
            return QString("Invalid"); break;
×
197
    }
198
    return QString();
199
}
200

201
Show::TimeDivision Show::stringToTempo(QString tempo)
5✔
202
{
203
    if (tempo == "Time")
5✔
204
        return Time;
1✔
205
    else if (tempo == "BPM_4_4")
4✔
206
        return BPM_4_4;
1✔
207
    else if (tempo == "BPM_3_4")
3✔
208
        return BPM_3_4;
1✔
209
    else if (tempo == "BPM_2_4")
2✔
210
        return BPM_2_4;
2✔
211
    else
212
        return Invalid;
×
213
}
214

215
/*****************************************************************************
216
 * Tracks
217
 *****************************************************************************/
218

219
bool Show::addTrack(Track *track, quint32 id)
9✔
220
{
221
    Q_ASSERT(track != NULL);
9✔
222

223
    // No ID given, this method can assign one
224
    if (id == Track::invalidId())
9✔
225
        id = createTrackId();
7✔
226

227
     track->setId(id);
9✔
228
     track->setShowId(this->id());
9✔
229
     m_tracks[id] = track;
9✔
230

231
     registerAttribute(track->name());
9✔
232

233
     return true;
9✔
234
}
235

236
bool Show::removeTrack(quint32 id)
3✔
237
{
238
    if (m_tracks.contains(id) == true)
3✔
239
    {
240
        Track* trk = m_tracks.take(id);
2✔
241
        Q_ASSERT(trk != NULL);
2✔
242

243
        unregisterAttribute(trk->name());
2✔
244

245
        //emit trackRemoved(id);
246
        delete trk;
2✔
247

248
        return true;
2✔
249
    }
250
    else
251
    {
252
        qWarning() << Q_FUNC_INFO << "No track found with id" << id;
1✔
253
        return false;
1✔
254
    }
255
}
256

257
Track* Show::track(quint32 id) const
5✔
258
{
259
    if (m_tracks.contains(id) == true)
5✔
260
        return m_tracks[id];
3✔
261
    else
262
        return NULL;
2✔
263
}
264

265
Track* Show::getTrackFromSceneID(quint32 id)
2✔
266
{
267
    foreach(Track *track, m_tracks)
4✔
268
    {
269
        if (track->getSceneID() == id)
2✔
270
            return track;
1✔
271
    }
272
    return NULL;
1✔
273
}
274

275
int Show::getTracksCount()
5✔
276
{
277
    return m_tracks.size();
5✔
278
}
279

280
void Show::moveTrack(Track *track, int direction)
3✔
281
{
282
    if (track == NULL)
3✔
283
        return;
×
284

285
    qint32 trkID = track->id();
3✔
286
    if (trkID == 0 && direction == -1)
3✔
287
        return;
1✔
288
    qint32 maxID = -1;
2✔
289
    Track *swapTrack = NULL;
2✔
290
    qint32 swapID = -1;
2✔
291
    if (direction > 0) swapID = INT_MAX;
2✔
292

293
    foreach(quint32 id, m_tracks.keys())
12✔
294
    {
295
        qint32 signedID = (qint32)id;
4✔
296
        if (signedID > maxID) maxID = signedID;
4✔
297
        if (direction == -1 && signedID > swapID && signedID < trkID)
4✔
298
            swapID = signedID;
2✔
299
        else if (direction == 1 && signedID < swapID && signedID > trkID)
2✔
300
            swapID = signedID;
×
301
    }
302

303
    qDebug() << Q_FUNC_INFO << "Direction:" << direction << ", trackID:" << trkID << ", swapID:" << swapID;
2✔
304
    if (swapID == trkID || (direction > 0 && trkID == maxID))
2✔
305
        return;
×
306

307
    swapTrack = m_tracks[swapID];
2✔
308
    m_tracks[swapID] = track;
2✔
309
    m_tracks[trkID] = swapTrack;
2✔
310
    track->setId(swapID);
2✔
311
    swapTrack->setId(trkID);
2✔
312
}
313

314
QList <Track*> Show::tracks() const
15✔
315
{
316
    return m_tracks.values();
15✔
317
}
318

319
quint32 Show::createTrackId()
9✔
320
{
321
    while (m_tracks.contains(m_latestTrackId) == true ||
16✔
322
           m_latestTrackId == Track::invalidId())
7✔
323
    {
324
        m_latestTrackId++;
2✔
325
    }
326

327
    return m_latestTrackId;
7✔
328
}
329

330
/*****************************************************************************
331
 * Load & Save
332
 *****************************************************************************/
333

334
bool Show::saveXML(QXmlStreamWriter *doc)
1✔
335
{
336
    Q_ASSERT(doc != NULL);
1✔
337

338
    /* Function tag */
339
    doc->writeStartElement(KXMLQLCFunction);
1✔
340

341
    /* Common attributes */
342
    saveXMLCommon(doc);
1✔
343

344
    doc->writeStartElement(KXMLQLCShowTimeDivision);
1✔
345
    doc->writeAttribute(KXMLQLCShowTimeType, tempoToString(m_timeDivisionType));
1✔
346
    doc->writeAttribute(KXMLQLCShowTimeBPM, QString::number(m_timeDivisionBPM));
1✔
347
    doc->writeEndElement();
1✔
348

349
    foreach(Track *track, m_tracks)
5✔
350
        track->saveXML(doc);
2✔
351

352
    /* End the <Function> tag */
353
    doc->writeEndElement();
1✔
354

355
    return true;
1✔
356
}
357

358
bool Show::loadXML(QXmlStreamReader &root)
1✔
359
{
360
    if (root.name() != KXMLQLCFunction)
1✔
361
    {
362
        qWarning() << Q_FUNC_INFO << "Function node not found";
×
363
        return false;
×
364
    }
365

366
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::ShowType))
1✔
367
    {
368
        qWarning() << Q_FUNC_INFO << root.attributes().value(KXMLQLCFunctionType).toString()
×
369
                   << "is not a show";
×
370
        return false;
×
371
    }
372

373
    while (root.readNextStartElement())
4✔
374
    {
375
        if (root.name() == KXMLQLCShowTimeDivision)
3✔
376
        {
377
            QString type = root.attributes().value(KXMLQLCShowTimeType).toString();
3✔
378
            int bpm = root.attributes().value(KXMLQLCShowTimeBPM).toString().toInt();
1✔
379
            setTimeDivision(stringToTempo(type), bpm);
1✔
380
            root.skipCurrentElement();
1✔
381
        }
382
        else if (root.name() == KXMLQLCTrack)
2✔
383
        {
384
            Track *trk = new Track();
2✔
385
            if (trk->loadXML(root) == true)
2✔
386
                addTrack(trk, trk->id());
2✔
387
        }
388
        else
389
        {
390
            qWarning() << Q_FUNC_INFO << "Unknown Show tag:" << root.name();
×
391
            root.skipCurrentElement();
×
392
        }
393
    }
394

395
    return true;
1✔
396
}
397

398
void Show::postLoad()
×
399
{
400
    foreach (Track* track, m_tracks)
×
401
    {
402
        if (track->postLoad(doc()))
×
403
            doc()->setModified();
×
404
    }
405
}
×
406

407
bool Show::contains(quint32 functionId)
3✔
408
{
409
    Doc *doc = this->doc();
3✔
410
    Q_ASSERT(doc != NULL);
3✔
411

412
    if (functionId == id())
3✔
413
        return true;
1✔
414

415
    foreach (Track* track, m_tracks)
4✔
416
    {
417
        if (track->contains(doc, functionId))
2✔
418
            return true;
1✔
419
    }
420

421
    return false;
1✔
422
}
423

424
QList<quint32> Show::components()
1✔
425
{
426
    QList<quint32> ids;
1✔
427

428
    foreach (Track* track, m_tracks)
3✔
429
        ids.append(track->components());
1✔
430

431
    return ids;
1✔
432
}
433

434
/*****************************************************************************
435
 * Running
436
 *****************************************************************************/
437

438
void Show::preRun(MasterTimer* timer)
×
439
{
440
    Function::preRun(timer);
×
441
    m_runningChildren.clear();
×
442
    if (m_runner != NULL)
×
443
    {
444
        m_runner->stop();
×
445
        delete m_runner;
×
446
    }
447

448
    m_runner = new ShowRunner(doc(), this->id(), elapsed());
×
449
    int i = 0;
×
450
    foreach(Track *track, m_tracks.values())
×
451
        m_runner->adjustIntensity(getAttributeValue(i++), track);
×
452

453
    connect(m_runner, SIGNAL(timeChanged(quint32)), this, SIGNAL(timeChanged(quint32)));
×
454
    connect(m_runner, SIGNAL(showFinished()), this, SIGNAL(showFinished()));
×
455
    m_runner->start();
×
456
}
×
457

458
void Show::setPause(bool enable)
×
459
{
460
    if (m_runner != NULL)
×
461
        m_runner->setPause(enable);
×
462
    Function::setPause(enable);
×
463
}
×
464

465
void Show::write(MasterTimer* timer, QList<Universe *> universes)
×
466
{
467
    Q_UNUSED(universes);
468

469
    if (isPaused())
×
470
        return;
×
471

472
    m_runner->write(timer);
×
473
}
474

475
void Show::postRun(MasterTimer* timer, QList<Universe *> universes)
×
476
{
477
    if (m_runner != NULL)
×
478
    {
479
        m_runner->stop();
×
480
        delete m_runner;
×
481
        m_runner = NULL;
×
482
    }
483
    Function::postRun(timer, universes);
×
484
}
×
485

486
void Show::slotChildStopped(quint32 fid)
×
487
{
488
    Q_UNUSED(fid);
489
}
×
490

491
/*****************************************************************************
492
 * Attributes
493
 *****************************************************************************/
494

495
int Show::adjustAttribute(qreal fraction, int attributeId)
×
496
{
497
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
×
498

499
    if (m_runner != NULL)
×
500
    {
501
        QList<Track*> trkList = m_tracks.values();
×
502
        if (trkList.isEmpty() == false &&
×
503
            attrIndex >= 0 && attrIndex < trkList.count())
×
504
        {
505
            Track *track = trkList.at(attrIndex);
×
506
            if (track != NULL)
×
507
                m_runner->adjustIntensity(getAttributeValue(attrIndex), track);
×
508
        }
509
    }
510

511
    return attrIndex;
×
512
}
513

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