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

mcallegari / qlcplus / 23157810099

16 Mar 2026 05:44PM UTC coverage: 33.973% (-0.08%) from 34.05%
23157810099

push

github

mcallegari
Back to 5.2.2/4.14.5 debug

17651 of 51956 relevant lines covered (33.97%)

19863.28 hits per line

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

62.15
/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)
219✔
46
    : QObject(NULL)
47
    , m_doc(doc)
219✔
48
    , m_blackout(false)
219✔
49
    , m_universeChanged(false)
219✔
50
    , m_localProfilesLoaded(false)
219✔
51
    , m_currentBPM(0)
219✔
52
    , m_beatTime(new QElapsedTimer())
219✔
53
    , m_networkServerType(NativeServer)
219✔
54
    , m_networkServerAutoStart(false)
438✔
55
{
56
    m_grandMaster = new GrandMaster(this);
219✔
57
    for (quint32 i = 0; i < universes; i++)
1,095✔
58
        addUniverse();
876✔
59

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

65
InputOutputMap::~InputOutputMap()
415✔
66
{
67
    removeAllUniverses();
219✔
68
    delete m_grandMaster;
219✔
69
    delete m_beatTime;
219✔
70
    qDeleteAll(m_profiles);
219✔
71
}
415✔
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,092✔
131
{
132
    return UINT_MAX;
2,092✔
133
}
134

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

141
        if (id == InputOutputMap::invalidUniverse())
879✔
142
        {
143
            id = universesCount();
877✔
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);
878✔
167
        connect(m_doc->masterTimer(), SIGNAL(tickReady()), uni, SLOT(tick()), Qt::QueuedConnection);
878✔
168
        connect(uni, SIGNAL(universeWritten(quint32,QByteArray)), this, SIGNAL(universeWritten(quint32,QByteArray)));
878✔
169
        m_universeArray.append(uni);
878✔
170
    }
879✔
171

172
    emit universeAdded(id);
878✔
173
    return true;
878✔
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()
220✔
199
{
200
    QMutexLocker locker(&m_universeMutex);
220✔
201
    qDeleteAll(m_universeArray);
220✔
202
    m_universeArray.clear();
220✔
203
    return true;
220✔
204
}
220✔
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,685✔
277
{
278
    return (quint32)m_universeArray.count();
1,685✔
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,055✔
296
{
297
    m_universeMutex.lock();
1,055✔
298
    return m_universeArray;
1,055✔
299
}
300

301
void InputOutputMap::releaseUniverses(bool changed)
1,055✔
302
{
303
    m_universeChanged = changed;
1,055✔
304
    m_universeMutex.unlock();
1,055✔
305
}
1,055✔
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
78✔
339
{
340
    Q_ASSERT(m_grandMaster != NULL);
78✔
341

342
    return m_grandMaster->channelMode();
78✔
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
154✔
359
{
360
    Q_ASSERT(m_grandMaster != NULL);
154✔
361

362
    return m_grandMaster->valueMode();
154✔
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, quint32 input,
399
                                   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 (!inputUID.isEmpty() && plugin != NULL)
6✔
426
    {
427
        QStringList inputs = plugin->inputs();
5✔
428
        int lIdx = inputs.indexOf(inputUID);
5✔
429
        if (lIdx != -1)
5✔
430
        {
431
            qDebug() << "[IOMAP] Found match on input by name on universe" << universe << "-" << input << "vs" << lIdx;
5✔
432
            input = lIdx;
5✔
433
        }
434
        else
435
        {
436
            qDebug() << "[IOMAP] !!No match found!! for input on universe" << universe << "-" << input << inputUID;
×
437
            qDebug() << plugin->inputs();
×
438
        }
439
    }
5✔
440

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

461
    if (ip != NULL && currProfile != ip->profile())
6✔
462
        emit profileChanged(universe, ip->profileName());
3✔
463

464
    return true;
6✔
465
}
6✔
466

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

476
    InputPatch *currInPatch = m_universeArray.at(universe)->inputPatch();
×
477
    if (currInPatch != NULL)
×
478
        currInPatch->set(profile(profileName));
×
479

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

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

496
    QMutexLocker locker(&m_universeMutex);
19✔
497
    QLCIOPlugin *plugin = m_doc->ioPluginCache()->plugin(pluginName);
19✔
498

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

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

520
    return false;
521
}
19✔
522

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

531
    return m_universeArray.at(universe)->outputPatchesCount();
4✔
532
}
533

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

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

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

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

