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

mcallegari / qlcplus / 19144422256

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

push

github

mcallegari
Back to 5.1.0 debug

17718 of 51723 relevant lines covered (34.26%)

19528.23 hits per line

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

65.9
/engine/src/inputoutputmap.cpp
1
/*
2
  Q Light Controller Plus
3
  inputoutputmap.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
#if defined(WIN32) || defined(Q_OS_WIN)
21
#        include <Windows.h>
22
#else
23
#   include <unistd.h>
24
#endif
25

26
#include <QXmlStreamReader>
27
#include <QXmlStreamWriter>
28
#include <QElapsedTimer>
29
#include <QSettings>
30
#include <QDebug>
31
#include <qmath.h>
32

33
#include "inputoutputmap.h"
34
#include "qlcinputchannel.h"
35
#include "qlcinputsource.h"
36
#include "qlcioplugin.h"
37
#include "outputpatch.h"
38
#include "inputpatch.h"
39
#include "qlcconfig.h"
40
#include "universe.h"
41
#include "qlcfile.h"
42
#include "doc.h"
43

44
InputOutputMap::InputOutputMap(const Doc *doc, quint32 universes)
219✔
45
    : QObject(NULL)
46
    , m_doc(doc)
219✔
47
    , m_blackout(false)
219✔
48
    , m_universeChanged(false)
219✔
49
    , m_localProfilesLoaded(false)
219✔
50
    , m_currentBPM(0)
219✔
51
    , m_beatTime(new QElapsedTimer())
219✔
52
{
53
    m_grandMaster = new GrandMaster(this);
219✔
54
    for (quint32 i = 0; i < universes; i++)
1,095✔
55
        addUniverse();
876✔
56

57
    connect(doc->ioPluginCache(), SIGNAL(pluginConfigurationChanged(QLCIOPlugin*)),
219✔
58
            this, SLOT(slotPluginConfigurationChanged(QLCIOPlugin*)));
59
    connect(doc->masterTimer(), SIGNAL(beat()), this, SLOT(slotMasterTimerBeat()));
219✔
60
}
219✔
61

62
InputOutputMap::~InputOutputMap()
415✔
63
{
64
    removeAllUniverses();
219✔
65
    delete m_grandMaster;
219✔
66
    delete m_beatTime;
219✔
67
    qDeleteAll(m_profiles);
219✔
68
}
415✔
69

70
/*****************************************************************************
71
 * Blackout
72
 *****************************************************************************/
73

74
bool InputOutputMap::toggleBlackout()
4✔
75
{
76
    if (m_blackout == true)
4✔
77
        setBlackout(false);
2✔
78
    else
79
        setBlackout(true);
2✔
80

81
    return m_blackout;
4✔
82
}
83

84
bool InputOutputMap::setBlackout(bool blackout)
7✔
85
{
86
    /* Don't do blackout twice */
87
    if (m_blackout == blackout)
7✔
88
        return false;
2✔
89

90
    m_blackout = blackout;
5✔
91

92
    // blackout is an atomic setting, so it's safe to do it
93
    // without mutex locking
94
    foreach (Universe *universe, m_universeArray)
25✔
95
    {
96
        for (int i = 0; i < universe->outputPatchesCount(); i++)
32✔
97
        {
98
            OutputPatch *op = universe->outputPatch(i);
12✔
99
            if (op != NULL)
12✔
100
                op->setBlackout(blackout);
12✔
101
        }
102

103
        const QByteArray postGM = universe->postGMValues()->mid(0, universe->usedChannels());
20✔
104
        universe->dumpOutput(postGM, true);
20✔
105
    }
25✔
106

107
    emit blackoutChanged(m_blackout);
5✔
108

109
    return true;
5✔
110
}
111

112
void InputOutputMap::requestBlackout(BlackoutRequest blackout)
×
113
{
114
    if (blackout != BlackoutRequestNone)
×
115
        setBlackout(blackout == BlackoutRequestOn ? true : false);
×
116
}
×
117

118
bool InputOutputMap::blackout() const
9✔
119
{
120
    return m_blackout;
9✔
121
}
122

123
/*****************************************************************************
124
 * Universes
125
 *****************************************************************************/
126

127
quint32 InputOutputMap::invalidUniverse()
2,092✔
128
{
129
    return UINT_MAX;
2,092✔
130
}
131

132
bool InputOutputMap::addUniverse(quint32 id)
879✔
133
{
134
    {
135
        QMutexLocker locker(&m_universeMutex);
879✔
136
        Universe *uni = NULL;
879✔
137

138
        if (id == InputOutputMap::invalidUniverse())
879✔
139
        {
140
            id = universesCount();
877✔
141
        }
142
        else if (id < universesCount())
2✔
143
        {
144
            qWarning() << Q_FUNC_INFO
2✔
145
                << "Universe" << id << "is already present in the list."
1✔
146
                << "The universe list may be unsorted.";
1✔
147
            return false;
1✔
148
        }
149
        else if (id > universesCount())
1✔
150
        {
151
            qDebug() << Q_FUNC_INFO
2✔
152
                << "Gap between universe" << (universesCount() - 1)
1✔
153
                << "and universe" << id << ", filling the gap...";
1✔
154
            while (id > universesCount())
4✔
155
            {
156
                uni = new Universe(universesCount(), m_grandMaster);
3✔
157
                connect(m_doc->masterTimer(), SIGNAL(tickReady()), uni, SLOT(tick()), Qt::QueuedConnection);
3✔
158
                connect(uni, SIGNAL(universeWritten(quint32,QByteArray)), this, SIGNAL(universeWritten(quint32,QByteArray)));
3✔
159
                m_universeArray.append(uni);
3✔
160
            }
161
        }
162

163
        uni = new Universe(id, m_grandMaster);
878✔
164
        connect(m_doc->masterTimer(), SIGNAL(tickReady()), uni, SLOT(tick()), Qt::QueuedConnection);
878✔
165
        connect(uni, SIGNAL(universeWritten(quint32,QByteArray)), this, SIGNAL(universeWritten(quint32,QByteArray)));
878✔
166
        m_universeArray.append(uni);
878✔
167
    }
879✔
168

169
    emit universeAdded(id);
878✔
170
    return true;
878✔
171
}
172

