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

mcallegari / qlcplus / 27362797676

11 Jun 2026 04:45PM UTC coverage: 35.268% (-0.02%) from 35.286%
27362797676

push

github

mcallegari
qmlui: use UIDs and reset feedback on input patch removal (fix #2042)

18433 of 52265 relevant lines covered (35.27%)

41116.73 hits per line

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

62.38
/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 "audiocapture.h"
37
#include "qlcioplugin.h"
38
#include "outputpatch.h"
39
#include "inputpatch.h"
40
#include "qlcconfig.h"
41
#include "universe.h"
42
#include "qlcfile.h"
43
#include "doc.h"
44

45
InputOutputMap::InputOutputMap(const Doc *doc, quint32 universes)
233✔
46
    : QObject(NULL)
47
    , m_doc(doc)
233✔
48
    , m_blackout(false)
233✔
49
    , m_universeChanged(false)
233✔
50
    , m_localProfilesLoaded(false)
233✔
51
    , m_currentBPM(0)
233✔
52
    , m_beatTime(new QElapsedTimer())
233✔
53
    , m_networkServerType(NativeServer)
233✔
54
    , m_networkServerAutoStart(false)
466✔
55
{
56
    m_grandMaster = new GrandMaster(this);
233✔
57
    for (quint32 i = 0; i < universes; i++)
1,161✔
58
        addUniverse();
928✔
59

60
    connect(doc->ioPluginCache(), SIGNAL(pluginConfigurationChanged(QLCIOPlugin*)),
233✔
61
            this, SLOT(slotPluginConfigurationChanged(QLCIOPlugin*)));
62
    connect(doc->masterTimer(), SIGNAL(beat()), this, SLOT(slotMasterTimerBeat()));
233✔
63
}
233✔
64

65
InputOutputMap::~InputOutputMap()
443✔
66
{
67
    removeAllUniverses();
233✔
68
    delete m_grandMaster;
233✔
69
    delete m_beatTime;
233✔
70
    qDeleteAll(m_profiles);
233✔
71
}
443✔
72

73
/*****************************************************************************
74
 * Blackout
75
 *****************************************************************************/
76

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

84
    return m_blackout;
4✔
85
}
86

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

93
    m_blackout = blackout;
5✔
94

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

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

110
    emit blackoutChanged(m_blackout);
5✔
111

112
    return true;
5✔
113
}
114

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

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

126
/*****************************************************************************
127
 * Universes
128
 *****************************************************************************/
129

130
quint32 InputOutputMap::invalidUniverse()
2,200✔
131
{
132
    return UINT_MAX;
2,200✔
133
}
134

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

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

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

172
    emit universeAdded(id);
930✔
173
    return true;
930✔
174
}
175

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

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

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

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

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

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

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

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

217
    return invalidUniverse();
1✔
218
}
219

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

225
    return QString();
1✔
226
}
227

228
QString InputOutputMap::getUniverseNameByID(quint32 id) const
2✔
229
{
230
    return getUniverseNameByIndex(id);
2✔
231
}
232

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

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

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

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

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

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

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

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

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

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

292
    return NULL;
×
293
}
294

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

301
void InputOutputMap::releaseUniverses(bool changed)
1,083✔
302
{
303
    m_universeChanged = changed;
1,083✔
304
    m_universeMutex.unlock();
1,083✔
305
}
1,083✔
306

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

315
    /* Reset Grand Master parameters */
316
    setGrandMasterValue(255);
1✔
317
    setGrandMasterValueMode(GrandMaster::Reduce);
1✔
318
    setGrandMasterChannelMode(GrandMaster::Intensity);
1✔
319

320
    m_localProfilesLoaded = false;
1✔
321
}
1✔
322

323
/*********************************************************************
324
 * Grand Master
325
 *********************************************************************/
326

327
void InputOutputMap::setGrandMasterChannelMode(GrandMaster::ChannelMode mode)
2✔
328
{
329
    Q_ASSERT(m_grandMaster != NULL);
2✔
330

331
    if (m_grandMaster->channelMode() != mode)
2✔
332
    {
333
        m_grandMaster->setChannelMode(mode);
1✔
334
        m_universeChanged = true;
1✔
335
    }
336
}
2✔
337

338
GrandMaster::ChannelMode InputOutputMap::grandMasterChannelMode() const
79✔
339
{
340
    Q_ASSERT(m_grandMaster != NULL);
79✔
341

342
    return m_grandMaster->channelMode();
79✔
343
}
344

345
void InputOutputMap::setGrandMasterValueMode(GrandMaster::ValueMode mode)
2✔
346
{
347
    Q_ASSERT(m_grandMaster != NULL);
2✔
348

349
    if (m_grandMaster->valueMode() != mode)
2✔
350
    {
351
        m_grandMaster->setValueMode(mode);
1✔
352
        m_universeChanged = true;
1✔
353
    }
354

355
    emit grandMasterValueModeChanged(mode);
2✔
356
}
2✔
357

358
GrandMaster::ValueMode InputOutputMap::grandMasterValueMode() const
156✔
359
{
360
    Q_ASSERT(m_grandMaster != NULL);
156✔
361

362
    return m_grandMaster->valueMode();
156✔
363
}
364

365
void InputOutputMap::setGrandMasterValue(uchar value)
2✔
366
{
367
    Q_ASSERT(m_grandMaster != NULL);
2✔
368

369
    if (m_grandMaster->value() != value)
2✔
370
    {
371
        m_grandMaster->setValue(value);
1✔
372
        m_universeChanged = true;
1✔
373
    }
374

375
    if (m_universeChanged == true)
2✔
376
        emit grandMasterValueChanged(value);
2✔
377
}
2✔
378