570
    return list;
23✔
571
}
×
572

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

582
    return QLCIOPlugin::invalidLine();
13✔
583
}
584

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

598
    return QLCIOPlugin::invalidLine();
1✔
599
}
600

601
/*****************************************************************************
602
 * Plugins
603
 *****************************************************************************/
604

605
QString InputOutputMap::pluginDescription(const QString &pluginName) const
2✔
606
{
607
    QLCIOPlugin* plugin = NULL;
2✔
608

609
    if (pluginName.isEmpty() == false)
2✔
610
        plugin = m_doc->ioPluginCache()->plugin(pluginName);
2✔
611

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

620
void InputOutputMap::removeDuplicates(QStringList &list) const
3✔
621
{
622
    if (list.count() == 1)
3✔
623
        return;
×
624

625
    int c = 2;
3✔
626

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

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

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

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

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

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

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

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

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

722
    if (pluginName.isEmpty() == false)
9✔
723
        inputPlugin = m_doc->ioPluginCache()->plugin(pluginName);
9✔
724

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

737
    return info;
9✔
738
}
×
739

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

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

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

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

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

786
            if (op != NULL && op->plugin() == plugin)
×
787
            {
788
                /*success = */ op->reconnect();
×
789
            }
790
        }
791

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

794
        if (ip != NULL && ip->plugin() == plugin)
32✔
795
        {
796
            /*success = */ ip->reconnect();
×
797
        }
798

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

807
    emit pluginConfigurationChanged(plugin->name(), success);
8✔
808
}
8✔
809

810
/*****************************************************************************
811
 * Profiles
812
 *****************************************************************************/
813

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

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

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

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

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

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

870
        m_localProfilesLoaded = true;
×
871

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

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

887
    return NULL;
×
888
}
125✔
889

890
bool InputOutputMap::addProfile(QLCInputProfile* profile)
81✔
891
{
892
    Q_ASSERT(profile != NULL);
81✔
893

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

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

920
    return false;
1✔
921
}
922

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

929
    if (src->universe() >= universesCount())
5✔
930
        return false;
1✔
931

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

938
        ushort page = src->page();
1✔
939
        ushort channel = (src->channel() & 0x0000FFFF) + 1;
1✔
940

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

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

957
        ushort page = src->page();
1✔
958
        ushort channel = (src->channel() & 0x0000FFFF) + 1;
1✔
959

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

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

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

978
        ich = profile->channel(channel);
2✔
979
        if (ich != NULL)
2✔
980
            name = ich->name();
1✔
981
        else
982
            name = QString("?");
1✔
983

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

991
    return true;
3✔
992
}
993

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

1000
QDir InputOutputMap::systemProfileDirectory()
1✔
1001
{
1002
    return QLCFile::systemDirectory(QString(INPUTPROFILEDIR), QString(KExtInputProfile));
2✔
1003
}
1004

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

1011
/*********************************************************************
1012
 * Beats
1013
 *********************************************************************/
1014

1015
void InputOutputMap::setBeatGeneratorType(InputOutputMap::BeatGeneratorType type)
×
1016
{
1017
    if (type == m_beatGeneratorType)
×
1018
        return;
×
1019

1020
    if (m_beatGeneratorType == Audio)
×
1021
    {
1022
        m_inputCapture->unregisterBandsNumber(4);
×
1023
        disconnect(m_inputCapture, SIGNAL(beatDetected()), this, SLOT(slotProcessBeat()));
×
1024
    }
1025

1026
    m_beatGeneratorType = type;
×
1027
    qDebug() << "[InputOutputMap] setting beat type:" << m_beatGeneratorType;
×
1028

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

1064
    emit beatGeneratorTypeChanged();
×
1065
}
1066

