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

mcallegari / qlcplus / 29950884381

22 Jul 2026 07:25PM UTC coverage: 35.282% (-0.003%) from 35.285%
29950884381

push

github

web-flow
Merge pull request #2081 from mcallegari/beattracker

Improved Audio Beat Tracker

0 of 11 new or added lines in 1 file covered. (0.0%)

18560 of 52604 relevant lines covered (35.28%)

41026.37 hits per line

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

62.06
/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_beatTime(new QElapsedTimer())
234✔
53
    , m_networkServerType(NativeServer)
234✔
54
    , m_networkServerAutoStart(false)
468✔
55
{
56
    m_grandMaster = new GrandMaster(this);
234✔
57
    for (quint32 i = 0; i < universes; i++)
1,166✔
58
        addUniverse();
932✔
59

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

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

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

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

172
    emit universeAdded(id);
934✔
173
    return true;
934✔
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()
235✔
199
{
200
    QMutexLocker locker(&m_universeMutex);
235✔
201
    qDeleteAll(m_universeArray);
235✔
202
    m_universeArray.clear();
235✔
203
    return true;
235✔
204
}
235✔
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,760✔
277
{
278
    return (quint32)m_universeArray.count();
1,760✔
279
}
280

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

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

292
    return NULL;
×
293
}
294

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

469
    return true;
6✔
470
}
6✔
471

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

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

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

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

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

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

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

530
    return false;
531
}
19✔
532

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

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

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

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

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

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

580
    return list;
23✔
581
}
×
582

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

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

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

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

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

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

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

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

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

635
    int c = 2;
3✔
636

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

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

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

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

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

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

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

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

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

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

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

747
    return info;
9✔
748
}
×
749

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

880
        m_localProfilesLoaded = true;
×
881

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

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

897
    return NULL;
×
898
}
128✔
899

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

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

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

930
    return false;
1✔
931
}
932

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

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

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

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

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

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

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

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

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

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

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

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

1001
    return true;
3✔
1002
}
1003

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

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

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

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

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

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

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

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

1074
    emit beatGeneratorTypeChanged();
×
1075
}
1076

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

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

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

1102
    return Disabled;
×
1103
}
1104

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

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

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

1116
    emit bpmNumberChanged(m_currentBPM);
×
1117
}
1118

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

1124
    return m_currentBPM;
×
1125
}
1126

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

NEW
1134
    if (bpm > 0)
×
1135
    {
1136
        // the beat source provides its own tempo estimate
NEW
1137
        if (bpm != m_currentBPM)
×
1138
        {
NEW
1139
            qDebug() << "[InputOutputMap] beat source BPM:" << bpm;
×
NEW
1140
            setBpmNumber(bpm);
×
1141
        }
1142
    }
1143
    else
1144
    {
1145
        // no tempo estimate available: derive the BPM number from the
1146
        // wall-clock spacing of the beat signals
NEW
1147
        int elapsedBpm = qRound(60000.0 / (float)elapsed);
×
NEW
1148
        float currBpmTime = 60000.0 / (float)m_currentBPM;
×
1149
        // here we check if the difference between the current BPM duration
1150
        // and the current time elapsed is within a range of +/-1ms.
1151
        // If it isn't, then the BPM number has really changed, otherwise
1152
        // it's just a tiny time drift
NEW
1153
        if (qAbs((float)elapsed - currBpmTime) > 1)
×
NEW
1154
            setBpmNumber(elapsedBpm);
×
1155
    }
1156

1157
    m_doc->masterTimer()->requestBeat();
×
1158
    emit beat();
×
1159
}
×
1160

1161
void InputOutputMap::slotMasterTimerBeat()
×
1162
{
1163
    if (m_beatGeneratorType != Internal)
×
1164
        return;
×
1165

1166
    emit beat();
×
1167
}
1168