379
uchar InputOutputMap::grandMasterValue() const
2✔
380
{
381
    Q_ASSERT(m_grandMaster != NULL);
2✔
382

383
    return m_grandMaster->value();
2✔
384
}
385

386
/*********************************************************************
387
 * Patch
388
 *********************************************************************/
389

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

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

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

425
    if (plugin != NULL)
6✔
426
    {
427
        int lIdx = -1;
5✔
428
        // 1. Match by stable UID
429
        if (!inputUID.isEmpty())
5✔
430
            lIdx = plugin->inputsUID().indexOf(inputUID);
×
431
        // 2. Match by display name
432
        if (lIdx == -1 && !inputName.isEmpty())
5✔
433
            lIdx = plugin->inputs().indexOf(inputName);
5✔
434
        // 3. Fall back to saved line number
435
        if (lIdx != -1)
5✔
436
        {
437
            qDebug() << "[IOMAP] Found match on input on universe" << universe << "line" << lIdx;
5✔
438
            input = lIdx;
5✔
439
        }
440
        else
441
        {
442
            qDebug() << "[IOMAP] !!No match found!! for input on universe" << universe << "uid:" << inputUID << "name:" << inputName;
×
443
        }
444
    }
445

446
    if (m_universeArray.at(universe)->setInputPatch(
6✔
447
                plugin, input, profile(profileName)) == true)
6✔
448
    {
449
        ip = m_universeArray.at(universe)->inputPatch();
6✔
450
        if (ip != NULL)
6✔
451
        {
452
            connect(ip, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
5✔
453
                    this, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)));
454
            if (ip->plugin()->capabilities() & QLCIOPlugin::Beats)
5✔
455
            {
456
                connect(ip, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
457
                        this, SLOT(slotPluginBeat(quint32,quint32,uchar,const QString&)));
458
            }
459
        }
460
    }
461
    else
462
    {
463
        return false;
×
464
    }
465

466
    if (ip != NULL && currProfile != ip->profile())
6✔
467
        emit profileChanged(universe, ip->profileName());
3✔
468

469
    return true;
6✔
470
}
6✔
471

472
bool InputOutputMap::setInputProfile(quint32 universe, const QString &profileName)
×
473
{
474
    /* Check that the universe that we're doing mapping for is valid */
475
    if (universe >= universesCount())
×
476
    {
477
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
×
478
        return false;
×
479
    }
480

481
    InputPatch *currInPatch = m_universeArray.at(universe)->inputPatch();
×
482
    if (currInPatch != NULL)
×
483
        currInPatch->set(profile(profileName));
×
484

485
    /* if no input patch is set, then setting a profile is useless,
486
       but there's no reason to cause an error here */
487
    return true;
×
488
}
489

490
bool InputOutputMap::setOutputPatch(quint32 universe, const QString &pluginName,
21✔
491
                                    const QString &outputUID, const QString &outputName,
492
                                    quint32 output, bool isFeedback, int index)
493
{
494
    /* Check that the universe that we're doing mapping for is valid */
495
    if (universe >= universesCount())
21✔
496
    {
497
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
2✔
498
        return false;
2✔
499
    }
500

501
    QMutexLocker locker(&m_universeMutex);
19✔
502
    QLCIOPlugin *plugin = m_doc->ioPluginCache()->plugin(pluginName);
19✔
503

504
    if (plugin != NULL)
19✔
505
    {
506
        int lIdx = -1;
18✔
507
        // 1. Match by stable UID
508
        if (!outputUID.isEmpty())
18✔
509
            lIdx = plugin->outputsUID().indexOf(outputUID);
×
510
        // 2. Match by display name
511
        if (lIdx == -1 && !outputName.isEmpty())
18✔
512
            lIdx = plugin->outputs().indexOf(outputName);
12✔
513
        // 3. Fall back to saved line number
514
        if (lIdx != -1)
18✔
515
        {
516
            qDebug() << "[IOMAP] Found match on output on universe" << universe << "line" << lIdx;
12✔
517
            output = lIdx;
12✔
518
        }
519
        else
520
        {
521
            qDebug() << "[IOMAP] !!No match found!! for output on universe" << universe << "uid:" << outputUID << "name:" << outputName;
6✔
522
        }
523
    }
524

525
    if (isFeedback == false)
19✔
526
        return m_universeArray.at(universe)->setOutputPatch(plugin, output, index);
19✔
527
    else
528
        return m_universeArray.at(universe)->setFeedbackPatch(plugin, output);
×
529

530
    return false;
531
}
19✔
532

533
int InputOutputMap::outputPatchesCount(quint32 universe) const
4✔
534
{
535
    if (universe >= universesCount())
4✔
536
    {
537
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
×
538
        return 0;
×
539
    }
540

541
    return m_universeArray.at(universe)->outputPatchesCount();
4✔
542
}
543

544
InputPatch *InputOutputMap::inputPatch(quint32 universe) const
56✔
545
{
546
    if (universe >= universesCount())
56✔
547
    {
548
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
7✔
549
        return NULL;
7✔
550
    }
551
    return m_universeArray.at(universe)->inputPatch();
49✔
552
}
553

554
OutputPatch *InputOutputMap::outputPatch(quint32 universe, int index) const
27✔
555
{
556
    if (universe >= universesCount())
27✔
557
    {
558
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
×
559
        return NULL;
×
560
    }
561
    return m_universeArray.at(universe)->outputPatch(index);
27✔
562
}
563

564
OutputPatch *InputOutputMap::feedbackPatch(quint32 universe) const
2✔
565
{
566
    if (universe >= universesCount())
2✔
567
    {
568
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
1✔
569
        return NULL;
1✔
570
    }
571
    return m_universeArray.at(universe)->feedbackPatch();
1✔
572
}
573

