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

mcallegari / qlcplus / 28821771003

06 Jul 2026 08:40PM UTC coverage: 35.279% (-0.006%) from 35.285%
28821771003

Pull #2062

github

web-flow
Merge 069b05c1a into 17e74302a
Pull Request #2062: engine: fix beat tracker integration and add comb-ACF tempo tracker (issue #1881)

1 of 16 new or added lines in 1 file covered. (6.25%)

1 existing line in 1 file now uncovered.

18561 of 52612 relevant lines covered (35.28%)

40978.5 hits per line

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

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

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

66
InputOutputMap::~InputOutputMap()
445✔
67
{
68
    removeAllUniverses();
234✔
69
    delete m_grandMaster;
234✔
70
    delete m_beatTime;
234✔
71
    qDeleteAll(m_profiles);
234✔
72
}
445✔
73

74
/*****************************************************************************
75
 * Blackout
76
 *****************************************************************************/
77

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

85
    return m_blackout;
4✔
86
}
87

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

94
    m_blackout = blackout;
5✔
95

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

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

111
    emit blackoutChanged(m_blackout);
5✔
112

113
    return true;
5✔
114
}
115

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

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

127
/*****************************************************************************
128
 * Universes
129
 *****************************************************************************/
130

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

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

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

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

173
    emit universeAdded(id);
934✔
174
    return true;
934✔
175
}
176

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

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

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

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

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

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

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

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

218
    return invalidUniverse();
1✔
219
}
220

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

226
    return QString();
1✔
227
}
228

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

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

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

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

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

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

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

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

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

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

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

293
    return NULL;
×
294
}
295

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

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

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

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

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

324
/*********************************************************************
325
 * Grand Master
326
 *********************************************************************/
327

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

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

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

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

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

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

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

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

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

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

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

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

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

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

387
/*********************************************************************
388
 * Patch
389
 *********************************************************************/
390

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

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

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

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

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

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

470
    return true;
6✔
471
}
6✔
472

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

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

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

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

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

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

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

531
    return false;
532
}
19✔
533

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

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

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

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

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

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

581
    return list;
23✔
582
}
×
583

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

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

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

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

612
/*****************************************************************************
613
 * Plugins
614
 *****************************************************************************/
615

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

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

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

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

636
    int c = 2;
3✔
637

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

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

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

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

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

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

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

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

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

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

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

748
    return info;
9✔
749
}
×
750

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

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

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

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

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

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

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

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

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

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

821
/*****************************************************************************
822
 * Profiles
823
 *****************************************************************************/
824

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

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

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

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

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

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

881
        m_localProfilesLoaded = true;
×
882

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

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

898
    return NULL;
×
899
}
128✔
900

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

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

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

931
    return false;
1✔
932
}
933

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

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

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

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

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

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

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

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

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

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

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

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

1002
    return true;
3✔
1003
}
1004

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

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

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

1022
/*********************************************************************
1023
 * Beats
1024
 *********************************************************************/
1025

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

1031
    if (m_beatGeneratorType == Audio)
×
1032
    {
1033
        m_inputCapture->unregisterBandsNumber(4);
×
1034
        disconnect(m_inputCapture, SIGNAL(beatDetected()), this, SLOT(slotProcessBeat()));
×
NEW
1035
        disconnect(m_inputCapture, SIGNAL(beatBpmChanged(int)), this, SLOT(slotAudioBpmChanged(int)));
×
NEW
1036
        m_audioTrackerBpm = 0;
×
1037
    }
1038

1039
    m_beatGeneratorType = type;
×
1040
    qDebug() << "[InputOutputMap] setting beat type:" << m_beatGeneratorType;
×
1041

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

1079
    emit beatGeneratorTypeChanged();
×
1080
}
1081

1082
InputOutputMap::BeatGeneratorType InputOutputMap::beatGeneratorType() const
×
1083
{
1084
    return m_beatGeneratorType;
×
1085
}
1086

1087
QString InputOutputMap::beatTypeToString(BeatGeneratorType type) const
1✔
1088
{
1089
    switch (type)
1✔
1090
    {
1091
        case Internal:  return "Internal";
×
1092
        case Plugin:    return "Plugin";
×
1093
        case Audio:     return "Audio";
×
1094
        default:        return "Disabled";
1✔
1095
    }
1096
}
1097