1169
void InputOutputMap::slotPluginBeat(quint32 universe, quint32 channel, uchar value, const QString &key)
×
1170
{
1171
    Q_UNUSED(universe)
1172

1173
    // not interested in synthetic release or non-beat event
1174
    if (m_beatGeneratorType != Plugin || value == 0 || key != "beat")
×
1175
        return;
×
1176

1177
    qDebug() << "Plugin beat:" << channel << m_beatTime->elapsed();
×
1178

1179
    slotProcessBeat();
×
1180
}
1181

1182
/*********************************************************************
1183
 * Network server
1184
 *********************************************************************/
1185

1186
void InputOutputMap::setNetworkServerType(InputOutputMap::NetworkServerType type)
×
1187
{
1188
    m_networkServerType = type;
×
1189
}
×
1190

1191
InputOutputMap::NetworkServerType InputOutputMap::networkServerType() const
×
1192
{
1193
    return m_networkServerType;
×
1194
}
1195

1196
QString InputOutputMap::networkServerTypeToString(InputOutputMap::NetworkServerType type) const
1✔
1197
{
1198
    if (type == WebServer)
1✔
1199
        return "Web";
×
1200
    return "Native";
1✔
1201
}
1202

1203
InputOutputMap::NetworkServerType InputOutputMap::stringToNetworkServerType(const QString &str) const
×
1204
{
1205
    if (str.compare("Web", Qt::CaseInsensitive) == 0)
×
1206
        return WebServer;
×
1207
    return NativeServer;
×
1208
}
1209

1210
void InputOutputMap::setNetworkServerAutoStart(bool enable)
×
1211
{
1212
    m_networkServerAutoStart = enable;
×
1213
}
×
1214

1215
bool InputOutputMap::networkServerAutoStart() const
×
1216
{
1217
    return m_networkServerAutoStart;
×
1218
}
1219

1220
void InputOutputMap::setNetworkServerName(QString name)
×
1221
{
1222
    m_networkServerName = name;
×
1223
}
×
1224

1225
QString InputOutputMap::networkServerName() const
×
1226
{
1227
    return m_networkServerName;
×
1228
}
1229

1230
void InputOutputMap::setNetworkServerPassword(QString password)
×
1231
{
1232
    m_networkServerPassword = password;
×
1233
}
×
1234

1235
QString InputOutputMap::networkServerPassword() const
×
1236
{
1237
    return m_networkServerPassword;
×
1238
}
1239

1240
/*********************************************************************
1241
 * Defaults - !! FALLBACK !!
1242
 *********************************************************************/
1243

1244
void InputOutputMap::loadDefaults()
×
1245
{
1246
    /* ************************ INPUT *********************************** */
1247
    QSettings settings;
×
1248
    QString plugin;
×
1249
    QString input;
×
1250
    QString key;
×
1251

1252
    for (quint32 i = 0; i < universesCount(); i++)
×
1253
    {
1254
        QString profileName;
×
1255
        bool passthrough;
1256

1257
        /* Plugin name */
1258
        key = QString("/inputmap/universe%1/plugin/").arg(i);
×
1259
        plugin = settings.value(key).toString();
×
1260

1261
        /* Plugin input */
1262
        key = QString("/inputmap/universe%1/input/").arg(i);
×
1263
        input = settings.value(key).toString();
×
1264

1265
        /* Input profile */
1266
        key = QString("/inputmap/universe%1/profile/").arg(i);
×
1267
        profileName = settings.value(key).toString();
×
1268

1269
        key = QString("/inputmap/universe%1/passthrough/").arg(i);
×
1270
        passthrough = settings.value(key).toBool();
×
1271
        if (passthrough == true)
×
1272
            m_universeArray.at(i)->setPassthrough(passthrough);
×
1273

1274
        /* Do the mapping */
1275
        if (plugin != KInputNone && input != KInputNone)
×
1276
            setInputPatch(i, plugin, "", "", input.toUInt(), profileName);
×
1277
    }
×
1278

1279
    /* ************************ OUTPUT *********************************** */
1280
    QString output;
×
1281
    QString fb_plugin;
×
1282
    QString feedback;
×
1283

1284
    for (quint32 i = 0; i < universesCount(); i++)
×
1285
    {
1286
        /* Plugin name */
1287
        key = QString("/outputmap/universe%1/plugin/").arg(i);
×
1288
        plugin = settings.value(key).toString();
×
1289

1290
        /* Plugin output */
1291
        key = QString("/outputmap/universe%1/output/").arg(i);
×
1292
        output = settings.value(key).toString();
×
1293

1294
        /* Feedback plugin name */
1295
        key = QString("/outputmap/universe%1/feedbackplugin/").arg(i);
×
1296
        fb_plugin = settings.value(key).toString();
×
1297

1298
        /* Feedback line */
1299
        key = QString("/outputmap/universe%1/feedback/").arg(i);
×
1300
        feedback = settings.value(key).toString();
×
1301

1302
        if (plugin != KOutputNone && output != KOutputNone)
×
1303
            setOutputPatch(i, plugin, "", "", output.toUInt());
×
1304

1305
        if (fb_plugin != KOutputNone && feedback != KOutputNone)
×
1306
            setOutputPatch(i, fb_plugin, "", "", feedback.toUInt(), true);
×
1307
    }
1308
}
×
1309