574
QStringList InputOutputMap::universeNames() const
23✔
575
{
576
    QStringList list;
23✔
577
    for (quint32 i = 0; i < universesCount(); i++)
115✔
578
        list << m_universeArray.at(i)->name();
92✔
579

580
    return list;
23✔
581
}
×
582

583
quint32 InputOutputMap::inputMapping(const QString &pluginName, quint32 input) const
16✔
584
{
585
    for (quint32 uni = 0; uni < universesCount(); uni++)
70✔
586
    {
587
        const InputPatch* p = m_universeArray.at(uni)->inputPatch();
57✔
588
        if (p != NULL && p->pluginName() == pluginName && p->input() == input)
57✔
589
            return uni;
3✔
590
    }
591

592
    return QLCIOPlugin::invalidLine();
13✔
593
}
594

595
quint32 InputOutputMap::outputMapping(const QString &pluginName, quint32 output) const
2✔
596
{
597
    for (quint32 uni = 0; uni < universesCount(); uni++)
9✔
598
    {
599
        Universe *universe = m_universeArray.at(uni);
8✔
600
        for (int i = 0; i < universe->outputPatchesCount(); i++)
15✔
601
        {
602
            const OutputPatch* p = universe->outputPatch(i);
8✔
603
            if (p != NULL && p->pluginName() == pluginName && p->output() == output)
8✔
604
                return uni;
1✔
605
        }
606
    }
607

608
    return QLCIOPlugin::invalidLine();
1✔
609
}
610

611
/*****************************************************************************
612
 * Plugins
613
 *****************************************************************************/
614

615
QString InputOutputMap::pluginDescription(const QString &pluginName) const
2✔
616
{
617
    QLCIOPlugin* plugin = NULL;
2✔
618

619
    if (pluginName.isEmpty() == false)
2✔
620
        plugin = m_doc->ioPluginCache()->plugin(pluginName);
2✔
621

622
    if (plugin != NULL)
2✔
623
    {
624
        return plugin->pluginInfo();
1✔
625
    }
626
    else
627
        return "";
1✔
628
}
629

630
void InputOutputMap::removeDuplicates(QStringList &list) const
3✔
631
{
632
    if (list.count() == 1)
3✔
633
        return;
×
634

635
    int c = 2;
3✔
636

637
    for (int i = 1; i < list.count(); i++)
12✔
638
    {
639
        for (int j = 0; j < i; j++)
27✔
640
        {
641
            if (list.at(i) == list.at(j))
18✔
642
            {
643
                list.replace(i, QString("%1 %2").arg(list.at(j)).arg(c));
×
644
                c++;
×
645
            }
646
        }
647
    }
648
}
649

650
QStringList InputOutputMap::inputPluginNames() const
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::Input)
2✔
658
            list << plg->name();
2✔
659
    }
660
    return list;
4✔
661
}
2✔
662

663
QStringList InputOutputMap::outputPluginNames() const
2✔
664
{
665
    QStringList list;
2✔
666
    QListIterator <QLCIOPlugin*> it(m_doc->ioPluginCache()->plugins());
2✔
667
    while (it.hasNext() == true)
4✔
668
    {
669
        QLCIOPlugin* plg(it.next());
2✔
670
        if (plg->capabilities() & QLCIOPlugin::Output)
2✔
671
            list << plg->name();
2✔
672
    }
673
    return list;
4✔
674
}
2✔
675

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

689
QStringList InputOutputMap::pluginOutputs(const QString& pluginName) const
2✔
690
{
691
    QLCIOPlugin* op = m_doc->ioPluginCache()->plugin(pluginName);
2✔
692
    if (op == NULL)
2✔
693
        return QStringList();
1✔
694
    else
695
    {
696
        QStringList oList = op->outputs();
1✔
697
        removeDuplicates(oList);
1✔
698
        return oList;
1✔
699
    }
1✔
700
}
701

702
bool InputOutputMap::pluginSupportsFeedback(const QString& pluginName) const
1✔
703
{
704
    QLCIOPlugin* outputPlugin = m_doc->ioPluginCache()->plugin(pluginName);
1✔
705
    if (outputPlugin != NULL)
1✔
706
        return (outputPlugin->capabilities() & QLCIOPlugin::Feedback) > 0;
1✔
707
    else
708
        return false;
×
709
}
710

711
void InputOutputMap::configurePlugin(const QString& pluginName)
3✔
712
{
713
    QLCIOPlugin* outputPlugin = m_doc->ioPluginCache()->plugin(pluginName);
3✔
714
    if (outputPlugin != NULL)
3✔
715
        outputPlugin->configure();
3✔
716
}
3✔
717

718
bool InputOutputMap::canConfigurePlugin(const QString& pluginName) const
4✔
719
{
720
    QLCIOPlugin* outputPlugin = m_doc->ioPluginCache()->plugin(pluginName);
4✔
721
    if (outputPlugin != NULL)
4✔
722
        return outputPlugin->canConfigure();
2✔
723
    else
724
        return false;
2✔
725
}
726

727
QString InputOutputMap::inputPluginStatus(const QString& pluginName, quint32 input) const
9✔
728
{
729
    QLCIOPlugin* inputPlugin = NULL;
9✔
730
    QString info;
9✔
731

732
    if (pluginName.isEmpty() == false)
9✔
733
        inputPlugin = m_doc->ioPluginCache()->plugin(pluginName);
9✔
734

735
    if (inputPlugin != NULL)
9✔
736
    {
737
        info = inputPlugin->inputInfo(input);
4✔
738
    }
739
    else
740
    {
741
        /* Nothing selected */
742
        info += QString("<HTML><HEAD></HEAD><BODY>");
5✔
743
        info += QString("<H3>%1</H3>").arg(tr("Nothing selected"));
5✔
744
        info += QString("</BODY></HTML>");
5✔
745
    }
746

747
    return info;
9✔
748
}
×
749