173
bool InputOutputMap::removeUniverse(int index)
3✔
174
{
175
    {
176
        QMutexLocker locker(&m_universeMutex);
3✔
177

178
        if (index < 0 || index >= m_universeArray.count())
3✔
179
            return false;
1✔
180

181
        if (index != (m_universeArray.size() - 1))
2✔
182
        {
183
            qWarning() << Q_FUNC_INFO << "Removing universe" << index
2✔
184
                << "would create a gap in the universe list, cancelling";
1✔
185
            return false;
1✔
186
        }
187

188
        delete  m_universeArray.takeAt(index);
1✔
189
    }
3✔
190

191
    emit universeRemoved(index);
1✔
192
    return true;
1✔
193
}
194

195
bool InputOutputMap::removeAllUniverses()
220✔
196
{
197
    QMutexLocker locker(&m_universeMutex);
220✔
198
    qDeleteAll(m_universeArray);
220✔
199
    m_universeArray.clear();
220✔
200
    return true;
220✔
201
}
220✔
202

203
void InputOutputMap::startUniverses()
×
204
{
205
    foreach (Universe *uni, m_universeArray)
×
206
        uni->start();
×
207
}
×
208

209
quint32 InputOutputMap::getUniverseID(int index)
2✔
210
{
211
    if (index >= 0 && index < m_universeArray.count())
2✔
212
        return index;
1✔
213

214
    return invalidUniverse();
1✔
215
}
216

217
QString InputOutputMap::getUniverseNameByIndex(int index)
5✔
218
{
219
    if (index >= 0 && index < m_universeArray.count())
5✔
220
        return m_universeArray.at(index)->name();
4✔
221

222
    return QString();
1✔
223
}
224

225
QString InputOutputMap::getUniverseNameByID(quint32 id)
2✔
226
{
227
    return getUniverseNameByIndex(id);
2✔
228
}
229

230
void InputOutputMap::setUniverseName(int index, QString name)
2✔
231
{
232
    if (index < 0 || index >= m_universeArray.count())
2✔
233
        return;
1✔
234
    m_universeArray.at(index)->setName(name);
1✔
235
}
236

237
void InputOutputMap::setUniversePassthrough(int index, bool enable)
2✔
238
{
239
    if (index < 0 || index >= m_universeArray.count())
2✔
240
        return;
1✔
241
    m_universeArray.at(index)->setPassthrough(enable);
1✔
242
}
243

244
bool InputOutputMap::getUniversePassthrough(int index)
2✔
245
{
246
    if (index < 0 || index >= m_universeArray.count())
2✔
247
        return false;
1✔
248
    return m_universeArray.at(index)->passthrough();
1✔
249
}
250

251
void InputOutputMap::setUniverseMonitor(int index, bool enable)
2✔
252
{
253
    if (index < 0 || index >= m_universeArray.count())
2✔
254
        return;
1✔
255
    m_universeArray.at(index)->setMonitor(enable);
1✔
256
}
257

258
bool InputOutputMap::getUniverseMonitor(int index)
2✔
259
{
260
    if (index < 0 || index >= m_universeArray.count())
2✔
261
        return false;
1✔
262
    return m_universeArray.at(index)->monitor();
1✔
263
}
264

265
bool InputOutputMap::isUniversePatched(int index)
3✔
266
{
267
    if (index < 0 || index >= m_universeArray.count())
3✔
268
        return false;
1✔
269

270
    return m_universeArray.at(index)->isPatched();
2✔
271
}
272

273
quint32 InputOutputMap::universesCount() const
1,685✔
274
{
275
    return (quint32)m_universeArray.count();
1,685✔
276
}
277

278
QList<Universe *> InputOutputMap::universes() const
44✔
279
{
280
    return m_universeArray;
44✔
281
}
282

283
Universe *InputOutputMap::universe(quint32 id)
×
284
{
285
    for (int i = 0; i < m_universeArray.size(); i++)
×
286
        if (m_universeArray.at(i)->id() == id)
×
287
            return m_universeArray.at(i);
×
288

289
    return NULL;
×
290
}
291

292
QList<Universe*> InputOutputMap::claimUniverses()
1,055✔
293
{
294
    m_universeMutex.lock();
1,055✔
295
    return m_universeArray;
1,055✔
296
}
297

298
void InputOutputMap::releaseUniverses(bool changed)
1,055✔
299
{
300
    m_universeChanged = changed;
1,055✔
301
    m_universeMutex.unlock();
1,055✔
302
}
1,055✔
303

304
void InputOutputMap::resetUniverses()
1✔
305
{
306
    {
307
        QMutexLocker locker(&m_universeMutex);
1✔
308
        for (int i = 0; i < m_universeArray.size(); i++)
5✔
309
            m_universeArray.at(i)->reset();
4✔
310
    }
1✔
311

312
    /* Reset Grand Master parameters */
313
    setGrandMasterValue(255);
1✔
314
    setGrandMasterValueMode(GrandMaster::Reduce);
1✔
315
    setGrandMasterChannelMode(GrandMaster::Intensity);
1✔
316

317
    m_localProfilesLoaded = false;
1✔
318
}
1✔
319

320
/*********************************************************************
321
 * Grand Master
322
 *********************************************************************/
323

324
void InputOutputMap::setGrandMasterChannelMode(GrandMaster::ChannelMode mode)
2✔
325
{
326
    Q_ASSERT(m_grandMaster != NULL);
2✔
327

328
    if (m_grandMaster->channelMode() != mode)
2✔
329
    {
330
        m_grandMaster->setChannelMode(mode);
1✔
331
        m_universeChanged = true;
1✔
332
    }
333
}
2✔
334

335
GrandMaster::ChannelMode InputOutputMap::grandMasterChannelMode()
78✔
336
{
337
    Q_ASSERT(m_grandMaster != NULL);
78✔
338

339
    return m_grandMaster->channelMode();
78✔
340
}
341

342
void InputOutputMap::setGrandMasterValueMode(GrandMaster::ValueMode mode)
2✔
343
{
344
    Q_ASSERT(m_grandMaster != NULL);
2✔
345

346
    if (m_grandMaster->valueMode() != mode)
2✔
347
    {
348
        m_grandMaster->setValueMode(mode);
1✔
349
        m_universeChanged = true;
1✔
350
    }
351

352
    emit grandMasterValueModeChanged(mode);
2✔
353
}
2✔
354

355
GrandMaster::ValueMode InputOutputMap::grandMasterValueMode()
154✔
356
{
357
    Q_ASSERT(m_grandMaster != NULL);
154✔
358

359
    return m_grandMaster->valueMode();
154✔
360
}
361