1067
InputOutputMap::BeatGeneratorType InputOutputMap::beatGeneratorType() const
×
1068
{
1069
    return m_beatGeneratorType;
×
1070
}
1071

1072
QString InputOutputMap::beatTypeToString(BeatGeneratorType type) const
1✔
1073
{
1074
    switch (type)
1✔
1075
    {
1076
        case Internal:  return "Internal";
×
1077
        case Plugin:    return "Plugin";
×
1078
        case Audio:     return "Audio";
×
1079
        default:        return "Disabled";
1✔
1080
    }
1081
}
1082

1083
InputOutputMap::BeatGeneratorType InputOutputMap::stringToBeatType(QString str)
×
1084
{
1085
    if (str == "Internal")
×
1086
        return Internal;
×
1087
    else if (str == "Plugin")
×
1088
        return Plugin;
×
1089
    else if (str == "Audio")
×
1090
        return Audio;
×
1091

1092
    return Disabled;
×
1093
}
1094

1095
void InputOutputMap::setBpmNumber(int bpm)
×
1096
{
1097
    if (m_beatGeneratorType == Disabled || bpm == m_currentBPM)
×
1098
        return;
×
1099

1100
    //qDebug() << "[InputOutputMap] set BPM to" << bpm;
1101
    m_currentBPM = bpm;
×
1102

1103
    if (bpm != 0)
×
1104
        m_doc->masterTimer()->requestBpmNumber(bpm);
×
1105

1106
    emit bpmNumberChanged(m_currentBPM);
×
1107
}
1108

1109
int InputOutputMap::bpmNumber() const
×
1110
{
1111
    if (m_beatGeneratorType == Disabled)
×
1112
        return 0;
×
1113

1114
    return m_currentBPM;
×
1115
}
1116

1117
void InputOutputMap::slotProcessBeat()
×
1118
{
1119
    // process the timer as first thing, to avoid wasting time
1120
    // with the operations below
1121
    qint64 elapsed = m_beatTime->elapsed();
×
1122
    m_beatTime->restart();
×
1123

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

1133
    m_doc->masterTimer()->requestBeat();
×
1134
    emit beat();
×
1135
}
×
1136

1137
void InputOutputMap::slotMasterTimerBeat()
×
1138
{
1139
    if (m_beatGeneratorType != Internal)
×
1140
        return;
×
1141

1142
    emit beat();
×
1143
}
1144

1145
void InputOutputMap::slotPluginBeat(quint32 universe, quint32 channel, uchar value, const QString &key)
×
1146
{
1147
    Q_UNUSED(universe)
1148

1149
    // not interested in synthetic release or non-beat event
1150
    if (m_beatGeneratorType != Plugin || value == 0 || key != "beat")
×
1151
        return;
×
1152

1153
    qDebug() << "Plugin beat:" << channel << m_beatTime->elapsed();
×
1154

1155
    slotProcessBeat();
×
1156
}
1157

1158
/*********************************************************************
1159
 * Network server
1160
 *********************************************************************/
1161

1162
void InputOutputMap::setNetworkServerType(InputOutputMap::NetworkServerType type)
×
1163
{
1164
    m_networkServerType = type;
×
1165
}
×
1166

1167
InputOutputMap::NetworkServerType InputOutputMap::networkServerType() const
×
1168
{
1169
    return m_networkServerType;
×
1170
}
1171

1172
QString InputOutputMap::networkServerTypeToString(InputOutputMap::NetworkServerType type) const
1✔
1173
{
1174
    if (type == WebServer)
1✔
1175
        return "Web";
×
1176
    return "Native";
1✔
1177
}
1178