1310
void InputOutputMap::saveDefaults() const
×
1311
{
1312
    /* ************************ INPUT *********************************** */
1313
    QSettings settings;
×
1314
    QString key;
×
1315

1316
    for (quint32 i = 0; i < universesCount(); i++)
×
1317
    {
1318
        InputPatch* inPatch = inputPatch(i);
×
1319

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

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

1334
        /* Input profile */
1335
        key = QString("/inputmap/universe%1/profile/").arg(i);
×
1336
        if (inPatch != NULL)
×
1337
            settings.setValue(key, inPatch->profileName());
×
1338
        else
1339
            settings.setValue(key, KInputNone);
×
1340

1341
        /* Passthrough */
1342
        key = QString("/inputmap/universe%1/passthrough/").arg(i);
×
1343
        bool passthrough = m_universeArray.at(i)->passthrough();
×
1344
        if (passthrough == true)
×
1345
            settings.setValue(key, passthrough);
×
1346
        else
1347
            settings.remove(key);
×
1348
    }
1349

1350
    /* ************************ OUTPUT *********************************** */
1351

1352
    for (quint32 i = 0; i < universesCount(); i++)
×
1353
    {
1354
        OutputPatch* outPatch = outputPatch(i);
×
1355
        OutputPatch* fbPatch = feedbackPatch(i);
×
1356

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

1359
        /* Plugin name */
1360
        if (outPatch != NULL)
×
1361
            settings.setValue(key, outPatch->pluginName());
×
1362
        else
1363
            settings.setValue(key, KOutputNone);
×
1364

1365
        /* Plugin output */
1366
        key = QString("/outputmap/universe%1/output/").arg(i);
×
1367
        if (outPatch != NULL)
×
1368
            settings.setValue(key, outPatch->output());
×
1369
        else
1370
            settings.setValue(key, KOutputNone);
×
1371

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

1374
        /* Feedback plugin name */
1375
        if (fbPatch != NULL)
×
1376
            settings.setValue(key, fbPatch->pluginName());
×
1377
        else
1378
            settings.setValue(key, KOutputNone);
×
1379

1380
        /* Feedback plugin output */
1381
        key = QString("/outputmap/universe%1/feedback/").arg(i);
×
1382
        if (fbPatch != NULL)
×
1383
            settings.setValue(key, QString::number(fbPatch->output()));
×
1384
        else
1385
            settings.setValue(key, KOutputNone);
×
1386
    }
1387
}
×
1388

1389
/*********************************************************************
1390
 * Load & Save
1391
 *********************************************************************/
1392