750
QString InputOutputMap::outputPluginStatus(const QString& pluginName, quint32 output) const
9✔
751
{
752
    QLCIOPlugin* outputPlugin = m_doc->ioPluginCache()->plugin(pluginName);
9✔
753
    if (outputPlugin != NULL)
9✔
754
    {
755
        return outputPlugin->outputInfo(output);
4✔
756
    }
757
    else
758
    {
759
        QString info;
5✔
760
        info += QString("<HTML><HEAD></HEAD><BODY>");
5✔
761
        info += QString("<H3>%1</H3>").arg(tr("Nothing selected"));
5✔
762
        info += QString("</BODY></HTML>");
5✔
763
        return info;
5✔
764
    }
5✔
765
}
766

767
bool InputOutputMap::sendFeedBack(quint32 universe, quint32 channel, uchar value, const QVariant &params)
12✔
768
{
769
    if (universe >= universesCount())
12✔
770
        return false;
3✔
771

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

774
    if (patch != NULL && patch->isPatched())
9✔
775
    {
776
        patch->plugin()->sendFeedBack(universe, patch->output(), channel, value, params);
×
777
        return true;
×
778
    }
779
    else
780
    {
781
        return false;
9✔
782
    }
783
}
784

785
void InputOutputMap::slotPluginConfigurationChanged(QLCIOPlugin* plugin)
8✔
786
{
787
    QMutexLocker locker(&m_universeMutex);
8✔
788
    bool success = true;
8✔
789
    for (quint32 i = 0; i < universesCount(); i++)
40✔
790
    {
791
        Universe *universe = m_universeArray.at(i);
32✔
792
        for (int oi = 0; oi < universe->outputPatchesCount(); oi++)
32✔
793
        {
794
            OutputPatch* op = universe->outputPatch(oi);
×
795

796
            if (op != NULL && op->plugin() == plugin)
×
797
            {
798
                /*success = */ op->reconnect();
×
799
            }
800
        }
801

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

804
        if (ip != NULL && ip->plugin() == plugin)
32✔
805
        {
806
            /*success = */ ip->reconnect();
×
807
        }
808

809
        OutputPatch* fp = m_universeArray.at(i)->feedbackPatch();
32✔
810
        if (fp != NULL && fp->plugin() == plugin)
32✔
811
        {
812
            /*success = */ fp->reconnect();
×
813
        }
814
    }
815
    locker.unlock();
8✔
816

817
    emit pluginConfigurationChanged(plugin->name(), success);
8✔
818
}
8✔
819

820
/*****************************************************************************
821
 * Profiles
822
 *****************************************************************************/
823

824
void InputOutputMap::loadProfiles(const QDir& dir)
5✔
825
{
826
    if (dir.exists() == false || dir.isReadable() == false)
5✔
827
        return;
1✔
828

829
    /* Go thru all found file entries and attempt to load an input
830
       profile from each of them. */
831
    QStringListIterator it(dir.entryList());
4✔
832
    while (it.hasNext() == true)
124✔
833
    {
834
        QLCInputProfile* prof;
835
        QString path;
120✔
836

837
        path = dir.absoluteFilePath(it.next());
120✔
838
        prof = QLCInputProfile::loader(path);
120✔
839
        if (prof != NULL)
120✔
840
        {
841
            /* Check for duplicates */
842
            if (profile(prof->name()) == NULL)
120✔
843
                addProfile(prof);
80✔
844
            else
845
                delete prof;
40✔
846
        }
847
        else
848
        {
849
            qWarning() << Q_FUNC_INFO << "Unable to find an input profile from" << path;
×
850
        }
851
    }
120✔
852
}
4✔
853

854
QStringList InputOutputMap::profileNames() const
7✔
855
{
856
    QStringList list;
7✔
857
    QListIterator <QLCInputProfile*> it(m_profiles);
7✔
858
    while (it.hasNext() == true)
89✔
859
        list << it.next()->name();
82✔
860
    return list;
14✔
861
}
7✔
862

863
QLCInputProfile* InputOutputMap::profile(const QString& name)
128✔
864
{
865
    QListIterator <QLCInputProfile*> it(m_profiles);
128✔
866
    while (it.hasNext() == true)
2,525✔
867
    {
868
        QLCInputProfile *profile = it.next();
2,441✔
869
        if (profile->name() == name)
2,441✔
870
            return profile;
44✔
871
    }
872

873
    // Attempt to load input profile
874
    // from the workspace folder
875
    if (m_localProfilesLoaded == false)
84✔
876
    {
877
        if (m_doc->workspacePath().isEmpty())
84✔
878
            return NULL;
84✔
879

880
        m_localProfilesLoaded = true;
×
881

882
        qDebug() << "Input profile" << name << "not found. Attempt to load it from" << m_doc->workspacePath();
×
883
        QDir localDir(m_doc->workspacePath());
×
884
        localDir.setFilter(QDir::Files);
×
885
        localDir.setNameFilters(QStringList() << QString("*%1").arg(KExtInputProfile));
×
886
        loadProfiles(localDir);
×
887

888
        QListIterator <QLCInputProfile*> it(m_profiles);
×
889
        while (it.hasNext() == true)
×
890
        {
891
            QLCInputProfile *profile = it.next();
×
892
            if (profile->name() == name)
×
893
                return profile;
×
894
        }
895
    }
×
896

897
    return NULL;
×
898
}
128✔
899