1179
InputOutputMap::NetworkServerType InputOutputMap::stringToNetworkServerType(const QString &str) const
×
1180
{
1181
    if (str.compare("Web", Qt::CaseInsensitive) == 0)
×
1182
        return WebServer;
×
1183
    return NativeServer;
×
1184
}
1185

1186
void InputOutputMap::setNetworkServerAutoStart(bool enable)
×
1187
{
1188
    m_networkServerAutoStart = enable;
×
1189
}
×
1190

1191
bool InputOutputMap::networkServerAutoStart() const
×
1192
{
1193
    return m_networkServerAutoStart;
×
1194
}
1195

1196
void InputOutputMap::setNetworkServerName(QString name)
×
1197
{
1198
    m_networkServerName = name;
×
1199
}
×
1200

1201
QString InputOutputMap::networkServerName() const
×
1202
{
1203
    return m_networkServerName;
×
1204
}
1205

1206
void InputOutputMap::setNetworkServerPassword(QString password)
×
1207
{
1208
    m_networkServerPassword = password;
×
1209
}
×
1210

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

1216
/*********************************************************************
1217
 * Defaults - !! FALLBACK !!
1218
 *********************************************************************/
1219

1220
void InputOutputMap::loadDefaults()
×
1221
{
1222
    /* ************************ INPUT *********************************** */
1223
    QSettings settings;
×
1224
    QString plugin;
×
1225
    QString input;
×
1226
    QString key;
×
1227

1228
    for (quint32 i = 0; i < universesCount(); i++)
×
1229
    {
1230
        QString profileName;
×
1231
        bool passthrough;
1232

1233
        /* Plugin name */
1234
        key = QString("/inputmap/universe%1/plugin/").arg(i);
×
1235
        plugin = settings.value(key).toString();
×
1236

1237
        /* Plugin input */
1238
        key = QString("/inputmap/universe%1/input/").arg(i);
×
1239
        input = settings.value(key).toString();
×
1240

1241
        /* Input profile */
1242
        key = QString("/inputmap/universe%1/profile/").arg(i);
×
1243
        profileName = settings.value(key).toString();
×
1244

1245
        key = QString("/inputmap/universe%1/passthrough/").arg(i);
×
1246
        passthrough = settings.value(key).toBool();
×
1247
        if (passthrough == true)
×
1248
            m_universeArray.at(i)->setPassthrough(passthrough);
×
1249

1250
        /* Do the mapping */
1251
        if (plugin != KInputNone && input != KInputNone)
×
1252
            setInputPatch(i, plugin, "", input.toUInt(), profileName);
×
1253
    }
×
1254

1255
    /* ************************ OUTPUT *********************************** */
1256
    QString output;
×
1257
    QString fb_plugin;
×
1258
    QString feedback;
×
1259

1260
    for (quint32 i = 0; i < universesCount(); i++)
×
1261
    {
1262
        /* Plugin name */
1263
        key = QString("/outputmap/universe%1/plugin/").arg(i);
×
1264
        plugin = settings.value(key).toString();
×
1265

1266
        /* Plugin output */
1267
        key = QString("/outputmap/universe%1/output/").arg(i);
×
1268
        output = settings.value(key).toString();
×
1269

1270
        /* Feedback plugin name */
1271
        key = QString("/outputmap/universe%1/feedbackplugin/").arg(i);
×
1272
        fb_plugin = settings.value(key).toString();
×
1273

1274
        /* Feedback line */
1275
        key = QString("/outputmap/universe%1/feedback/").arg(i);
×
1276
        feedback = settings.value(key).toString();
×
1277

1278
        if (plugin != KOutputNone && output != KOutputNone)
×
1279
            setOutputPatch(i, plugin, "", output.toUInt());
×
1280

1281
        if (fb_plugin != KOutputNone && feedback != KOutputNone)
×
1282
            setOutputPatch(i, fb_plugin, "", feedback.toUInt(), true);
×
1283
    }
1284
}
×
1285