362
void InputOutputMap::setGrandMasterValue(uchar value)
2✔
363
{
364
    Q_ASSERT(m_grandMaster != NULL);
2✔
365

366
    if (m_grandMaster->value() != value)
2✔
367
    {
368
        m_grandMaster->setValue(value);
1✔
369
        m_universeChanged = true;
1✔
370
    }
371

372
    if (m_universeChanged == true)
2✔
373
        emit grandMasterValueChanged(value);
2✔
374
}
2✔
375

376
uchar InputOutputMap::grandMasterValue()
2✔
377
{
378
    Q_ASSERT(m_grandMaster != NULL);
2✔
379

380
    return m_grandMaster->value();
2✔
381
}
382

383
/*********************************************************************
384
 * Patch
385
 *********************************************************************/
386

387
void InputOutputMap::flushInputs()
5✔
388
{
389
    QMutexLocker locker(&m_universeMutex);
5✔
390
    foreach (Universe *universe, m_universeArray)
25✔
391
        universe->flushInput();
25✔
392
}
5✔
393

394
bool InputOutputMap::setInputPatch(quint32 universe, const QString &pluginName,
7✔
395
                                   const QString &inputUID, quint32 input,
396
                                   const QString &profileName)
397
{
398
    /* Check that the universe that we're doing mapping for is valid */
399
    if (universe >= universesCount())
7✔
400
    {
401
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
1✔
402
        return false;
1✔
403
    }
404

405
    QMutexLocker locker(&m_universeMutex);
6✔
406
    InputPatch *currInPatch = m_universeArray.at(universe)->inputPatch();
6✔
407
    QLCInputProfile *currProfile = NULL;
6✔
408
    if (currInPatch != NULL)
6✔
409
    {
410
        currProfile = currInPatch->profile();
1✔
411
        disconnect(currInPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
1✔
412
                this, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)));
413
        if (currInPatch->plugin()->capabilities() & QLCIOPlugin::Beats)
1✔
414
        {
415
            disconnect(currInPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
416
                       this, SLOT(slotPluginBeat(quint32,quint32,uchar,const QString&)));
417
        }
418
    }
419
    InputPatch *ip = NULL;
6✔
420
    QLCIOPlugin *plugin = m_doc->ioPluginCache()->plugin(pluginName);
6✔
421

422
    if (!inputUID.isEmpty() && plugin != NULL)
6✔
423
    {
424
        QStringList inputs = plugin->inputs();
5✔
425
        int lIdx = inputs.indexOf(inputUID);
5✔
426
        if (lIdx != -1)
5✔
427
        {
428
            qDebug() << "[IOMAP] Found match on input by name on universe" << universe << "-" << input << "vs" << lIdx;
5✔
429
            input = lIdx;
5✔
430
        }
431
        else
432
        {
433
            qDebug() << "[IOMAP] !!No match found!! for input on universe" << universe << "-" << input << inputUID;
×
434
            qDebug() << plugin->inputs();
×
435
        }
436
    }
5✔
437

438
    if (m_universeArray.at(universe)->setInputPatch(
6✔
439
                plugin, input, profile(profileName)) == true)
6✔
440
    {
441
        ip = m_universeArray.at(universe)->inputPatch();
6✔
442
        if (ip != NULL)
6✔
443
        {
444
            connect(ip, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
5✔
445
                    this, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)));
446
            if (ip->plugin()->capabilities() & QLCIOPlugin::Beats)
5✔
447
            {
448
                connect(ip, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
449
                        this, SLOT(slotPluginBeat(quint32,quint32,uchar,const QString&)));
450
            }
451
        }
452
    }
453
    else
454
    {
455
        return false;
×
456
    }
457

458
    if (ip != NULL && currProfile != ip->profile())
6✔
459
        emit profileChanged(universe, ip->profileName());
3✔
460

461
    return true;
6✔
462
}
6✔
463

464
bool InputOutputMap::setInputProfile(quint32 universe, const QString &profileName)
×
465
{
466
    /* Check that the universe that we're doing mapping for is valid */
467
    if (universe >= universesCount())
×
468
    {
469
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
×
470
        return false;
×
471
    }
472

473
    InputPatch *currInPatch = m_universeArray.at(universe)->inputPatch();
×
474
    if (currInPatch != NULL)
×
475
        currInPatch->set(profile(profileName));
×
476

477
    /* if no input patch is set, then setting a profile is useless,
478
       but there's no reason to cause an error here */
479
    return true;
×
480
}
481

482
bool InputOutputMap::setOutputPatch(quint32 universe, const QString &pluginName,
21✔
483
                                    const QString &outputUID, quint32 output,
484
                                    bool isFeedback, int index)
485
{
486
    /* Check that the universe that we're doing mapping for is valid */
487
    if (universe >= universesCount())
21✔
488
    {
489
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
2✔
490
        return false;
2✔
491
    }
492

493
    QMutexLocker locker(&m_universeMutex);
19✔
494
    QLCIOPlugin *plugin = m_doc->ioPluginCache()->plugin(pluginName);
19✔
495

496
    if (!outputUID.isEmpty() && plugin != NULL)
19✔
497
    {
498
        QStringList inputs = plugin->outputs();
12✔
499
        int lIdx = inputs.indexOf(outputUID);
12✔
500
        if (lIdx != -1)
12✔
501
        {
502
            qDebug() << "[IOMAP] Found match on output by name on universe" << universe << "-" << output << "vs" << lIdx;
12✔
503
            output = lIdx;
12✔
504
        }
505
        else
506
        {
507
            qDebug() << "[IOMAP] !!No match found!! for output on universe" << universe << "-" << output << outputUID;
×
508
            qDebug() << plugin->outputs();
×
509
        }
510
    }
12✔
511

512
    if (isFeedback == false)
19✔
513
        return m_universeArray.at(universe)->setOutputPatch(plugin, output, index);
19✔
514
    else
515
        return m_universeArray.at(universe)->setFeedbackPatch(plugin, output);
×
516

517
    return false;
518
}
19✔
519

520
int InputOutputMap::outputPatchesCount(quint32 universe) const
4✔
521
{
522
    if (universe >= universesCount())
4✔
523
    {
524
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
×
525
        return 0;
×
526
    }
527

528
    return m_universeArray.at(universe)->outputPatchesCount();
4✔
529
}
530