900
bool InputOutputMap::addProfile(QLCInputProfile* profile)
83✔
901
{
902
    Q_ASSERT(profile != NULL);
83✔
903

904
    /* Don't add the same profile twice */
905
    if (m_profiles.contains(profile) == false)
83✔
906
    {
907
        m_profiles.append(profile);
82✔
908
        return true;
82✔
909
    }
910
    else
911
    {
912
        return false;
1✔
913
    }
914
}
915

916
bool InputOutputMap::removeProfile(const QString& name)
2✔
917
{
918
    QMutableListIterator <QLCInputProfile*> it(m_profiles);
2✔
919
    while (it.hasNext() == true)
3✔
920
    {
921
        QLCInputProfile *profile = it.next();
2✔
922
        if (profile->name() == name)
2✔
923
        {
924
            it.remove();
1✔
925
            delete profile;
1✔
926
            return true;
1✔
927
        }
928
    }
929

930
    return false;
1✔
931
}
932

933
bool InputOutputMap::inputSourceNames(const QLCInputSource *src,
8✔
934
                                QString& uniName, QString& chName) const
935
{
936
    if (src == NULL || src->isValid() == false)
8✔
937
        return false;
3✔
938

939
    if (src->universe() >= universesCount())
5✔
940
        return false;
1✔
941

942
    InputPatch* pat = m_universeArray.at(src->universe())->inputPatch();
4✔
943
    if (pat == NULL)
4✔
944
    {
945
        /* There is no patch for the given universe */
946
        uniName = QString("%1 -UNPATCHED-").arg(src->universe() + 1);
1✔
947

948
        ushort page = src->page();
1✔
949
        ushort channel = (src->channel() & 0x0000FFFF) + 1;
1✔
950

951
        if (page != 0)
1✔
952
            chName = QString("%1: ? (Page %2)").arg(channel).arg(page + 1);
×
953
        else
954
            chName = QString("%1: ?").arg(channel);
1✔
955
        return true;
1✔
956
    }
957

958
    QLCInputProfile* profile = pat->profile();
3✔
959
    if (profile == NULL)
3✔
960
    {
961
        /* There is no profile. Display plugin name and channel number. */
962
        if (pat->plugin() != NULL)
1✔
963
            uniName = QString("%1: %2").arg(src->universe() + 1).arg(pat->plugin()->name());
1✔
964
        else
965
            uniName = QString("%1: ??").arg(src->universe() + 1);
×
966

967
        ushort page = src->page();
1✔
968
        ushort channel = (src->channel() & 0x0000FFFF) + 1;
1✔
969

970
        if (page != 0)
1✔
971
            chName = QString("%1: ? (Page %2)").arg(channel).arg(page + 1);
×
972
        else
973
            chName = QString("%1: ?").arg(channel);
1✔
974
    }
975
    else
976
    {
977
        QLCInputChannel* ich;
978
        QString name;
2✔
979

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

983
        /* User can input the channel number by hand, so put something
984
           rational to the channel name in those cases as well. */
985
        ushort page = src->page();
2✔
986
        ushort channel = (src->channel() & 0x0000FFFF);
2✔
987

988
        ich = profile->channel(channel);
2✔
989
        if (ich != NULL)
2✔
990
            name = ich->name();
1✔
991
        else
992
            name = QString("?");
1✔
993

994
        /* Display channel name */
995
        if (page != 0)
2✔
996
            chName = QString("%1: %2 (Page %3)").arg(channel + 1).arg(name).arg(page + 1);
×
997
        else
998
            chName = QString("%1: %2").arg(channel + 1).arg(name);
2✔
999
    }
2✔
1000

1001
    return true;
3✔
1002
}
1003

1004
bool InputOutputMap::inputSourceNames(QSharedPointer<QLCInputSource> const& src,
×
1005
                                QString& uniName, QString& chName) const
1006
{
1007
    return inputSourceNames(src.data(), uniName, chName);
×
1008
}
1009

1010
QDir InputOutputMap::systemProfileDirectory()
1✔
1011
{
1012
    return QLCFile::systemDirectory(QString(INPUTPROFILEDIR), QString(KExtInputProfile));
2✔
1013
}
1014

1015
QDir InputOutputMap::userProfileDirectory()
1✔
1016
{
1017
    return QLCFile::userDirectory(QString(USERINPUTPROFILEDIR), QString(INPUTPROFILEDIR),
2✔
1018
                                  QStringList() << QString("*%1").arg(KExtInputProfile));
4✔
1019
}
1020

1021
/*********************************************************************
1022
 * Beats
1023
 *********************************************************************/
1024

1025
void InputOutputMap::setBeatGeneratorType(InputOutputMap::BeatGeneratorType type)
×
1026
{
1027
    if (type == m_beatGeneratorType)
×
1028
        return;
×
1029

1030
    if (m_beatGeneratorType == Audio)
×
1031
    {
1032
        m_inputCapture->unregisterBandsNumber(4);
×
1033
        disconnect(m_inputCapture, SIGNAL(beatDetected()), this, SLOT(slotProcessBeat()));
×
1034
    }
1035

1036
    m_beatGeneratorType = type;
×
1037
    qDebug() << "[InputOutputMap] setting beat type:" << m_beatGeneratorType;
×
1038

1039
    switch (m_beatGeneratorType)
×
1040
    {
1041
        case Internal:
×
1042
        {
1043
            m_doc->masterTimer()->setBeatSourceType(MasterTimer::Internal);
×
1044
            setBpmNumber(m_doc->masterTimer()->bpmNumber());
×
1045
        }
1046
        break;
×
1047
        case Plugin:
×
1048
        {
1049
            m_doc->masterTimer()->setBeatSourceType(MasterTimer::External);
×
1050
            // reset the current BPM number and detect it from the MIDI beats
1051
            setBpmNumber(0);
×
1052
            m_beatTime->restart();
×
1053
        }
1054
        break;
×
1055
        case Audio:
×
1056
        {
1057
            m_doc->masterTimer()->setBeatSourceType(MasterTimer::External);
×
1058
            // reset the current BPM number and detect it from the audio input
1059
            setBpmNumber(0);
×
1060
            m_beatTime->restart();
×
1061
            QSharedPointer<AudioCapture> capture(m_doc->audioInputCapture());
×
1062
            m_inputCapture = capture.data();
×
1063
            connect(m_inputCapture, SIGNAL(beatDetected()), this, SLOT(slotProcessBeat()));
×
1064
            m_inputCapture->registerBandsNumber(4);
×
1065
        }
×
1066
        break;
×
1067
        case Disabled:
×
1068
        default:
1069
            m_doc->masterTimer()->setBeatSourceType(MasterTimer::None);
×
1070
            setBpmNumber(0);
×
1071
        break;
×
1072
    }
1073

1074
    emit beatGeneratorTypeChanged();
×
1075
}
1076