1286
void InputOutputMap::saveDefaults() const
×
1287
{
1288
    /* ************************ INPUT *********************************** */
1289
    QSettings settings;
×
1290
    QString key;
×
1291

1292
    for (quint32 i = 0; i < universesCount(); i++)
×
1293
    {
1294
        InputPatch* inPatch = inputPatch(i);
×
1295

1296
        /* Plugin name */
1297
        key = QString("/inputmap/universe%1/plugin/").arg(i);
×
1298
        if (inPatch != NULL)
×
1299
            settings.setValue(key, inPatch->pluginName());
×
1300
        else
1301
            settings.setValue(key, KInputNone);
×
1302

1303
        /* Plugin input */
1304
        key = QString("/inputmap/universe%1/input/").arg(i);
×
1305
        if (inPatch != NULL)
×
1306
            settings.setValue(key, QString::number(inPatch->input()));
×
1307
        else
1308
            settings.setValue(key, KInputNone);
×
1309

1310
        /* Input profile */
1311
        key = QString("/inputmap/universe%1/profile/").arg(i);
×
1312
        if (inPatch != NULL)
×
1313
            settings.setValue(key, inPatch->profileName());
×
1314
        else
1315
            settings.setValue(key, KInputNone);
×
1316

1317
        /* Passthrough */
1318
        key = QString("/inputmap/universe%1/passthrough/").arg(i);
×
1319
        bool passthrough = m_universeArray.at(i)->passthrough();
×
1320
        if (passthrough == true)
×
1321
            settings.setValue(key, passthrough);
×
1322
        else
1323
            settings.remove(key);
×
1324
    }
1325

1326
    /* ************************ OUTPUT *********************************** */
1327

1328
    for (quint32 i = 0; i < universesCount(); i++)
×
1329
    {
1330
        OutputPatch* outPatch = outputPatch(i);
×
1331
        OutputPatch* fbPatch = feedbackPatch(i);
×
1332

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

1335
        /* Plugin name */
1336
        if (outPatch != NULL)
×
1337
            settings.setValue(key, outPatch->pluginName());
×
1338
        else
1339
            settings.setValue(key, KOutputNone);
×
1340

1341
        /* Plugin output */
1342
        key = QString("/outputmap/universe%1/output/").arg(i);
×
1343
        if (outPatch != NULL)
×
1344
            settings.setValue(key, outPatch->output());
×
1345
        else
1346
            settings.setValue(key, KOutputNone);
×
1347

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

1350
        /* Feedback plugin name */
1351
        if (fbPatch != NULL)
×
1352
            settings.setValue(key, fbPatch->pluginName());
×
1353
        else
1354
            settings.setValue(key, KOutputNone);
×
1355

1356
        /* Feedback plugin output */
1357
        key = QString("/outputmap/universe%1/feedback/").arg(i);
×
1358
        if (fbPatch != NULL)
×
1359
            settings.setValue(key, QString::number(fbPatch->output()));
×
1360
        else
1361
            settings.setValue(key, KOutputNone);
×
1362
    }
1363
}
×
1364

1365
/*********************************************************************
1366
 * Load & Save
1367
 *********************************************************************/
1368