1393
bool InputOutputMap::loadXML(QXmlStreamReader &root)
×
1394
{
1395
    if (root.name() != KXMLIOMap)
×
1396
    {
1397
        qWarning() << Q_FUNC_INFO << "InputOutputMap node not found";
×
1398
        return false;
×
1399
    }
1400

1401
    /** Reset the current universe list and read the new one */
1402
    removeAllUniverses();
×
1403

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

1421
            if (attrs.hasAttribute(KXMLIOBeatType))
×
1422
                setBeatGeneratorType(stringToBeatType(attrs.value(KXMLIOBeatType).toString()));
×
1423

1424
            if (attrs.hasAttribute(KXMLIOBeatsPerMinute))
×
1425
                setBpmNumber(attrs.value(KXMLIOBeatsPerMinute).toInt());
×
1426

1427
            root.skipCurrentElement();
×
1428
        }
×
1429
        else if (root.name() == KXMLIONetworkServer)
×
1430
        {
1431
            QXmlStreamAttributes attrs = root.attributes();
×
1432
            NetworkServerType type = NativeServer;
×
1433

1434
            if (attrs.hasAttribute(KXMLIONetworkType))
×
1435
                type = stringToNetworkServerType(attrs.value(KXMLIONetworkType).toString());
×
1436
            setNetworkServerType(type);
×
1437

1438
            if (attrs.hasAttribute(KXMLIONetworkAutoStart))
×
1439
                setNetworkServerAutoStart(attrs.value(KXMLIONetworkAutoStart) == KXMLQLCTrue);
×
1440

1441
            if (type == NativeServer)
×
1442
            {
1443
                setNetworkServerName(attrs.value(KXMLIONetworkName).toString());
×
1444
                setNetworkServerPassword(attrs.value(KXMLIONetworkPassword).toString());
×
1445
            }
1446
            else
1447
            {
1448
                setNetworkServerName(QString());
×
1449
                setNetworkServerPassword(QString());
×
1450
            }
1451

1452
            root.skipCurrentElement();
×
1453
        }
×
1454
        else
1455
        {
1456
            qWarning() << Q_FUNC_INFO << "Unknown IO Map tag:" << root.name();
×
1457
            root.skipCurrentElement();
×
1458
        }
1459
    }
1460

1461
    return true;
×
1462
}
1463

1464
bool InputOutputMap::saveXML(QXmlStreamWriter *doc) const
1✔
1465
{
1466
    Q_ASSERT(doc != NULL);
1✔
1467

1468
    /* IO Map Instance entry */
1469
    doc->writeStartElement(KXMLIOMap);
2✔
1470

1471
    doc->writeStartElement(KXMLIOBeatGenerator);
2✔
1472
    doc->writeAttribute(KXMLIOBeatType, beatTypeToString(m_beatGeneratorType));
2✔
1473
    doc->writeAttribute(KXMLIOBeatsPerMinute, QString::number(m_currentBPM));
2✔
1474
    doc->writeEndElement();
1✔
1475

1476
    doc->writeStartElement(KXMLIONetworkServer);
2✔
1477
    doc->writeAttribute(KXMLIONetworkType, networkServerTypeToString(m_networkServerType));
2✔
1478
    doc->writeAttribute(KXMLIONetworkAutoStart, m_networkServerAutoStart ? KXMLQLCTrue : KXMLQLCFalse);
3✔
1479
    if (m_networkServerType == NativeServer)
1✔
1480
    {
1481
        doc->writeAttribute(KXMLIONetworkName, m_networkServerName);
2✔
1482
        doc->writeAttribute(KXMLIONetworkPassword, m_networkServerPassword);
2✔
1483
    }
1484
    doc->writeEndElement();
1✔
1485

1486
    foreach (Universe *uni, m_universeArray)
5✔
1487
        uni->saveXML(doc);
5✔
1488

1489
    doc->writeEndElement();
1✔
1490

1491
    return true;
1✔
1492
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc