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

mcallegari / qlcplus / 8530698081

02 Apr 2024 11:14PM UTC coverage: 32.084% (+0.006%) from 32.078%
8530698081

Pull #1528

github

web-flow
Merge 5c495ac52 into 8337821bf
Pull Request #1528: Update qlcplus_fr_FR.ts

15389 of 47965 relevant lines covered (32.08%)

22934.26 hits per line

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

66.26
/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(Doc *doc, quint32 universes)
199✔
45
  : QObject(doc)
46
  , m_blackout(false)
47
  , m_universeChanged(false)
48
  , m_currentBPM(0)
49
  , m_beatTime(new QElapsedTimer())
199✔
50
{
51
    m_grandMaster = new GrandMaster(this);
199✔
52
    for (quint32 i = 0; i < universes; i++)
995✔
53
        addUniverse();
796✔
54

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

60
InputOutputMap::~InputOutputMap()
375✔
61
{
62
    removeAllUniverses();
199✔
63
    delete m_grandMaster;
199✔
64
    delete m_beatTime;
199✔
65
}
375✔
66

67
Doc* InputOutputMap::doc() const
863✔
68
{
69
    return qobject_cast<Doc*> (parent());
863✔
70
}
71

72
/*****************************************************************************
73
 * Blackout
74
 *****************************************************************************/
75

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

83
    return m_blackout;
4✔
84
}
85

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

92
    m_blackout = blackout;
5✔
93

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

105
        const QByteArray postGM = universe->postGMValues()->mid(0, universe->usedChannels());
40✔
106
        universe->dumpOutput(postGM, true);
20✔
107
    }
108

109
    emit blackoutChanged(m_blackout);
5✔
110

111
    return true;
5✔
112
}
113

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

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

125
/*****************************************************************************
126
 * Universes
127
 *****************************************************************************/
128

129
quint32 InputOutputMap::invalidUniverse()
1,856✔
130
{
131
    return UINT_MAX;
1,856✔
132
}
133

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

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

165
        uni = new Universe(id, m_grandMaster);
798✔
166
        connect(doc()->masterTimer(), SIGNAL(tickReady()), uni, SLOT(tick()), Qt::QueuedConnection);
798✔
167
        connect(uni, SIGNAL(universeWritten(quint32,QByteArray)), this, SIGNAL(universeWritten(quint32,QByteArray)));
798✔
168
        m_universeArray.append(uni);
798✔
169
    }
170

171
    emit universeAdded(id);
798✔
172
    return true;
798✔
173
}
174

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

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

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

190
        delete  m_universeArray.takeAt(index);
1✔
191
    }
192

193
    emit universeRemoved(index);
1✔
194
    return true;
1✔
195
}
196

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

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

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

216
    return invalidUniverse();
1✔
217
}
218

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

224
    return QString();
1✔
225
}
226

227
QString InputOutputMap::getUniverseNameByID(quint32 id)
1✔
228
{
229
    return getUniverseNameByIndex(id);
1✔
230
}
231

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

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

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

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

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

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

272
    return m_universeArray.at(index)->isPatched();
2✔
273
}
274

275
quint32 InputOutputMap::universesCount() const
1,592✔
276
{
277
    return (quint32)m_universeArray.count();
1,592✔
278
}
279

280
QList<Universe *> InputOutputMap::universes() const
42✔
281
{
282
    return m_universeArray;
42✔
283
}
284

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

291
    return NULL;
×
292
}
293

294
QList<Universe*> InputOutputMap::claimUniverses()
985✔
295
{
296
    m_universeMutex.lock();
985✔
297
    return m_universeArray;
985✔
298
}
299

300
void InputOutputMap::releaseUniverses(bool changed)
985✔
301
{
302
    m_universeChanged = changed;
985✔
303
    m_universeMutex.unlock();
985✔
304
}
985✔
305

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

314
    /* Reset Grand Master parameters */
315
    setGrandMasterValue(255);
1✔
316
    setGrandMasterValueMode(GrandMaster::Reduce);
1✔
317
    setGrandMasterChannelMode(GrandMaster::Intensity);
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);
10✔
390
    foreach (Universe *universe, m_universeArray)