1098
InputOutputMap::BeatGeneratorType InputOutputMap::stringToBeatType(QString str)
×
1099
{
1100
    if (str == "Internal")
×
1101
        return Internal;
×
1102
    else if (str == "Plugin")
×
1103
        return Plugin;
×
1104
    else if (str == "Audio")
×
1105
        return Audio;
×
1106

1107
    return Disabled;
×
1108
}
1109

1110
void InputOutputMap::setBpmNumber(int bpm)
×
1111
{
1112
    if (m_beatGeneratorType == Disabled || bpm == m_currentBPM)
×
1113
        return;
×
1114

1115
    //qDebug() << "[InputOutputMap] set BPM to" << bpm;
1116
    m_currentBPM = bpm;
×
1117

1118
    if (bpm != 0)
×
1119
        m_doc->masterTimer()->requestBpmNumber(bpm);
×
1120

1121
    emit bpmNumberChanged(m_currentBPM);
×
1122
}
1123

1124
int InputOutputMap::bpmNumber() const
×
1125
{
1126
    if (m_beatGeneratorType == Disabled)
×
1127
        return 0;
×
1128

1129
    return m_currentBPM;
×
1130
}
1131

1132
void InputOutputMap::slotProcessBeat()
×
1133
{
1134
    // process the timer as first thing, to avoid wasting time
1135
    // with the operations below
1136
    qint64 elapsed = m_beatTime->elapsed();
×
1137
    m_beatTime->restart();
×
1138

1139
    // When the audio tracker reports its own tempo estimate (see
1140
    // slotAudioBpmChanged), that value drives the BPM number; deriving
1141
    // it from the wall-clock spacing of beat signals amplifies every
1142
    // hop of emission jitter into a BPM change
NEW
1143
    if (m_beatGeneratorType != Audio || m_audioTrackerBpm <= 0)
×
1144
    {
NEW
1145
        int bpm = qRound(60000.0 / (float)elapsed);
×
NEW
1146
        float currBpmTime = 60000.0 / (float)m_currentBPM;
×
1147
        // here we check if the difference between the current BPM duration
1148
        // and the current time elapsed is within a range of +/-1ms.
1149
        // If it isn't, then the BPM number has really changed, otherwise
1150
        // it's just a tiny time drift
NEW
1151
        if (qAbs((float)elapsed - currBpmTime) > 1)
×
NEW
1152
            setBpmNumber(bpm);
×
1153
    }
1154

1155
    m_doc->masterTimer()->requestBeat();
×
1156
    emit beat();
×
1157
}
×
1158

NEW
1159
void InputOutputMap::slotAudioBpmChanged(int bpm)
×
1160
{
NEW
1161
    qDebug() << "[InputOutputMap] audio tracker BPM:" << bpm;
×
NEW
1162
    m_audioTrackerBpm = bpm;
×
NEW
1163
    if (m_beatGeneratorType == Audio && bpm > 0)
×
NEW
1164
        setBpmNumber(bpm);
×
NEW
1165
}
×
1166

UNCOV
1167
void InputOutputMap::slotMasterTimerBeat()
×
1168
{
1169
    if (m_beatGeneratorType != Internal)
×
1170
        return;
×
1171

1172
    emit beat();
×
1173
}
1174

1175
void InputOutputMap::slotPluginBeat(quint32 universe, quint32 channel, uchar value, const QString &key)
×
1176
{
1177
    Q_UNUSED(universe)
1178

1179
    // not interested in synthetic release or non-beat event
1180
    if (m_beatGeneratorType != Plugin || value == 0 || key != "beat")
×
1181
        return;
×
1182

1183
    qDebug() << "Plugin beat:" << channel << m_beatTime->elapsed();
×
1184

1185
    slotProcessBeat();
×
1186
}
1187

1188
/*********************************************************************
1189
 * Network server
1190
 *********************************************************************/
1191

1192
void InputOutputMap::setNetworkServerType(InputOutputMap::NetworkServerType type)
×
1193
{
1194
    m_networkServerType = type;
×
1195
}
×
1196

1197
InputOutputMap::NetworkServerType InputOutputMap::networkServerType() const
×
1198
{
1199
    return m_networkServerType;
×
1200
}
1201

1202
QString InputOutputMap::networkServerTypeToString(InputOutputMap::NetworkServerType type) const
1✔
1203
{
1204
    if (type == WebServer)
1✔
1205
        return "Web";
×
1206
    return "Native";
1✔
1207
}
1208

1209
InputOutputMap::NetworkServerType InputOutputMap::stringToNetworkServerType(const QString &str) const
×
1210
{
1211
    if (str.compare("Web", Qt::CaseInsensitive) == 0)
×
1212
        return WebServer;
×
1213
    return NativeServer;
×
1214
}
1215

1216
void InputOutputMap::setNetworkServerAutoStart(bool enable)
×
1217
{
1218
    m_networkServerAutoStart = enable;
×
1219
}
×
1220

1221
bool InputOutputMap::networkServerAutoStart() const
×
1222
{
1223
    return m_networkServerAutoStart;
×
1224
}
1225

1226
void InputOutputMap::setNetworkServerName(QString name)
×
1227
{
1228
    m_networkServerName = name;
×
1229
}
×
1230

1231
QString InputOutputMap::networkServerName() const
×
1232
{
1233
    return m_networkServerName;
×
1234
}
1235

1236
void InputOutputMap::setNetworkServerPassword(QString password)
×
1237
{
1238
    m_networkServerPassword = password;
×
1239
}
×
1240

1241
QString InputOutputMap::networkServerPassword() const
×
1242
{
1243
    return m_networkServerPassword;
×
1244
}
1245

1246
/*********************************************************************
1247
 * Defaults - !! FALLBACK !!
1248
 *********************************************************************/
1249

1250
void InputOutputMap::loadDefaults()
×
1251
{
1252
    /* ************************ INPUT *********************************** */
1253
    QSettings settings;
×
1254
    QString plugin;
×
1255
    QString input;
×
1256
    QString key;
×
1257

1258
    for (quint32 i = 0; i < universesCount(); i++)
×
1259
    {
1260
        QString profileName;
×
1261
        bool passthrough;
1262

1263
        /* Plugin name */
1264
        key = QString("/inputmap/universe%1/plugin/").arg(i);
×
1265
        plugin = settings.value(key).toString();
×
1266

1267
        /* Plugin input */
1268
        key = QString("/inputmap/universe%1/input/").arg(i);
×
1269
        input = settings.value(key).toString();
×
1270

1271
        /* Input profile */
1272
        key = QString("/inputmap/universe%1/profile/").arg(i);
×
1273
        profileName = settings.value(key).toString();
×
1274

1275
        key = QString("/inputmap/universe%1/passthrough/").arg(i);
×
1276
        passthrough = settings.value(key).toBool();
×
1277
        if (passthrough == true)
×
1278
            m_universeArray.at(i)->setPassthrough(passthrough);
×
1279

1280
        /* Do the mapping */
1281
        if (plugin != KInputNone && input != KInputNone)
×
1282
            setInputPatch(i, plugin, "", "", input.toUInt(), profileName);
×
1283
    }
×
1284

1285
    /* ************************ OUTPUT *********************************** */
1286
    QString output;
×
1287
    QString fb_plugin;
×
1288
    QString feedback;
×
1289

1290
    for (quint32 i = 0; i < universesCount(); i++)
×
1291
    {
1292
        /* Plugin name */
1293
        key = QString("/outputmap/universe%1/plugin/").arg(i);
×
1294
        plugin = settings.value(key).toString();
×
1295

1296
        /* Plugin output */
1297
        key = QString("/outputmap/universe%1/output/").arg(i);
×
1298
        output = settings.value(key).toString();
×
1299

1300
        /* Feedback plugin name */
1301
        key = QString("/outputmap/universe%1/feedbackplugin/").arg(i);
×
1302
        fb_plugin = settings.value(key).toString();
×
1303

1304
        /* Feedback line */
1305
        key = QString("/outputmap/universe%1/feedback/").arg(i);
×
1306
        feedback = settings.value(key).toString();
×
1307

1308
        if (plugin != KOutputNone && output != KOutputNone)
×
1309
            setOutputPatch(i, plugin, "", "", output.toUInt());
×
1310

1311
        if (fb_plugin != KOutputNone && feedback != KOutputNone)
×
1312
            setOutputPatch(i, fb_plugin, "", "", feedback.toUInt(), true);
×
1313
    }
1314
}
×
1315