531
InputPatch *InputOutputMap::inputPatch(quint32 universe) const
56✔
532
{
533
    if (universe >= universesCount())
56✔
534
    {
535
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
7✔
536
        return NULL;
7✔
537
    }
538
    return m_universeArray.at(universe)->inputPatch();
49✔
539
}
540

541
OutputPatch *InputOutputMap::outputPatch(quint32 universe, int index) const
27✔
542
{
543
    if (universe >= universesCount())
27✔
544
    {
545
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
×
546
        return NULL;
×
547
    }
548
    return m_universeArray.at(universe)->outputPatch(index);
27✔
549
}
550

551
OutputPatch *InputOutputMap::feedbackPatch(quint32 universe) const
2✔
552
{
553
    if (universe >= universesCount())
2✔
554
    {
555
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
1✔
556
        return NULL;
1✔
557
    }
558
    return m_universeArray.at(universe)->feedbackPatch();
1✔
559
}
560

561
QStringList InputOutputMap::universeNames() const
23✔
562
{
563
    QStringList list;
23✔
564
    for (quint32 i = 0; i < universesCount(); i++)
115✔
565
        list << m_universeArray.at(i)->name();
92✔
566

567
    return list;
23✔
568
}
×
569

570
quint32 InputOutputMap::inputMapping(const QString &pluginName, quint32 input) const
16✔
571
{
572
    for (quint32 uni = 0; uni < universesCount(); uni++)
70✔
573
    {
574
        const InputPatch* p = m_universeArray.at(uni)->inputPatch();
57✔
575
        if (p != NULL && p->pluginName() == pluginName && p->input() == input)
57✔
576
            return uni;
3✔
577
    }
578

579
    return QLCIOPlugin::invalidLine();
13✔
580
}
581

582
quint32 InputOutputMap::outputMapping(const QString &pluginName, quint32 output) const
2✔
583
{
584
    for (quint32 uni = 0; uni < universesCount(); uni++)
9✔
585
    {
586
        Universe *universe = m_universeArray.at(uni);
8✔
587
        for (int i = 0; i < universe->outputPatchesCount(); i++)
15✔
588
        {
589
            const OutputPatch* p = universe->outputPatch(i);
8✔
590
            if (p != NULL && p->pluginName() == pluginName && p->output() == output)
8✔
591
                return uni;
1✔
592
        }
593
    }
594

595
    return QLCIOPlugin::invalidLine();
1✔
596
}
597

598
/*****************************************************************************
599
 * Plugins
600
 *****************************************************************************/
601

602
QString InputOutputMap::pluginDescription(const QString &pluginName)
2✔
603
{
604
    QLCIOPlugin* plugin = NULL;
2✔
605

606
    if (pluginName.isEmpty() == false)
2✔
607
        plugin = m_doc->ioPluginCache()->plugin(pluginName);
2✔
608

609
    if (plugin != NULL)
2✔
610
    {
611
        return plugin->pluginInfo();
1✔
612
    }
613
    else
614
        return "";
1✔
615
}
616

617
void InputOutputMap::removeDuplicates(QStringList &list)
3✔
618
{
619
    if (list.count() == 1)
3✔
620
        return;
×
621

622
    int c = 2;
3✔
623

624
    for (int i = 1; i < list.count(); i++)
12✔
625
    {
626
        for (int j = 0; j < i; j++)
27✔
627
        {
628
            if (list.at(i) == list.at(j))
18✔
629
            {
630
                list.replace(i, QString("%1 %2").arg(list.at(j)).arg(c));
×
631
                c++;
×
632
            }
633
        }
634
    }
635
}
636

637
QStringList InputOutputMap::inputPluginNames()
2✔
638
{
639
    QStringList list;
2✔
640
    QListIterator <QLCIOPlugin*> it(m_doc->ioPluginCache()->plugins());
2✔
641
    while (it.hasNext() == true)
4✔
642
    {
643
        QLCIOPlugin* plg(it.next());
2✔
644
        if (plg->capabilities() & QLCIOPlugin::Input)
2✔
645
            list << plg->name();
2✔
646
    }
647
    return list;
4✔
648
}
2✔
649

650
QStringList InputOutputMap::outputPluginNames()
2✔
651
{
652
    QStringList list;
2✔
653
    QListIterator <QLCIOPlugin*> it(m_doc->ioPluginCache()->plugins());
2✔
654
    while (it.hasNext() == true)
4✔
655
    {
656
        QLCIOPlugin* plg(it.next());
2✔
657
        if (plg->capabilities() & QLCIOPlugin::Output)
2✔
658
            list << plg->name();
2✔
659
    }
660
    return list;
4✔
661
}
2✔
662

663
QStringList InputOutputMap::pluginInputs(const QString& pluginName)
3✔
664
{
665
    QLCIOPlugin* ip = m_doc->ioPluginCache()->plugin(pluginName);
3✔
666
    if (ip == NULL)
3✔
667
        return QStringList();
1✔
668
    else
669
    {
670
        QStringList iList = ip->inputs();
2✔
671
        removeDuplicates(iList);
2✔
672
        return iList;
2✔
673
    }
2✔
674
}
675

676
QStringList InputOutputMap::pluginOutputs(const QString& pluginName)
2✔
677
{
678
    QLCIOPlugin* op = m_doc->ioPluginCache()->plugin(pluginName);
2✔
679
    if (op == NULL)
2✔
680
        return QStringList();
1✔
681
    else
682
    {
683
        QStringList oList = op->outputs();
1✔
684
        removeDuplicates(oList);
1✔
685
        return oList;
1✔
686
    }
1✔
687
}
688

689
bool InputOutputMap::pluginSupportsFeedback(const QString& pluginName)
1✔
690
{
691
    QLCIOPlugin* outputPlugin = m_doc->ioPluginCache()->plugin(pluginName);
1✔
692
    if (outputPlugin != NULL)
1✔
693
        return (outputPlugin->capabilities() & QLCIOPlugin::Feedback) > 0;
1✔
694
    else
695
        return false;
×
696
}
697

698
void InputOutputMap::configurePlugin(const QString& pluginName)
3✔
699
{
700
    QLCIOPlugin* outputPlugin = m_doc->ioPluginCache()->plugin(pluginName);
3✔
701
    if (outputPlugin != NULL)
3✔
702
        outputPlugin->configure();
3✔
703
}
3✔
704