1077
InputOutputMap::BeatGeneratorType InputOutputMap::beatGeneratorType() const
×
1078
{
1079
    return m_beatGeneratorType;
×
1080
}
1081

1082
QString InputOutputMap::beatTypeToString(BeatGeneratorType type) const
1✔
1083
{
1084
    switch (type)
1✔
1085
    {
1086
        case Internal:  return "Internal";
×
1087
        case Plugin:    return "Plugin";
×
1088
        case Audio:     return "Audio";
×
1089
        default:        return "Disabled";
1✔
1090
    }
1091
}
1092

1093
InputOutputMap::BeatGeneratorType InputOutputMap::stringToBeatType(QString str)
×
1094
{
1095
    if (str == "Internal")
×
1096
        return Internal;
×
1097
    else if (str == "Plugin")
×
1098
        return Plugin;
×
1099
    else if (str == "Audio")
×
1100
        return Audio;
×
1101

1102
    return Disabled;
×
1103
}
1104

1105
void InputOutputMap::setBpmNumber(int bpm)
×
1106
{
1107
    if (m_beatGeneratorType == Disabled || bpm == m_currentBPM)
×
1108
        return;
×
1109

1110
    //qDebug() << "[InputOutputMap] set BPM to" << bpm;
1111
    m_currentBPM = bpm;
×
1112

1113
    if (bpm != 0)
×
1114
        m_doc->masterTimer()->requestBpmNumber(bpm);
×
1115

1116
    emit bpmNumberChanged(m_currentBPM);
×
1117
}
1118

1119
int InputOutputMap::bpmNumber() const
×
1120
{
1121
    if (m_beatGeneratorType == Disabled)
×
1122
        return 0;
×
1123

1124
    return m_currentBPM;
×
1125
}
1126

1127
void InputOutputMap::slotProcessBeat()
×
1128
{
1129
    // process the timer as first thing, to avoid wasting time
1130
    // with the operations below
1131
    qint64 elapsed = m_beatTime->elapsed();
×
1132
    m_beatTime->restart();
×
1133

1134
    int bpm = qRound(60000.0 / (float)elapsed);
×
1135
    float currBpmTime = 60000.0 / (float)m_currentBPM;
×
1136
    // here we check if the difference between the current BPM duration
1137
    // and the current time elapsed is within a range of +/-1ms.
1138
    // If it isn't, then the BPM number has really changed, otherwise
1139
    // it's just a tiny time drift
1140
    if (qAbs((float)elapsed - currBpmTime) > 1)
×
1141
        setBpmNumber(bpm);
×
1142

1143
    m_doc->masterTimer()->requestBeat();
×
1144
    emit beat();
×
1145
}
×
1146

1147
void InputOutputMap::slotMasterTimerBeat()
×
1148
{
1149
    if (m_beatGeneratorType != Internal)
×
1150
        return;
×
1151

1152
    emit beat();
×
1153
}
1154

1155
void InputOutputMap::slotPluginBeat(quint32 universe, quint32 channel, uchar value, const QString &key)
×
1156
{
1157
    Q_UNUSED(universe)
1158

1159
    // not interested in synthetic release or non-beat event
1160
    if (m_beatGeneratorType != Plugin || value == 0 || key != "beat")
×
1161
        return;
×
1162

1163
    qDebug() << "Plugin beat:" << channel << m_beatTime->elapsed();
×
1164

1165
    slotProcessBeat();
×
1166
}
1167

1168
/*********************************************************************
1169
 * Network server
1170
 *********************************************************************/
1171

1172
void InputOutputMap::setNetworkServerType(InputOutputMap::NetworkServerType type)
×
1173
{
1174
    m_networkServerType = type;
×
1175
}
×
1176

1177
InputOutputMap::NetworkServerType InputOutputMap::networkServerType() const
×
1178
{
1179
    return m_networkServerType;
×
1180
}
1181

1182
QString InputOutputMap::networkServerTypeToString(InputOutputMap::NetworkServerType type) const
1✔
1183
{
1184
    if (type == WebServer)
1✔
1185
        return "Web";
×
1186
    return "Native";
1✔
1187
}
1188

1189
InputOutputMap::NetworkServerType InputOutputMap::stringToNetworkServerType(const QString &str) const
×
1190
{
1191
    if (str.compare("Web", Qt::CaseInsensitive) == 0)
×
1192
        return WebServer;
×
1193
    return NativeServer;
×
1194
}
1195

1196
void InputOutputMap::setNetworkServerAutoStart(bool enable)
×
1197
{
1198
    m_networkServerAutoStart = enable;
×
1199
}
×
1200