1316
void InputOutputMap::saveDefaults() const
×
1317
{
1318
    /* ************************ INPUT *********************************** */
1319
    QSettings settings;
×
1320
    QString key;
×
1321

1322
    for (quint32 i = 0; i < universesCount(); i++)
×
1323
    {
1324
        InputPatch* inPatch = inputPatch(i);
×
1325

1326
        /* Plugin name */
1327
        key = QString("/inputmap/universe%1/plugin/").arg(i);
×
1328
        if (inPatch != NULL)
×
1329
            settings.setValue(key, inPatch->pluginName());
×
1330
        else
1331
            settings.setValue(key, KInputNone);
×
1332

1333
        /* Plugin input */
1334
        key = QString("/inputmap/universe%1/input/").arg(i);
×
1335
        if (inPatch != NULL)
×
1336
            settings.setValue(key, QString::number(inPatch->input()));
×
1337
        else
1338
            settings.setValue(key, KInputNone);
×
1339

1340
        /* Input profile */
1341
        key = QString("/inputmap/universe%1/profile/").arg(i);
×
1342
        if (inPatch != NULL)
×
1343
            settings.setValue(key, inPatch->profileName());
×
1344
        else
1345
            settings.setValue(key, KInputNone);
×
1346

1347
        /* Passthrough */
1348
        key = QString("/inputmap/universe%1/passthrough/").arg(i);
×
1349
        bool passthrough = m_universeArray.at(i)->passthrough();
×
1350
        if (passthrough == true)
×
1351
            settings.setValue(key, passthrough);
×
1352
        else
1353
            settings.remove(key);
×
1354
    }
1355

1356
    /* ************************ OUTPUT *********************************** */
1357

1358
    for (quint32 i = 0; i < universesCount(); i++)
×
1359
    {
1360
        OutputPatch* outPatch = outputPatch(i);
×
1361
        OutputPatch* fbPatch = feedbackPatch(i);
×
1362

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

1365
        /* Plugin name */
1366
        if (outPatch != NULL)
×
1367
            settings.setValue(key, outPatch->pluginName());
×
1368
        else
1369
            settings.setValue(key, KOutputNone);
×
1370

1371
        /* Plugin output */
1372
        key = QString("/outputmap/universe%1/output/").arg(i);
×
1373
        if (outPatch != NULL)
×
1374
            settings.setValue(key, outPatch->output());
×
1375
        else
1376
            settings.setValue(key, KOutputNone);
×
1377

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

1380
        /* Feedback plugin name */
1381
        if (fbPatch != NULL)
×
1382
            settings.setValue(key, fbPatch->pluginName());
×
1383
        else
1384
            settings.setValue(key, KOutputNone);
×
1385

1386
        /* Feedback plugin output */
1387
        key = QString("/outputmap/universe%1/feedback/").arg(i);
×
1388
        if (fbPatch != NULL)
×
1389
            settings.setValue(key, QString::number(fbPatch->output()));
×
1390
        else
1391
            settings.setValue(key, KOutputNone);
×
1392
    }
1393
}
×
1394

1395
/*********************************************************************
1396
 * Load & Save
1397
 *********************************************************************/
1398