705
bool InputOutputMap::canConfigurePlugin(const QString& pluginName)
4✔
706
{
707
    QLCIOPlugin* outputPlugin = m_doc->ioPluginCache()->plugin(pluginName);
4✔
708
    if (outputPlugin != NULL)
4✔
709
        return outputPlugin->canConfigure();
2✔
710
    else
711
        return false;
2✔
712
}
713

714
QString InputOutputMap::inputPluginStatus(const QString& pluginName, quint32 input)
9✔
715
{
716
    QLCIOPlugin* inputPlugin = NULL;
9✔
717
    QString info;
9✔
718

719
    if (pluginName.isEmpty() == false)
9✔
720
        inputPlugin = m_doc->ioPluginCache()->plugin(pluginName);
9✔
721

722
    if (inputPlugin != NULL)
9✔
723
    {
724
        info = inputPlugin->inputInfo(input);
4✔
725
    }
726
    else
727
    {
728
        /* Nothing selected */
729
        info += QString("<HTML><HEAD></HEAD><BODY>");
5✔
730
        info += QString("<H3>%1</H3>").arg(tr("Nothing selected"));
5✔
731
        info += QString("</BODY></HTML>");
5✔
732
    }
733

734
    return info;
9✔
735
}
×
736

737
QString InputOutputMap::outputPluginStatus(const QString& pluginName, quint32 output)
9✔
738
{
739
    QLCIOPlugin* outputPlugin = m_doc->ioPluginCache()->plugin(pluginName);
9✔
740
    if (outputPlugin != NULL)
9✔
741
    {
742
        return outputPlugin->outputInfo(output);
4✔
743
    }
744
    else
745
    {
746
        QString info;
5✔
747
        info += QString("<HTML><HEAD></HEAD><BODY>");
5✔
748
        info += QString("<H3>%1</H3>").arg(tr("Nothing selected"));
5✔
749
        info += QString("</BODY></HTML>");
5✔
750
        return info;
5✔
751
    }
5✔
752
}
753

754
bool InputOutputMap::sendFeedBack(quint32 universe, quint32 channel, uchar value, const QVariant &params)
12✔
755
{
756
    if (universe >= universesCount())
12✔
757
        return false;
3✔
758

759
    OutputPatch* patch = m_universeArray.at(universe)->feedbackPatch();
9✔
760

761
    if (patch != NULL && patch->isPatched())
9✔
762
    {
763
        patch->plugin()->sendFeedBack(universe, patch->output(), channel, value, params);
×
764
        return true;
×
765
    }
766
    else
767
    {
768
        return false;
9✔
769
    }
770
}
771

772
void InputOutputMap::slotPluginConfigurationChanged(QLCIOPlugin* plugin)
8✔
773
{
774
    QMutexLocker locker(&m_universeMutex);
8✔
775
    bool success = true;
8✔
776
    for (quint32 i = 0; i < universesCount(); i++)
40✔
777
    {
778
        Universe *universe = m_universeArray.at(i);
32✔
779
        for (int oi = 0; oi < universe->outputPatchesCount(); oi++)
32✔
780
        {
781
            OutputPatch* op = universe->outputPatch(oi);
×
782

783
            if (op != NULL && op->plugin() == plugin)
×
784
            {
785
                /*success = */ op->reconnect();
×
786
            }
787
        }
788

789
        InputPatch* ip = m_universeArray.at(i)->inputPatch();
32✔
790

791
        if (ip != NULL && ip->plugin() == plugin)
32✔
792
        {
793
            /*success = */ ip->reconnect();
×
794
        }
795

796
        OutputPatch* fp = m_universeArray.at(i)->feedbackPatch();
32✔
797
        if (fp != NULL && fp->plugin() == plugin)
32✔
798
        {
799
            /*success = */ fp->reconnect();
×
800
        }
801
    }
802
    locker.unlock();
8✔
803

804
    emit pluginConfigurationChanged(plugin->name(), success);
8✔
805
}
8✔
806

807
/*****************************************************************************
808
 * Profiles
809
 *****************************************************************************/
810

811
void InputOutputMap::loadProfiles(const QDir& dir)
5✔
812
{
813
    if (dir.exists() == false || dir.isReadable() == false)
5✔
814
        return;
1✔
815

816
    /* Go thru all found file entries and attempt to load an input
817
       profile from each of them. */
818
    QStringListIterator it(dir.entryList());
4✔
819
    while (it.hasNext() == true)
121✔
820
    {
821
        QLCInputProfile* prof;
822
        QString path;
117✔
823

824
        path = dir.absoluteFilePath(it.next());
117✔
825
        prof = QLCInputProfile::loader(path);
117✔
826
        if (prof != NULL)
117✔
827
        {
828
            /* Check for duplicates */
829
            if (profile(prof->name()) == NULL)
117✔
830
                addProfile(prof);
78✔
831
            else
832
                delete prof;
39✔
833
        }
834
        else
835
        {
836
            qWarning() << Q_FUNC_INFO << "Unable to find an input profile from" << path;
×
837
        }
838
    }
117✔
839
}
4✔
840

841
QStringList InputOutputMap::profileNames()
7✔
842
{
843
    QStringList list;
7✔
844
    QListIterator <QLCInputProfile*> it(m_profiles);
7✔
845
    while (it.hasNext() == true)
87✔
846
        list << it.next()->name();
80✔
847
    return list;
14✔
848
}
7✔
849

850
QLCInputProfile* InputOutputMap::profile(const QString& name)
125✔
851
{
852
    QListIterator <QLCInputProfile*> it(m_profiles);
125✔
853
    while (it.hasNext() == true)
2,403✔
854
    {
855
        QLCInputProfile *profile = it.next();
2,321✔
856
        if (profile->name() == name)
2,321✔
857
            return profile;
43✔
858
    }
859

860
    // Attempt to load input profile
861
    // from the workspace folder
862
    if (m_localProfilesLoaded == false)
82✔
863
    {
864
        if (m_doc->workspacePath().isEmpty())
82✔
865
            return NULL;
82✔
866

867
        m_localProfilesLoaded = true;
×
868

869
        qDebug() << "Input profile" << name << "not found. Attempt to load it from" << m_doc->workspacePath();
×
870
        QDir localDir(m_doc->workspacePath());
×
871
        localDir.setFilter(QDir::Files);
×
872
        localDir.setNameFilters(QStringList() << QString("*%1").arg(KExtInputProfile));
×
873
        loadProfiles(localDir);
×
874

875
        QListIterator <QLCInputProfile*> it(m_profiles);
×
876
        while (it.hasNext() == true)
×
877
        {
878
            QLCInputProfile *profile = it.next();
×
879
            if (profile->name() == name)
×
880
                return profile;
×
881
        }
882
    }
×
883

884
    return NULL;
×
885
}
125✔
886