1201
bool InputOutputMap::networkServerAutoStart() const
×
1202
{
1203
    return m_networkServerAutoStart;
×
1204
}
1205

1206
void InputOutputMap::setNetworkServerName(QString name)
×
1207
{
1208
    m_networkServerName = name;
×
1209
}
×
1210

1211
QString InputOutputMap::networkServerName() const
×
1212
{
1213
    return m_networkServerName;
×
1214
}
1215

1216
void InputOutputMap::setNetworkServerPassword(QString password)
×
1217
{
1218
    m_networkServerPassword = password;
×
1219
}
×
1220

1221
QString InputOutputMap::networkServerPassword() const
×
1222
{
1223
    return m_networkServerPassword;
×
1224
}
1225

1226
/*********************************************************************
1227
 * Defaults - !! FALLBACK !!
1228
 *********************************************************************/
1229

1230
void InputOutputMap::loadDefaults()
×
1231
{
1232
    /* ************************ INPUT *********************************** */
1233
    QSettings settings;
×
1234
    QString plugin;
×
1235
    QString input;
×
1236
    QString key;
×
1237

1238
    for (quint32 i = 0; i < universesCount(); i++)
×
1239
    {
1240
        QString profileName;
×
1241
        bool passthrough;
1242

1243
        /* Plugin name */
1244
        key = QString("/inputmap/universe%1/plugin/").arg(i);
×
1245
        plugin = settings.value(key).toString();
×
1246

1247
        /* Plugin input */
1248
        key = QString("/inputmap/universe%1/input/").arg(i);
×
1249
        input = settings.value(key).toString();
×
1250

1251
        /* Input profile */
1252
        key = QString("/inputmap/universe%1/profile/").arg(i);
×
1253
        profileName = settings.value(key).toString();
×
1254

1255
        key = QString("/inputmap/universe%1/passthrough/").arg(i);
×
1256
        passthrough = settings.value(key).toBool();
×
1257
        if (passthrough == true)
×
1258
            m_universeArray.at(i)->setPassthrough(passthrough);
×
1259

1260
        /* Do the mapping */
1261
        if (plugin != KInputNone && input != KInputNone)
×
1262
            setInputPatch(i, plugin, "", "", input.toUInt(), profileName);
×
1263
    }
×
1264

1265
    /* ************************ OUTPUT *********************************** */
1266
    QString output;
×
1267
    QString fb_plugin;
×
1268
    QString feedback;
×
1269

1270
    for (quint32 i = 0; i < universesCount(); i++)
×
1271
    {
1272
        /* Plugin name */
1273
        key = QString("/outputmap/universe%1/plugin/").arg(i);
×
1274
        plugin = settings.value(key).toString();
×
1275

1276
        /* Plugin output */
1277
        key = QString("/outputmap/universe%1/output/").arg(i);
×
1278
        output = settings.value(key).toString();
×
1279

1280
        /* Feedback plugin name */
1281
        key = QString("/outputmap/universe%1/feedbackplugin/").arg(i);
×
1282
        fb_plugin = settings.value(key).toString();
×
1283

1284
        /* Feedback line */
1285
        key = QString("/outputmap/universe%1/feedback/").arg(i);
×
1286
        feedback = settings.value(key).toString();
×
1287

1288
        if (plugin != KOutputNone && output != KOutputNone)
×
1289
            setOutputPatch(i, plugin, "", "", output.toUInt());
×
1290

1291
        if (fb_plugin != KOutputNone && feedback != KOutputNone)
×
1292
            setOutputPatch(i, fb_plugin, "", "", feedback.toUInt(), true);
×
1293
    }
1294
}
×
1295

1296
void InputOutputMap::saveDefaults() const
×
1297
{
1298
    /* ************************ INPUT *********************************** */
1299
    QSettings settings;
×
1300
    QString key;
×
1301

1302
    for (quint32 i = 0; i < universesCount(); i++)
×
1303
    {
1304
        InputPatch* inPatch = inputPatch(i);
×
1305

1306
        /* Plugin name */
1307
        key = QString("/inputmap/universe%1/plugin/").arg(i);
×
1308
        if (inPatch != NULL)
×
1309
            settings.setValue(key, inPatch->pluginName());
×
1310
        else
1311
            settings.setValue(key, KInputNone);
×
1312

1313
        /* Plugin input */
1314
        key = QString("/inputmap/universe%1/input/").arg(i);
×
1315
        if (inPatch != NULL)
×
1316
            settings.setValue(key, QString::number(inPatch->input()));
×
1317
        else
1318
            settings.setValue(key, KInputNone);
×
1319

1320
        /* Input profile */
1321
        key = QString("/inputmap/universe%1/profile/").arg(i);
×
1322
        if (inPatch != NULL)
×
1323
            settings.setValue(key, inPatch->profileName());
×
1324
        else
1325
            settings.setValue(key, KInputNone);
×
1326

1327
        /* Passthrough */
1328
        key = QString("/inputmap/universe%1/passthrough/").arg(i);
×
1329
        bool passthrough = m_universeArray.at(i)->passthrough();
×
1330
        if (passthrough == true)
×
1331
            settings.setValue(key, passthrough);
×
1332
        else
1333
            settings.remove(key);
×
1334
    }
1335

1336
    /* ************************ OUTPUT *********************************** */
1337

1338
    for (quint32 i = 0; i < universesCount(); i++)
×
1339
    {
1340
        OutputPatch* outPatch = outputPatch(i);
×
1341
        OutputPatch* fbPatch = feedbackPatch(i);
×
1342

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

1345
        /* Plugin name */
1346
        if (outPatch != NULL)
×
1347
            settings.setValue(key, outPatch->pluginName());
×
1348
        else
1349
            settings.setValue(key, KOutputNone);
×
1350

1351
        /* Plugin output */
1352
        key = QString("/outputmap/universe%1/output/").arg(i);
×
1353
        if (outPatch != NULL)
×
1354
            settings.setValue(key, outPatch->output());
×
1355
        else
1356
            settings.setValue(key, KOutputNone);
×
1357

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

1360
        /* Feedback plugin name */
1361
        if (fbPatch != NULL)
×
1362
            settings.setValue(key, fbPatch->pluginName());
×
1363
        else
1364
            settings.setValue(key, KOutputNone);
×
1365

1366
        /* Feedback plugin output */
1367
        key = QString("/outputmap/universe%1/feedback/").arg(i);
×
1368
        if (fbPatch != NULL)
×
1369
            settings.setValue(key, QString::number(fbPatch->output()));
×
1370
        else
1371
            settings.setValue(key, KOutputNone);
×
1372
    }
1373
}
×
1374