1399
bool InputOutputMap::loadXML(QXmlStreamReader &root)
×
1400
{
1401
    if (root.name() != KXMLIOMap)
×
1402
    {
1403
        qWarning() << Q_FUNC_INFO << "InputOutputMap node not found";
×
1404
        return false;
×
1405
    }
1406

1407
    /** Reset the current universe list and read the new one */
1408
    removeAllUniverses();
×
1409

1410
    while (root.readNextStartElement())
×
1411
    {
1412
        if (root.name() == KXMLQLCUniverse)
×
1413
        {
1414
            quint32 id = InputOutputMap::invalidUniverse();
×
1415
            if (root.attributes().hasAttribute(KXMLQLCUniverseID))
×
1416
                id = root.attributes().value(KXMLQLCUniverseID).toString().toUInt();
×
1417
            if (addUniverse(id))
×
1418
            {
1419
                Universe *uni = m_universeArray.last();
×
1420
                uni->loadXML(root, m_universeArray.count() - 1, this);
×
1421
            }
1422
        }
1423
        else if (root.name() == KXMLIOBeatGenerator)
×
1424
        {
1425
            QXmlStreamAttributes attrs = root.attributes();
×
1426

1427
            if (attrs.hasAttribute(KXMLIOBeatType))
×
1428
                setBeatGeneratorType(stringToBeatType(attrs.value(KXMLIOBeatType).toString()));
×
1429

1430
            if (attrs.hasAttribute(KXMLIOBeatsPerMinute))
×
1431
                setBpmNumber(attrs.value(KXMLIOBeatsPerMinute).toInt());
×
1432

1433
            root.skipCurrentElement();
×
1434
        }
×
1435
        else if (root.name() == KXMLIONetworkServer)
×
1436
        {
1437
            QXmlStreamAttributes attrs = root.attributes();
×
1438
            NetworkServerType type = NativeServer;
×
1439

1440
            if (attrs.hasAttribute(KXMLIONetworkType))
×
1441
                type = stringToNetworkServerType(attrs.value(KXMLIONetworkType).toString());
×
1442
            setNetworkServerType(type);
×
1443

1444
            if (attrs.hasAttribute(KXMLIONetworkAutoStart))
×
1445
                setNetworkServerAutoStart(attrs.value(KXMLIONetworkAutoStart) == KXMLQLCTrue);
×
1446

1447
            if (type == NativeServer)
×
1448
            {
1449
                setNetworkServerName(attrs.value(KXMLIONetworkName).toString());
×
1450
                setNetworkServerPassword(attrs.value(KXMLIONetworkPassword).toString());
×
1451
            }
1452
            else
1453
            {
1454
                setNetworkServerName(QString());
×
1455
                setNetworkServerPassword(QString());
×
1456
            }
1457

1458
            root.skipCurrentElement();
×
1459
        }
×
1460
        else
1461
        {
1462
            qWarning() << Q_FUNC_INFO << "Unknown IO Map tag:" << root.name();
×
1463
            root.skipCurrentElement();
×
1464
        }
1465
    }
1466

1467
    return true;
×
1468
}
1469

1470
bool InputOutputMap::saveXML(QXmlStreamWriter *doc) const
1✔
1471
{
1472
    Q_ASSERT(doc != NULL);
1✔
1473

1474
    /* IO Map Instance entry */
1475
    doc->writeStartElement(KXMLIOMap);
2✔
1476

1477
    doc->writeStartElement(KXMLIOBeatGenerator);
2✔
1478
    doc->writeAttribute(KXMLIOBeatType, beatTypeToString(m_beatGeneratorType));
2✔
1479
    doc->writeAttribute(KXMLIOBeatsPerMinute, QString::number(m_currentBPM));
2✔
1480
    doc->writeEndElement();
1✔
1481

1482
    doc->writeStartElement(KXMLIONetworkServer);
2✔
1483
    doc->writeAttribute(KXMLIONetworkType, networkServerTypeToString(m_networkServerType));
2✔
1484
    doc->writeAttribute(KXMLIONetworkAutoStart, m_networkServerAutoStart ? KXMLQLCTrue : KXMLQLCFalse);
3✔
1485
    if (m_networkServerType == NativeServer)
1✔
1486
    {
1487
        doc->writeAttribute(KXMLIONetworkName, m_networkServerName);
2✔
1488
        doc->writeAttribute(KXMLIONetworkPassword, m_networkServerPassword);
2✔
1489
    }
1490
    doc->writeEndElement();
1✔
1491

1492
    foreach (Universe *uni, m_universeArray)
5✔
1493
        uni->saveXML(doc);
5✔
1494

1495
    doc->writeEndElement();
1✔
1496

1497
    return true;
1✔
1498
}
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