887
bool InputOutputMap::addProfile(QLCInputProfile* profile)
81✔
888
{
889
    Q_ASSERT(profile != NULL);
81✔
890

891
    /* Don't add the same profile twice */
892
    if (m_profiles.contains(profile) == false)
81✔
893
    {
894
        m_profiles.append(profile);
80✔
895
        return true;
80✔
896
    }
897
    else
898
    {
899
        return false;
1✔
900
    }
901
}
902

903
bool InputOutputMap::removeProfile(const QString& name)
2✔
904
{
905
    QMutableListIterator <QLCInputProfile*> it(m_profiles);
2✔
906
    while (it.hasNext() == true)
3✔
907
    {
908
        QLCInputProfile *profile = it.next();
2✔
909
        if (profile->name() == name)
2✔
910
        {
911
            it.remove();
1✔
912
            delete profile;
1✔
913
            return true;
1✔
914
        }
915
    }
916

917
    return false;
1✔
918
}
919

920
bool InputOutputMap::inputSourceNames(const QLCInputSource *src,
8✔
921
                                QString& uniName, QString& chName) const
922
{
923
    if (src == NULL || src->isValid() == false)
8✔
924
        return false;
3✔
925

926
    if (src->universe() >= universesCount())
5✔
927
        return false;
1✔
928

929
    InputPatch* pat = m_universeArray.at(src->universe())->inputPatch();
4✔
930
    if (pat == NULL)
4✔
931
    {
932
        /* There is no patch for the given universe */
933
        uniName = QString("%1 -UNPATCHED-").arg(src->universe() + 1);
1✔
934

935
        ushort page = src->page();
1✔
936
        ushort channel = (src->channel() & 0x0000FFFF) + 1;
1✔
937

938
        if (page != 0)
1✔
939
            chName = QString("%1: ? (Page %2)").arg(channel).arg(page + 1);
×
940
        else
941
            chName = QString("%1: ?").arg(channel);
1✔
942
        return true;
1✔
943
    }
944

945
    QLCInputProfile* profile = pat->profile();
3✔
946
    if (profile == NULL)
3✔
947
    {
948
        /* There is no profile. Display plugin name and channel number. */
949
        if (pat->plugin() != NULL)
1✔
950
            uniName = QString("%1: %2").arg(src->universe() + 1).arg(pat->plugin()->name());
1✔
951
        else
952
            uniName = QString("%1: ??").arg(src->universe() + 1);
×
953

954
        ushort page = src->page();
1✔
955
        ushort channel = (src->channel() & 0x0000FFFF) + 1;
1✔
956

957
        if (page != 0)
1✔
958
            chName = QString("%1: ? (Page %2)").arg(channel).arg(page + 1);
×
959
        else
960
            chName = QString("%1: ?").arg(channel);
1✔
961
    }
962
    else
963
    {
964
        QLCInputChannel* ich;
965
        QString name;
2✔
966

967
        /* Display profile name for universe */
968
        uniName = QString("%1: %2").arg(src->universe() + 1).arg(profile->name());
2✔
969

970
        /* User can input the channel number by hand, so put something
971
           rational to the channel name in those cases as well. */
972
        ushort page = src->page();
2✔
973
        ushort channel = (src->channel() & 0x0000FFFF);
2✔
974

975
        ich = profile->channel(channel);
2✔
976
        if (ich != NULL)
2✔
977
            name = ich->name();
1✔
978
        else
979
            name = QString("?");
1✔
980

981
        /* Display channel name */
982
        if (page != 0)
2✔
983
            chName = QString("%1: %2 (Page %3)").arg(channel + 1).arg(name).arg(page + 1);
×
984
        else
985
            chName = QString("%1: %2").arg(channel + 1).arg(name);
2✔
986
    }
2✔
987

988
    return true;
3✔
989
}
990

991
bool InputOutputMap::inputSourceNames(QSharedPointer<QLCInputSource> const& src,
×
992
                                QString& uniName, QString& chName) const
993
{
994
    return inputSourceNames(src.data(), uniName, chName);
×
995
}
996

997
QDir InputOutputMap::systemProfileDirectory()
1✔
998
{
999
    return QLCFile::systemDirectory(QString(INPUTPROFILEDIR), QString(KExtInputProfile));
2✔
1000
}
1001

1002
QDir InputOutputMap::userProfileDirectory()
1✔
1003
{
1004
    return QLCFile::userDirectory(QString(USERINPUTPROFILEDIR), QString(INPUTPROFILEDIR),
2✔
1005
                                  QStringList() << QString("*%1").arg(KExtInputProfile));
4✔
1006
}
1007

1008
/*********************************************************************
1009
 * Beats
1010
 *********************************************************************/
1011

1012
void InputOutputMap::setBeatGeneratorType(InputOutputMap::BeatGeneratorType type)
×
1013
{
1014
    if (type == m_beatGeneratorType)
×
1015
        return;
×
1016

1017
    m_beatGeneratorType = type;
×
1018
    qDebug() << "[InputOutputMap] setting beat type:" << m_beatGeneratorType;
×
1019

1020
    switch (m_beatGeneratorType)
×
1021
    {
1022
        case Internal:
×
1023
            m_doc->masterTimer()->setBeatSourceType(MasterTimer::Internal);
×
1024
            setBpmNumber(m_doc->masterTimer()->bpmNumber());
×
1025
        break;
×
1026
        case Plugin:
×
1027
            m_doc->masterTimer()->setBeatSourceType(MasterTimer::External);
×
1028
            // reset the current BPM number and detect it from the MIDI beats
1029
            setBpmNumber(0);
×
1030
            m_beatTime->restart();
×
1031
        break;
×
1032
        case Audio:
×
1033
            m_doc->masterTimer()->setBeatSourceType(MasterTimer::External);
×
1034
            // reset the current BPM number and detect it from the audio input
1035
            setBpmNumber(0);
×
1036
            m_beatTime->restart();
×
1037
        break;
×
1038
        case Disabled:
×
1039
        default:
1040
            m_doc->masterTimer()->setBeatSourceType(MasterTimer::None);
×
1041
            setBpmNumber(0);
×
1042
        break;
×
1043
    }
1044

1045
    emit beatGeneratorTypeChanged();
×
1046
}
1047