1375
/*********************************************************************
1376
 * Load & Save
1377
 *********************************************************************/
1378

1379
bool InputOutputMap::loadXML(QXmlStreamReader &root)
×
1380
{
1381
    if (root.name() != KXMLIOMap)
×
1382
    {
1383
        qWarning() << Q_FUNC_INFO << "InputOutputMap node not found";
×
1384
        return false;
×
1385
    }
1386

1387
    /** Reset the current universe list and read the new one */
1388
    removeAllUniverses();
×
1389

1390
    while (root.readNextStartElement())
×
1391
    {
1392
        if (root.name() == KXMLQLCUniverse)
×
1393
        {
1394
            quint32 id = InputOutputMap::invalidUniverse();
×
1395
            if (root.attributes().hasAttribute(KXMLQLCUniverseID))
×
1396
                id = root.attributes().value(KXMLQLCUniverseID).toString().toUInt();
×
1397
            if (addUniverse(id))
×
1398
            {
1399
                Universe *uni = m_universeArray.last();
×
1400
                uni->loadXML(root, m_universeArray.count() - 1, this);
×
1401
            }
1402
        }
1403
        else if (root.name() == KXMLIOBeatGenerator)
×
1404
        {
1405
            QXmlStreamAttributes attrs = root.attributes();
×
1406

1407
            if (attrs.hasAttribute(KXMLIOBeatType))
×
1408
                setBeatGeneratorType(stringToBeatType(attrs.value(KXMLIOBeatType).toString()));
×
1409

1410
            if (attrs.hasAttribute(KXMLIOBeatsPerMinute))
×
1411
                setBpmNumber(attrs.value(KXMLIOBeatsPerMinute).toInt());
×
1412

1413
            root.skipCurrentElement();
×
1414
        }
×
1415
        else if (root.name() == KXMLIONetworkServer)
×
1416
        {
1417
            QXmlStreamAttributes attrs = root.attributes();
×
1418
            NetworkServerType type = NativeServer;
×
1419

1420
            if (attrs.hasAttribute(KXMLIONetworkType))
×
1421
                type = stringToNetworkServerType(attrs.value(KXMLIONetworkType).toString());
×
1422
            setNetworkServerType(type);
×
1423

1424
            if (attrs.hasAttribute(KXMLIONetworkAutoStart))
×
1425
                setNetworkServerAutoStart(attrs.value(KXMLIONetworkAutoStart) == KXMLQLCTrue);
×
1426

1427
            if (type == NativeServer)
×
1428
            {
1429
                setNetworkServerName(attrs.value(KXMLIONetworkName).toString());
×
1430
                setNetworkServerPassword(attrs.value(KXMLIONetworkPassword).toString());
×
1431
            }
1432
            else
1433
            {
1434
                setNetworkServerName(QString());
×
1435
                setNetworkServerPassword(QString());
×
1436
            }
1437

1438
            root.skipCurrentElement();
×
1439
        }
×
1440
        else
1441
        {
1442
            qWarning() << Q_FUNC_INFO << "Unknown IO Map tag:" << root.name();
×
1443
            root.skipCurrentElement();
×
1444
        }
1445
    }
1446

1447
    return true;
×
1448
}
1449

1450
bool InputOutputMap::saveXML(QXmlStreamWriter *doc) const
1✔
1451
{
1452
    Q_ASSERT(doc != NULL);
1✔
1453

1454
    /* IO Map Instance entry */
1455
    doc->writeStartElement(KXMLIOMap);
2✔
1456

1457
    doc->writeStartElement(KXMLIOBeatGenerator);
2✔
1458
    doc->writeAttribute(KXMLIOBeatType, beatTypeToString(m_beatGeneratorType));
2✔
1459
    doc->writeAttribute(KXMLIOBeatsPerMinute, QString::number(m_currentBPM));
2✔
1460
    doc->writeEndElement();
1✔
1461

1462
    doc->writeStartElement(KXMLIONetworkServer);
2✔
1463
    doc->writeAttribute(KXMLIONetworkType, networkServerTypeToString(m_networkServerType));
2✔
1464
    doc->writeAttribute(KXMLIONetworkAutoStart, m_networkServerAutoStart ? KXMLQLCTrue : KXMLQLCFalse);
3✔
1465
    if (m_networkServerType == NativeServer)
1✔
1466
    {
1467
        doc->writeAttribute(KXMLIONetworkName, m_networkServerName);
2✔
1468
        doc->writeAttribute(KXMLIONetworkPassword, m_networkServerPassword);
2✔
1469
    }
1470
    doc->writeEndElement();
1✔
1471

1472
    foreach (Universe *uni, m_universeArray)
5✔
1473
        uni->saveXML(doc);
5✔
1474

1475
    doc->writeEndElement();
1✔
1476

1477
    return true;
1✔
1478
}
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