45✔
391
        universe->flushInput();
20✔
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);
12✔
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 = doc()->ioPluginCache()->plugin(pluginName);
6✔
421

422
    if (!inputUID.isEmpty() && plugin != NULL)
6✔
423
    {
424
        QStringList inputs = plugin->inputs();
10✔
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
    }
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
}
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);
38✔
494
    QLCIOPlugin *plugin = doc()->ioPluginCache()->plugin(pluginName);
19✔
495

496
    if (!outputUID.isEmpty() && plugin != NULL)
19✔
497
    {
498
        QStringList inputs = plugin->outputs();
24✔
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
    }
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
}
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
54✔
532
{
533
    if (universe >= universesCount())
54✔
534
    {
535
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
5✔
536
        return NULL;
5✔
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 = 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(doc()->ioPluginCache()->plugins());
6✔
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
}
649

650
QStringList InputOutputMap::outputPluginNames()
2✔
651
{
652
    QStringList list;
2✔
653
    QListIterator <QLCIOPlugin*> it(doc()->ioPluginCache()->plugins());
6✔
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
}
662

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

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

689
bool InputOutputMap::pluginSupportsFeedback(const QString& pluginName)
1✔
690
{
691
    QLCIOPlugin* outputPlugin = 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 = 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 = 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 = 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 = doc()->ioPluginCache()->plugin(pluginName);
9✔
740
    if (outputPlugin != NULL)
9✔
741
    {
742
        return outputPlugin->outputInfo(output);
4✔
743
    }
744
    else
745
    {
746
        QString info;
10✔
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
    }
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());
12✔
819
    while (it.hasNext() == true)
118✔
820
    {
821
        QLCInputProfile* prof;
822
        QString path;
228✔
823

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

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

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

860
    return NULL;
80✔
861
}
862

863
bool InputOutputMap::addProfile(QLCInputProfile* profile)
79✔
864
{
865
    Q_ASSERT(profile != NULL);
79✔
866

867
    /* Don't add the same profile twice */
868
    if (m_profiles.contains(profile) == false)
79✔
869
    {
870
        m_profiles.append(profile);
78✔
871
        return true;
78✔
872
    }
873
    else
874
    {
875
        return false;
1✔
876
    }
877
}
878

879
bool InputOutputMap::removeProfile(const QString& name)
2✔
880
{
881
    QMutableListIterator <QLCInputProfile*> it(m_profiles);
2✔
882
    while (it.hasNext() == true)
3✔
883
    {
884
        QLCInputProfile *profile = it.next();
2✔
885
        if (profile->name() == name)
2✔
886
        {
887
            it.remove();
1✔
888
            delete profile;
1✔
889
            return true;
1✔
890
        }
891
    }
892

893
    return false;
1✔
894
}
895

896
bool InputOutputMap::inputSourceNames(const QLCInputSource *src,
8✔
897
                                QString& uniName, QString& chName) const
898
{
899
    if (src == NULL || src->isValid() == false)
8✔
900
        return false;
3✔
901

902
    if (src->universe() >= universesCount())
5✔
903
        return false;
1✔
904

905
    InputPatch* pat = m_universeArray.at(src->universe())->inputPatch();
4✔
906
    if (pat == NULL)
4✔
907
    {
908
        /* There is no patch for the given universe */
909
        uniName = QString("%1 -UNPATCHED-").arg(src->universe() + 1);
1✔
910

911
        ushort page = src->page();
1✔
912
        ushort channel = (src->channel() & 0x0000FFFF) + 1;
1✔
913

914
        if (page != 0)
1✔
915
            chName = QString("%1: ? (Page %2)").arg(channel).arg(page + 1);
×
916
        else
917
            chName = QString("%1: ?").arg(channel);
1✔
918
        return true;
1✔
919
    }
920

921
    QLCInputProfile* profile = pat->profile();
3✔
922
    if (profile == NULL)
3✔
923
    {
924
        /* There is no profile. Display plugin name and channel number. */
925
        if (pat->plugin() != NULL)
1✔
926
            uniName = QString("%1: %2").arg(src->universe() + 1).arg(pat->plugin()->name());
1✔
927
        else
928
            uniName = QString("%1: ??").arg(src->universe() + 1);
×
929

930
        ushort page = src->page();
1✔
931
        ushort channel = (src->channel() & 0x0000FFFF) + 1;
1✔
932

933
        if (page != 0)
1✔
934
            chName = QString("%1: ? (Page %2)").arg(channel).arg(page + 1);
×
935
        else
936
            chName = QString("%1: ?").arg(channel);
1✔
937
    }
938
    else
939
    {
940
        QLCInputChannel* ich;
941
        QString name;
4✔
942

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

946
        /* User can input the channel number by hand, so put something
947
           rational to the channel name in those cases as well. */
948
        ushort page = src->page();
2✔
949
        ushort channel = (src->channel() & 0x0000FFFF);
2✔
950

951
        ich = profile->channel(channel);
2✔
952
        if (ich != NULL)
2✔
953
            name = ich->name();
1✔
954
        else
955
            name = QString("?");
1✔
956

957
        /* Display channel name */
958
        if (page != 0)
2✔
959
            chName = QString("%1: %2 (Page %3)").arg(channel + 1).arg(name).arg(page + 1);
×
960
        else
961
            chName = QString("%1: %2").arg(channel + 1).arg(name);
2✔
962
    }
963

964
    return true;
3✔
965
}
966

967
bool InputOutputMap::inputSourceNames(QSharedPointer<QLCInputSource> const& src,
×
968
                                QString& uniName, QString& chName) const
969
{
970
    return inputSourceNames(src.data(), uniName, chName);
×
971
}
972

973
QDir InputOutputMap::systemProfileDirectory()
1✔
974
{
975
    return QLCFile::systemDirectory(QString(INPUTPROFILEDIR), QString(KExtInputProfile));
2✔
976
}
977

978
QDir InputOutputMap::userProfileDirectory()
1✔
979
{
980
    return QLCFile::userDirectory(QString(USERINPUTPROFILEDIR), QString(INPUTPROFILEDIR),
2✔
981
                                  QStringList() << QString("*%1").arg(KExtInputProfile));
3✔
982
}
983

984
/*********************************************************************
985
 * Beats
986
 *********************************************************************/
987

988
void InputOutputMap::setBeatGeneratorType(InputOutputMap::BeatGeneratorType type)
×
989
{
990
    if (type == m_beatGeneratorType)
×
991
        return;
×
992

993
    m_beatGeneratorType = type;
×
994
    qDebug() << "[InputOutputMap] setting beat type:" << m_beatGeneratorType;
×
995

996
    switch (m_beatGeneratorType)
×
997
    {
998
        case Internal:
×
999
            doc()->masterTimer()->setBeatSourceType(MasterTimer::Internal);
×
1000
            setBpmNumber(doc()->masterTimer()->bpmNumber());
×
1001
        break;
×
1002
        case Plugin:
×
1003
            doc()->masterTimer()->setBeatSourceType(MasterTimer::External);
×
1004
            // reset the current BPM number and detect it from the MIDI beats
1005
            setBpmNumber(0);
×
1006
            m_beatTime->restart();
×
1007
        break;
×
1008
        case Audio:
×
1009
            doc()->masterTimer()->setBeatSourceType(MasterTimer::External);
×
1010
            // reset the current BPM number and detect it from the audio input
1011
            setBpmNumber(0);
×
1012
            m_beatTime->restart();
×
1013
        break;
×
1014
        case Disabled:
×
1015
        default:
1016
            doc()->masterTimer()->setBeatSourceType(MasterTimer::None);
×
1017
            setBpmNumber(0);
×
1018
        break;
×
1019
    }
1020

1021
    emit beatGeneratorTypeChanged();
×
1022
}
1023

1024
InputOutputMap::BeatGeneratorType InputOutputMap::beatGeneratorType() const
×
1025
{
1026
    return m_beatGeneratorType;
×
1027
}
1028

1029
QString InputOutputMap::beatTypeToString(BeatGeneratorType type) const
1✔
1030
{
1031
    switch (type)
1✔
1032
    {
1033
        case Internal:  return "Internal";
×
1034
        case Plugin:    return "Plugin";
×
1035
        case Audio:     return "Audio";
×
1036
        default:        return "Disabled";
1✔
1037
    }
1038
}
1039

1040
InputOutputMap::BeatGeneratorType InputOutputMap::stringToBeatType(QString str)
×
1041
{
1042
    if (str == "Internal")
×
1043
        return Internal;
×
1044
    else if (str == "Plugin")
×
1045
        return Plugin;
×
1046
    else if (str == "Audio")
×
1047
        return Audio;
×
1048

1049
    return Disabled;
×
1050
}
1051

1052
void InputOutputMap::setBpmNumber(int bpm)
×
1053
{
1054
    if (m_beatGeneratorType == Disabled || bpm == m_currentBPM)
×
1055
        return;
×
1056

1057
    //qDebug() << "[InputOutputMap] set BPM to" << bpm;
1058
    m_currentBPM = bpm;
×
1059

1060
    if (bpm != 0)
×
1061
        doc()->masterTimer()->requestBpmNumber(bpm);
×
1062

1063
    emit bpmNumberChanged(m_currentBPM);
×
1064
}
1065

1066
int InputOutputMap::bpmNumber() const
×
1067
{
1068
    if (m_beatGeneratorType == Disabled)
×
1069
        return 0;
×
1070

1071
    return m_currentBPM;
×
1072
}
1073

1074
void InputOutputMap::slotMasterTimerBeat()
×
1075
{
1076
    if (m_beatGeneratorType != Internal)
×
1077
        return;
×
1078

1079
    emit beat();
×
1080
}
1081

1082
void InputOutputMap::slotPluginBeat(quint32 universe, quint32 channel, uchar value, const QString &key)
×
1083
{
1084
    Q_UNUSED(universe)
1085

1086
    // not interested in synthetic release or non-beat event
1087
    if (m_beatGeneratorType != Plugin || value == 0 || key != "beat")
×
1088
        return;
×
1089

1090
    qDebug() << "Plugin beat:" << channel << m_beatTime->elapsed();
×
1091

1092
    // process the timer as first thing, to avoid wasting time
1093
    // with the operations below
1094
    int elapsed = m_beatTime->elapsed();
×
1095
    m_beatTime->restart();
×
1096

1097
    int bpm = qRound(60000.0 / (float)elapsed);
×
1098
    float currBpmTime = 60000.0 / (float)m_currentBPM;
×
1099
    // here we check if the difference between the current BPM duration
1100
    // and the current time elapsed is within a range of +/-1ms.
1101
    // If it isn't, then the BPM number has really changed, otherwise
1102
    // it's just a tiny time drift
1103
    if (qAbs((float)elapsed - currBpmTime) > 1)
×
1104
        setBpmNumber(bpm);
×
1105

1106
    doc()->masterTimer()->requestBeat();
×
1107
    emit beat();
×
1108
}
1109

1110
void InputOutputMap::slotAudioSpectrum(double *spectrumBands, int size, double maxMagnitude, quint32 power)
×
1111
{
1112
    Q_UNUSED(spectrumBands)
1113
    Q_UNUSED(size)
1114
    Q_UNUSED(maxMagnitude)
1115
    Q_UNUSED(power)
1116
}
×
1117

1118
/*********************************************************************
1119
 * Defaults - !! FALLBACK !!
1120
 *********************************************************************/
1121

1122
void InputOutputMap::loadDefaults()
×
1123
{
1124
    /* ************************ INPUT *********************************** */
1125
    QSettings settings;
×
1126
    QString plugin;
×
1127
    QString input;
×
1128
    QString key;
×
1129

1130
    for (quint32 i = 0; i < universesCount(); i++)
×
1131
    {
1132
        QString profileName;
×
1133
        bool passthrough;
1134

1135
        /* Plugin name */
1136
        key = QString("/inputmap/universe%1/plugin/").arg(i);
×
1137
        plugin = settings.value(key).toString();
×
1138

1139
        /* Plugin input */
1140
        key = QString("/inputmap/universe%1/input/").arg(i);
×
1141
        input = settings.value(key).toString();
×
1142

1143
        /* Input profile */
1144
        key = QString("/inputmap/universe%1/profile/").arg(i);
×
1145
        profileName = settings.value(key).toString();
×
1146

1147
        key = QString("/inputmap/universe%1/passthrough/").arg(i);
×
1148
        passthrough = settings.value(key).toBool();
×
1149
        if (passthrough == true)
×
1150
            m_universeArray.at(i)->setPassthrough(passthrough);
×
1151

1152
        /* Do the mapping */
1153
        if (plugin != KInputNone && input != KInputNone)
×
1154
            setInputPatch(i, plugin, "", input.toUInt(), profileName);
×
1155
    }
1156

1157
    /* ************************ OUTPUT *********************************** */
1158
    QString output;
×
1159
    QString fb_plugin;
×
1160
    QString feedback;
×
1161

1162
    for (quint32 i = 0; i < universesCount(); i++)
×
1163
    {
1164
        /* Plugin name */
1165
        key = QString("/outputmap/universe%1/plugin/").arg(i);
×
1166
        plugin = settings.value(key).toString();
×
1167

1168
        /* Plugin output */
1169
        key = QString("/outputmap/universe%1/output/").arg(i);
×
1170
        output = settings.value(key).toString();
×
1171

1172
        /* Feedback plugin name */
1173
        key = QString("/outputmap/universe%1/feedbackplugin/").arg(i);
×
1174
        fb_plugin = settings.value(key).toString();
×
1175

1176
        /* Feedback line */
1177
        key = QString("/outputmap/universe%1/feedback/").arg(i);
×
1178
        feedback = settings.value(key).toString();
×
1179

1180
        if (plugin != KOutputNone && output != KOutputNone)
×
1181
            setOutputPatch(i, plugin, "", output.toUInt());
×
1182

1183
        if (fb_plugin != KOutputNone && feedback != KOutputNone)
×
1184
            setOutputPatch(i, fb_plugin, "", feedback.toUInt(), true);
×
1185
    }
1186
}
×
1187

1188
void InputOutputMap::saveDefaults()
×
1189
{
1190
    /* ************************ INPUT *********************************** */
1191
    QSettings settings;
×
1192
    QString key;
×
1193

1194
    for (quint32 i = 0; i < universesCount(); i++)
×
1195
    {
1196
        InputPatch* inPatch = inputPatch(i);
×
1197

1198
        /* Plugin name */
1199
        key = QString("/inputmap/universe%1/plugin/").arg(i);
×
1200
        if (inPatch != NULL)
×
1201
            settings.setValue(key, inPatch->pluginName());
×
1202
        else
1203
            settings.setValue(key, KInputNone);
×
1204

1205
        /* Plugin input */
1206
        key = QString("/inputmap/universe%1/input/").arg(i);
×
1207
        if (inPatch != NULL)
×
1208
            settings.setValue(key, QString::number(inPatch->input()));
×
1209
        else
1210
            settings.setValue(key, KInputNone);
×
1211

1212
        /* Input profile */
1213
        key = QString("/inputmap/universe%1/profile/").arg(i);
×
1214
        if (inPatch != NULL)
×
1215
            settings.setValue(key, inPatch->profileName());
×
1216
        else
1217
            settings.setValue(key, KInputNone);
×
1218

1219
        /* Passthrough */
1220
        key = QString("/inputmap/universe%1/passthrough/").arg(i);
×
1221
        bool passthrough = m_universeArray.at(i)->passthrough();
×
1222
        if (passthrough == true)
×
1223
            settings.setValue(key, passthrough);
×
1224
        else
1225
            settings.remove(key);
×
1226
    }
1227

1228
    /* ************************ OUTPUT *********************************** */
1229

1230
    for (quint32 i = 0; i < universesCount(); i++)
×
1231
    {
1232
        OutputPatch* outPatch = outputPatch(i);
×
1233
        OutputPatch* fbPatch = feedbackPatch(i);
×
1234

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

1237
        /* Plugin name */
1238
        if (outPatch != NULL)
×
1239
            settings.setValue(key, outPatch->pluginName());
×
1240
        else
1241
            settings.setValue(key, KOutputNone);
×
1242

1243
        /* Plugin output */
1244
        key = QString("/outputmap/universe%1/output/").arg(i);
×
1245
        if (outPatch != NULL)
×
1246
            settings.setValue(key, outPatch->output());
×
1247
        else
1248
            settings.setValue(key, KOutputNone);
×
1249

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

1252
        /* Feedback plugin name */
1253
        if (fbPatch != NULL)
×
1254
            settings.setValue(key, fbPatch->pluginName());
×
1255
        else
1256
            settings.setValue(key, KOutputNone);
×
1257

1258
        /* Feedback plugin output */
1259
        key = QString("/outputmap/universe%1/feedback/").arg(i);
×
1260
        if (fbPatch != NULL)
×
1261
            settings.setValue(key, QString::number(fbPatch->output()));
×
1262
        else
1263
            settings.setValue(key, KOutputNone);
×
1264
    }
1265
}
×
1266

1267
/*********************************************************************
1268
 * Load & Save
1269
 *********************************************************************/
1270

1271
bool InputOutputMap::loadXML(QXmlStreamReader &root)
×
1272
{
1273
    if (root.name() != KXMLIOMap)
×
1274
    {
1275
        qWarning() << Q_FUNC_INFO << "InputOutputMap node not found";
×
1276
        return false;
×
1277
    }
1278

1279
    /** Reset the current universe list and read the new one */
1280
    removeAllUniverses();
×
1281

1282
    while (root.readNextStartElement())
×
1283
    {
1284
        if (root.name() == KXMLQLCUniverse)
×
1285
        {
1286
            quint32 id = InputOutputMap::invalidUniverse();
×
1287
            if (root.attributes().hasAttribute(KXMLQLCUniverseID))
×
1288
                id = root.attributes().value(KXMLQLCUniverseID).toString().toUInt();
×
1289
            if (addUniverse(id))
×
1290
            {
1291
                Universe *uni = m_universeArray.last();
×
1292
                uni->loadXML(root, m_universeArray.count() - 1, this);
×
1293
            }
1294
        }
1295
        else if (root.name() == KXMLIOBeatGenerator)
×
1296
        {
1297
            QXmlStreamAttributes attrs = root.attributes();
×
1298

1299
            if (attrs.hasAttribute(KXMLIOBeatType))
×
1300
                setBeatGeneratorType(stringToBeatType(attrs.value(KXMLIOBeatType).toString()));
×
1301

1302
            if (attrs.hasAttribute(KXMLIOBeatsPerMinute))
×
1303
                setBpmNumber(attrs.value(KXMLIOBeatsPerMinute).toInt());
×
1304

1305
            root.skipCurrentElement();
×
1306
        }
1307
        else
1308
        {
1309
            qWarning() << Q_FUNC_INFO << "Unknown IO Map tag:" << root.name();
×
1310
            root.skipCurrentElement();
×
1311
        }
1312
    }
1313

1314
    return true;
×
1315
}
1316

1317
bool InputOutputMap::saveXML(QXmlStreamWriter *doc) const
1✔
1318
{
1319
    Q_ASSERT(doc != NULL);
1✔
1320

1321
    /* IO Map Instance entry */
1322
    doc->writeStartElement(KXMLIOMap);
1✔
1323

1324
    doc->writeStartElement(KXMLIOBeatGenerator);
1✔
1325
    doc->writeAttribute(KXMLIOBeatType, beatTypeToString(m_beatGeneratorType));
1✔
1326
    doc->writeAttribute(KXMLIOBeatsPerMinute, QString::number(m_currentBPM));
1✔
1327
    doc->writeEndElement();
1✔
1328

1329
    foreach (Universe *uni, m_universeArray)
9✔
1330
        uni->saveXML(doc);
4✔
1331

1332
    doc->writeEndElement();
1✔
1333

1334
    return true;
1✔
1335
}
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