1048
InputOutputMap::BeatGeneratorType InputOutputMap::beatGeneratorType() const
×
1049
{
1050
    return m_beatGeneratorType;
×
1051
}
1052

1053
QString InputOutputMap::beatTypeToString(BeatGeneratorType type) const
1✔
1054
{
1055
    switch (type)
1✔
1056
    {
1057
        case Internal:  return "Internal";
×
1058
        case Plugin:    return "Plugin";
×
1059
        case Audio:     return "Audio";
×
1060
        default:        return "Disabled";
1✔
1061
    }
1062
}
1063

1064
InputOutputMap::BeatGeneratorType InputOutputMap::stringToBeatType(QString str)
×
1065
{
1066
    if (str == "Internal")
×
1067
        return Internal;
×
1068
    else if (str == "Plugin")
×
1069
        return Plugin;
×
1070
    else if (str == "Audio")
×
1071
        return Audio;
×
1072

1073
    return Disabled;
×
1074
}
1075

1076
void InputOutputMap::setBpmNumber(int bpm)
×
1077
{
1078
    if (m_beatGeneratorType == Disabled || bpm == m_currentBPM)
×
1079
        return;
×
1080

1081
    //qDebug() << "[InputOutputMap] set BPM to" << bpm;
1082
    m_currentBPM = bpm;
×
1083

1084
    if (bpm != 0)
×
1085
        m_doc->masterTimer()->requestBpmNumber(bpm);
×
1086

1087
    emit bpmNumberChanged(m_currentBPM);
×
1088
}
1089

1090
int InputOutputMap::bpmNumber() const
×
1091
{
1092
    if (m_beatGeneratorType == Disabled)
×
1093
        return 0;
×
1094

1095
    return m_currentBPM;
×
1096
}
1097

1098
void InputOutputMap::slotMasterTimerBeat()
×
1099
{
1100
    if (m_beatGeneratorType != Internal)
×
1101
        return;
×
1102

1103
    emit beat();
×
1104
}
1105

1106
void InputOutputMap::slotPluginBeat(quint32 universe, quint32 channel, uchar value, const QString &key)
×
1107
{
1108
    Q_UNUSED(universe)
1109

1110
    // not interested in synthetic release or non-beat event
1111
    if (m_beatGeneratorType != Plugin || value == 0 || key != "beat")
×
1112
        return;
×
1113

1114
    qDebug() << "Plugin beat:" << channel << m_beatTime->elapsed();
×
1115

1116
    // process the timer as first thing, to avoid wasting time
1117
    // with the operations below
1118
    int elapsed = m_beatTime->elapsed();
×
1119
    m_beatTime->restart();
×
1120

1121
    int bpm = qRound(60000.0 / (float)elapsed);
×
1122
    float currBpmTime = 60000.0 / (float)m_currentBPM;
×
1123
    // here we check if the difference between the current BPM duration
1124
    // and the current time elapsed is within a range of +/-1ms.
1125
    // If it isn't, then the BPM number has really changed, otherwise
1126
    // it's just a tiny time drift
1127
    if (qAbs((float)elapsed - currBpmTime) > 1)
×
1128
        setBpmNumber(bpm);
×
1129

1130
    m_doc->masterTimer()->requestBeat();
×
1131
    emit beat();
×
1132
}
1133

1134
void InputOutputMap::slotAudioSpectrum(double *spectrumBands, int size, double maxMagnitude, quint32 power)
×
1135
{
1136
    Q_UNUSED(spectrumBands)
1137
    Q_UNUSED(size)
1138
    Q_UNUSED(maxMagnitude)
1139
    Q_UNUSED(power)
1140
}
×
1141

1142
/*********************************************************************
1143
 * Defaults - !! FALLBACK !!
1144
 *********************************************************************/
1145

1146
void InputOutputMap::loadDefaults()
×
1147
{
1148
    /* ************************ INPUT *********************************** */
1149
    QSettings settings;
×
1150
    QString plugin;
×
1151
    QString input;
×
1152
    QString key;
×
1153

1154
    for (quint32 i = 0; i < universesCount(); i++)
×
1155
    {
1156
        QString profileName;
×
1157
        bool passthrough;
1158

1159
        /* Plugin name */
1160
        key = QString("/inputmap/universe%1/plugin/").arg(i);
×
1161
        plugin = settings.value(key).toString();
×
1162

1163
        /* Plugin input */
1164
        key = QString("/inputmap/universe%1/input/").arg(i);
×
1165
        input = settings.value(key).toString();
×
1166

1167
        /* Input profile */
1168
        key = QString("/inputmap/universe%1/profile/").arg(i);
×
1169
        profileName = settings.value(key).toString();
×
1170

1171
        key = QString("/inputmap/universe%1/passthrough/").arg(i);
×
1172
        passthrough = settings.value(key).toBool();
×
1173
        if (passthrough == true)
×
1174
            m_universeArray.at(i)->setPassthrough(passthrough);
×
1175

1176
        /* Do the mapping */
1177
        if (plugin != KInputNone && input != KInputNone)
×
1178
            setInputPatch(i, plugin, "", input.toUInt(), profileName);
×
1179
    }
×
1180

1181
    /* ************************ OUTPUT *********************************** */
1182
    QString output;
×
1183
    QString fb_plugin;
×
1184
    QString feedback;
×
1185

1186
    for (quint32 i = 0; i < universesCount(); i++)
×
1187
    {
1188
        /* Plugin name */
1189
        key = QString("/outputmap/universe%1/plugin/").arg(i);
×
1190
        plugin = settings.value(key).toString();
×
1191

1192
        /* Plugin output */
1193
        key = QString("/outputmap/universe%1/output/").arg(i);
×
1194
        output = settings.value(key).toString();
×
1195

1196
        /* Feedback plugin name */
1197
        key = QString("/outputmap/universe%1/feedbackplugin/").arg(i);
×
1198
        fb_plugin = settings.value(key).toString();
×
1199

1200
        /* Feedback line */
1201
        key = QString("/outputmap/universe%1/feedback/").arg(i);
×
1202
        feedback = settings.value(key).toString();
×
1203

1204
        if (plugin != KOutputNone && output != KOutputNone)
×
1205
            setOutputPatch(i, plugin, "", output.toUInt());
×
1206

1207
        if (fb_plugin != KOutputNone && feedback != KOutputNone)
×
1208
            setOutputPatch(i, fb_plugin, "", feedback.toUInt(), true);
×
1209
    }
1210
}
×
1211