1369
bool InputOutputMap::loadXML(QXmlStreamReader &root)
×
1370
{
1371
    if (root.name() != KXMLIOMap)
×
1372
    {
1373
        qWarning() << Q_FUNC_INFO << "InputOutputMap node not found";
×
1374
        return false;
×
1375
    }
1376

1377
    /** Reset the current universe list and read the new one */
1378
    removeAllUniverses();
×
1379

1380
    while (root.readNextStartElement())
×
1381
    {
1382
        if (root.name() == KXMLQLCUniverse)
×
1383
        {
1384
            quint32 id = InputOutputMap::invalidUniverse();
×
1385
            if (root.attributes().hasAttribute(KXMLQLCUniverseID))
×
1386
                id = root.attributes().value(KXMLQLCUniverseID).toString().toUInt();
×
1387
            if (addUniverse(id))
×
1388
            {
1389
                Universe *uni = m_universeArray.last();
×
1390
                uni->loadXML(root, m_universeArray.count() - 1, this);
×
1391
            }
1392
        }
1393
        else if (root.name() == KXMLIOBeatGenerator)
×
1394
        {
1395
            QXmlStreamAttributes attrs = root.attributes();
×
1396

1397
            if (attrs.hasAttribute(KXMLIOBeatType))
×
1398
                setBeatGeneratorType(stringToBeatType(attrs.value(KXMLIOBeatType).toString()));
×
1399

1400
            if (attrs.hasAttribute(KXMLIOBeatsPerMinute))
×
1401
                setBpmNumber(attrs.value(KXMLIOBeatsPerMinute).toInt());
×
1402

1403
            root.skipCurrentElement();
×
1404
        }
×
1405
        else if (root.name() == KXMLIONetworkServer)
×
1406
        {
1407
            QXmlStreamAttributes attrs = root.attributes();
×
1408
            NetworkServerType type = NativeServer;
×
1409

1410
            if (attrs.hasAttribute(KXMLIONetworkType))
×
1411
                type = stringToNetworkServerType(attrs.value(KXMLIONetworkType).toString());
×
1412
            setNetworkServerType(type);
×
1413

1414
            if (attrs.hasAttribute(KXMLIONetworkAutoStart))
×
1415
                setNetworkServerAutoStart(attrs.value(KXMLIONetworkAutoStart) == KXMLQLCTrue);
×
1416

1417
            if (type == NativeServer)
×
1418
            {
1419
                setNetworkServerName(attrs.value(KXMLIONetworkName).toString());
×
1420
                setNetworkServerPassword(attrs.value(KXMLIONetworkPassword).toString());
×
1421
            }
1422
            else
1423
            {
1424
                setNetworkServerName(QString());
×
1425
                setNetworkServerPassword(QString());
×
1426
            }
1427

1428
            root.skipCurrentElement();
×
1429
        }
×
1430
        else
1431
        {
1432
            qWarning() << Q_FUNC_INFO << "Unknown IO Map tag:" << root.name();
×
1433
            root.skipCurrentElement();
×
1434
        }
1435
    }
1436

1437
    return true;
×
1438
}
1439

1440
bool InputOutputMap::saveXML(QXmlStreamWriter *doc) const
1✔
1441
{
1442
    Q_ASSERT(doc != NULL);
1✔
1443

1444
    /* IO Map Instance entry */
1445
    doc->writeStartElement(KXMLIOMap);
2✔
1446

1447
    doc->writeStartElement(KXMLIOBeatGenerator);
2✔
1448
    doc->writeAttribute(KXMLIOBeatType, beatTypeToString(m_beatGeneratorType));
2✔
1449
    doc->writeAttribute(KXMLIOBeatsPerMinute, QString::number(m_currentBPM));
2✔
1450
    doc->writeEndElement();
1✔
1451

1452
    doc->writeStartElement(KXMLIONetworkServer);
2✔
1453
    doc->writeAttribute(KXMLIONetworkType, networkServerTypeToString(m_networkServerType));
2✔
1454
    doc->writeAttribute(KXMLIONetworkAutoStart, m_networkServerAutoStart ? KXMLQLCTrue : KXMLQLCFalse);
3✔
1455
    if (m_networkServerType == NativeServer)
1✔
1456
    {
1457
        doc->writeAttribute(KXMLIONetworkName, m_networkServerName);
2✔
1458
        doc->writeAttribute(KXMLIONetworkPassword, m_networkServerPassword);
2✔
1459
    }
1460
    doc->writeEndElement();
1✔
1461

1462
    foreach (Universe *uni, m_universeArray)
5✔
1463
        uni->saveXML(doc);
5✔
1464

1465
    doc->writeEndElement();
1✔
1466

1467
    return true;
1✔
1468
}
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