1212
void InputOutputMap::saveDefaults()
×
1213
{
1214
    /* ************************ INPUT *********************************** */
1215
    QSettings settings;
×
1216
    QString key;
×
1217

1218
    for (quint32 i = 0; i < universesCount(); i++)
×
1219
    {
1220
        InputPatch* inPatch = inputPatch(i);
×
1221

1222
        /* Plugin name */
1223
        key = QString("/inputmap/universe%1/plugin/").arg(i);
×
1224
        if (inPatch != NULL)
×
1225
            settings.setValue(key, inPatch->pluginName());
×
1226
        else
1227
            settings.setValue(key, KInputNone);
×
1228

1229
        /* Plugin input */
1230
        key = QString("/inputmap/universe%1/input/").arg(i);
×
1231
        if (inPatch != NULL)
×
1232
            settings.setValue(key, QString::number(inPatch->input()));
×
1233
        else
1234
            settings.setValue(key, KInputNone);
×
1235

1236
        /* Input profile */
1237
        key = QString("/inputmap/universe%1/profile/").arg(i);
×
1238
        if (inPatch != NULL)
×
1239
            settings.setValue(key, inPatch->profileName());
×
1240
        else
1241
            settings.setValue(key, KInputNone);
×
1242

1243
        /* Passthrough */
1244
        key = QString("/inputmap/universe%1/passthrough/").arg(i);
×
1245
        bool passthrough = m_universeArray.at(i)->passthrough();
×
1246
        if (passthrough == true)
×
1247
            settings.setValue(key, passthrough);
×
1248
        else
1249
            settings.remove(key);
×
1250
    }
1251

1252
    /* ************************ OUTPUT *********************************** */
1253

1254
    for (quint32 i = 0; i < universesCount(); i++)
×
1255
    {
1256
        OutputPatch* outPatch = outputPatch(i);
×
1257
        OutputPatch* fbPatch = feedbackPatch(i);
×
1258

1259
        key = QString("/outputmap/universe%1/plugin/").arg(i);
×
1260

1261
        /* Plugin name */
1262
        if (outPatch != NULL)
×
1263
            settings.setValue(key, outPatch->pluginName());
×
1264
        else
1265
            settings.setValue(key, KOutputNone);
×
1266

1267
        /* Plugin output */
1268
        key = QString("/outputmap/universe%1/output/").arg(i);
×
1269
        if (outPatch != NULL)
×
1270
            settings.setValue(key, outPatch->output());
×
1271
        else
1272
            settings.setValue(key, KOutputNone);
×
1273

1274
        key = QString("/outputmap/universe%1/feedbackplugin/").arg(i);
×
1275

1276
        /* Feedback plugin name */
1277
        if (fbPatch != NULL)
×
1278
            settings.setValue(key, fbPatch->pluginName());
×
1279
        else
1280
            settings.setValue(key, KOutputNone);
×
1281

1282
        /* Feedback plugin output */
1283
        key = QString("/outputmap/universe%1/feedback/").arg(i);
×
1284
        if (fbPatch != NULL)
×
1285
            settings.setValue(key, QString::number(fbPatch->output()));
×
1286
        else
1287
            settings.setValue(key, KOutputNone);
×
1288
    }
1289
}
×
1290

1291
/*********************************************************************
1292
 * Load & Save
1293
 *********************************************************************/
1294

1295
bool InputOutputMap::loadXML(QXmlStreamReader &root)
×
1296
{
1297
    if (root.name() != KXMLIOMap)
×
1298
    {
1299
        qWarning() << Q_FUNC_INFO << "InputOutputMap node not found";
×
1300
        return false;
×
1301
    }
1302

1303
    /** Reset the current universe list and read the new one */
1304
    removeAllUniverses();
×
1305

1306
    while (root.readNextStartElement())
×
1307
    {
1308
        if (root.name() == KXMLQLCUniverse)
×
1309
        {
1310
            quint32 id = InputOutputMap::invalidUniverse();
×
1311
            if (root.attributes().hasAttribute(KXMLQLCUniverseID))
×
1312
                id = root.attributes().value(KXMLQLCUniverseID).toString().toUInt();
×
1313
            if (addUniverse(id))
×
1314
            {
1315
                Universe *uni = m_universeArray.last();
×
1316
                uni->loadXML(root, m_universeArray.count() - 1, this);
×
1317
            }
1318
        }
1319
        else if (root.name() == KXMLIOBeatGenerator)
×
1320
        {
1321
            QXmlStreamAttributes attrs = root.attributes();
×
1322

1323
            if (attrs.hasAttribute(KXMLIOBeatType))
×
1324
                setBeatGeneratorType(stringToBeatType(attrs.value(KXMLIOBeatType).toString()));
×
1325

1326
            if (attrs.hasAttribute(KXMLIOBeatsPerMinute))
×
1327
                setBpmNumber(attrs.value(KXMLIOBeatsPerMinute).toInt());
×
1328

1329
            root.skipCurrentElement();
×
1330
        }
×
1331
        else
1332
        {
1333
            qWarning() << Q_FUNC_INFO << "Unknown IO Map tag:" << root.name();
×
1334
            root.skipCurrentElement();
×
1335
        }
1336
    }
1337

1338
    return true;
×
1339
}
1340

1341
bool InputOutputMap::saveXML(QXmlStreamWriter *doc) const
1✔
1342
{
1343
    Q_ASSERT(doc != NULL);
1✔
1344

1345
    /* IO Map Instance entry */
1346
    doc->writeStartElement(KXMLIOMap);
2✔
1347

1348
    doc->writeStartElement(KXMLIOBeatGenerator);
2✔
1349
    doc->writeAttribute(KXMLIOBeatType, beatTypeToString(m_beatGeneratorType));
2✔
1350
    doc->writeAttribute(KXMLIOBeatsPerMinute, QString::number(m_currentBPM));
2✔
1351
    doc->writeEndElement();
1✔
1352

1353
    foreach (Universe *uni, m_universeArray)
5✔
1354
        uni->saveXML(doc);
5✔
1355

1356
    doc->writeEndElement();
1✔
1357

1358
    return true;
1✔
1359
}
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