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

mcallegari / qlcplus / 19340353609

13 Nov 2025 05:33PM UTC coverage: 34.25% (-0.006%) from 34.256%
19340353609

push

github

web-flow
Merge pull request #1868 from mcallegari/v5triggers

[QLC+ 5] Virtual Console Audio Triggers implementation

1 of 19 new or added lines in 2 files covered. (5.26%)

3 existing lines in 1 file now uncovered.

17718 of 51732 relevant lines covered (34.25%)

19541.38 hits per line

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

65.05
/engine/src/inputoutputmap.cpp
1
/*
2
  Q Light Controller Plus
3
  inputoutputmap.cpp
4

5
  Copyright (c) Massimo Callegari
6

7
  Licensed under the Apache License, Version 2.0 (the "License");
8
  you may not use this file except in compliance with the License.
9
  You may obtain a copy of the License at
10

11
      http://www.apache.org/licenses/LICENSE-2.0.txt
12

13
  Unless required by applicable law or agreed to in writing, software
14
  distributed under the License is distributed on an "AS IS" BASIS,
15
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
  See the License for the specific language governing permissions and
17
  limitations under the License.
18
*/
19

20
#if defined(WIN32) || defined(Q_OS_WIN)
21
#        include <Windows.h>
22
#else
23
#   include <unistd.h>
24
#endif
25

26
#include <QXmlStreamReader>
27
#include <QXmlStreamWriter>
28
#include <QElapsedTimer>
29
#include <QSettings>
30
#include <QDebug>
31
#include <qmath.h>
32

33
#include "inputoutputmap.h"
34
#include "qlcinputchannel.h"
35
#include "qlcinputsource.h"
36
#include "audiocapture.h"
37
#include "qlcioplugin.h"
38
#include "outputpatch.h"
39
#include "inputpatch.h"
40
#include "qlcconfig.h"
41
#include "universe.h"
42
#include "qlcfile.h"
43
#include "doc.h"
44

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

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

63
InputOutputMap::~InputOutputMap()
415✔
64
{
65
    removeAllUniverses();
219✔
66
    delete m_grandMaster;
219✔
67
    delete m_beatTime;
219✔
68
    qDeleteAll(m_profiles);
219✔
69
}
415✔
70

71
/*****************************************************************************
72
 * Blackout
73
 *****************************************************************************/
74

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

82
    return m_blackout;
4✔
83
}
84

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

91
    m_blackout = blackout;
5✔
92

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

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

108
    emit blackoutChanged(m_blackout);
5✔
109

110
    return true;
5✔
111
}
112

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

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

124
/*****************************************************************************
125
 * Universes
126
 *****************************************************************************/
127

128
quint32 InputOutputMap::invalidUniverse()
2,092✔
129
{
130
    return UINT_MAX;
2,092✔
131
}
132

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

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

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

170
    emit universeAdded(id);
878✔
171
    return true;
878✔
172
}
173

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

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

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

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

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

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

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

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

215
    return invalidUniverse();
1✔
216
}
217

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

223
    return QString();
1✔
224
}
225

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

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

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

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

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

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

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

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

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

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

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

290
    return NULL;
×
291
}
292

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

299
void InputOutputMap::releaseUniverses(bool changed)
1,055✔
300
{
301
    m_universeChanged = changed;
1,055✔
302
    m_universeMutex.unlock();
1,055✔
303
}
1,055✔
304

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

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

318
    m_localProfilesLoaded = false;
1✔
319
}
1✔
320

321
/*********************************************************************
322
 * Grand Master
323
 *********************************************************************/
324

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

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

336
GrandMaster::ChannelMode InputOutputMap::grandMasterChannelMode()
78✔
337
{
338
    Q_ASSERT(m_grandMaster != NULL);
78✔
339

340
    return m_grandMaster->channelMode();
78✔
341
}
342

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

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

353
    emit grandMasterValueModeChanged(mode);
2✔
354
}
2✔
355

356
GrandMaster::ValueMode InputOutputMap::grandMasterValueMode()
154✔
357
{
358
    Q_ASSERT(m_grandMaster != NULL);
154✔
359

360
    return m_grandMaster->valueMode();
154✔
361
}
362

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

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

373
    if (m_universeChanged == true)
2✔
374
        emit grandMasterValueChanged(value);
2✔
375
}
2✔
376

377
uchar InputOutputMap::grandMasterValue()
2✔
378
{
379
    Q_ASSERT(m_grandMaster != NULL);
2✔
380

381
    return m_grandMaster->value();
2✔
382
}
383

384
/*********************************************************************
385
 * Patch
386
 *********************************************************************/
387

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

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

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

423
    if (!inputUID.isEmpty() && plugin != NULL)
6✔
424
    {
425
        QStringList inputs = plugin->inputs();
5✔
426
        int lIdx = inputs.indexOf(inputUID);
5✔
427
        if (lIdx != -1)
5✔
428
        {
429
            qDebug() << "[IOMAP] Found match on input by name on universe" << universe << "-" << input << "vs" << lIdx;
5✔
430
            input = lIdx;
5✔
431
        }
432
        else
433
        {
434
            qDebug() << "[IOMAP] !!No match found!! for input on universe" << universe << "-" << input << inputUID;
×
435
            qDebug() << plugin->inputs();
×
436
        }
437
    }
5✔
438

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

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

462
    return true;
6✔
463
}
6✔
464

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

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

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

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

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

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

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

518
    return false;
519
}
19✔
520

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

529
    return m_universeArray.at(universe)->outputPatchesCount();
4✔
530
}
531

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

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

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

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

568
    return list;
23✔
569
}
×
570

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

580
    return QLCIOPlugin::invalidLine();
13✔
581
}
582

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

596
    return QLCIOPlugin::invalidLine();
1✔
597
}
598

599
/*****************************************************************************
600
 * Plugins
601
 *****************************************************************************/
602

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

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

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

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

623
    int c = 2;
3✔
624

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

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

651
QStringList InputOutputMap::outputPluginNames()
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::Output)
2✔
659
            list << plg->name();
2✔
660
    }
661
    return list;
4✔
662
}
2✔
663

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

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

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

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

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

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

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

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

735
    return info;
9✔
736
}
×
737

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

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

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

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

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

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

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

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

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

805
    emit pluginConfigurationChanged(plugin->name(), success);
8✔
806
}
8✔
807

808
/*****************************************************************************
809
 * Profiles
810
 *****************************************************************************/
811

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

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

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

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

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

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

868
        m_localProfilesLoaded = true;
×
869

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

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

885
    return NULL;
×
886
}
125✔
887

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

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

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

918
    return false;
1✔
919
}
920

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

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

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

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

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

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

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

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

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

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

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

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

989
    return true;
3✔
990
}
991

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

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

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

1009
/*********************************************************************
1010
 * Beats
1011
 *********************************************************************/
1012

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

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

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

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

1062
    emit beatGeneratorTypeChanged();
×
1063
}
1064

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

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

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

1090
    return Disabled;
×
1091
}
1092

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

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

1101
    if (bpm != 0)
×
1102
        m_doc->masterTimer()->requestBpmNumber(bpm);
×
1103

1104
    emit bpmNumberChanged(m_currentBPM);
×
1105
}
1106

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

1112
    return m_currentBPM;
×
1113
}
1114

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

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

1131
    m_doc->masterTimer()->requestBeat();
×
1132
    emit beat();
×
UNCOV
1133
}
×
1134

NEW
1135
void InputOutputMap::slotMasterTimerBeat()
×
1136
{
NEW
1137
    if (m_beatGeneratorType != Internal)
×
NEW
1138
        return;
×
1139

NEW
1140
    emit beat();
×
1141
}
1142

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

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

NEW
1151
    qDebug() << "Plugin beat:" << channel << m_beatTime->elapsed();
×
1152

NEW
1153
    slotProcessBeat();
×
1154
}
1155

1156
/*********************************************************************
1157
 * Defaults - !! FALLBACK !!
1158
 *********************************************************************/
1159

1160
void InputOutputMap::loadDefaults()
×
1161
{
1162
    /* ************************ INPUT *********************************** */
1163
    QSettings settings;
×
1164
    QString plugin;
×
1165
    QString input;
×
1166
    QString key;
×
1167

1168
    for (quint32 i = 0; i < universesCount(); i++)
×
1169
    {
1170
        QString profileName;
×
1171
        bool passthrough;
1172

1173
        /* Plugin name */
1174
        key = QString("/inputmap/universe%1/plugin/").arg(i);
×
1175
        plugin = settings.value(key).toString();
×
1176

1177
        /* Plugin input */
1178
        key = QString("/inputmap/universe%1/input/").arg(i);
×
1179
        input = settings.value(key).toString();
×
1180

1181
        /* Input profile */
1182
        key = QString("/inputmap/universe%1/profile/").arg(i);
×
1183
        profileName = settings.value(key).toString();
×
1184

1185
        key = QString("/inputmap/universe%1/passthrough/").arg(i);
×
1186
        passthrough = settings.value(key).toBool();
×
1187
        if (passthrough == true)
×
1188
            m_universeArray.at(i)->setPassthrough(passthrough);
×
1189

1190
        /* Do the mapping */
1191
        if (plugin != KInputNone && input != KInputNone)
×
1192
            setInputPatch(i, plugin, "", input.toUInt(), profileName);
×
1193
    }
×
1194

1195
    /* ************************ OUTPUT *********************************** */
1196
    QString output;
×
1197
    QString fb_plugin;
×
1198
    QString feedback;
×
1199

1200
    for (quint32 i = 0; i < universesCount(); i++)
×
1201
    {
1202
        /* Plugin name */
1203
        key = QString("/outputmap/universe%1/plugin/").arg(i);
×
1204
        plugin = settings.value(key).toString();
×
1205

1206
        /* Plugin output */
1207
        key = QString("/outputmap/universe%1/output/").arg(i);
×
1208
        output = settings.value(key).toString();
×
1209

1210
        /* Feedback plugin name */
1211
        key = QString("/outputmap/universe%1/feedbackplugin/").arg(i);
×
1212
        fb_plugin = settings.value(key).toString();
×
1213

1214
        /* Feedback line */
1215
        key = QString("/outputmap/universe%1/feedback/").arg(i);
×
1216
        feedback = settings.value(key).toString();
×
1217

1218
        if (plugin != KOutputNone && output != KOutputNone)
×
1219
            setOutputPatch(i, plugin, "", output.toUInt());
×
1220

1221
        if (fb_plugin != KOutputNone && feedback != KOutputNone)
×
1222
            setOutputPatch(i, fb_plugin, "", feedback.toUInt(), true);
×
1223
    }
1224
}
×
1225

1226
void InputOutputMap::saveDefaults()
×
1227
{
1228
    /* ************************ INPUT *********************************** */
1229
    QSettings settings;
×
1230
    QString key;
×
1231

1232
    for (quint32 i = 0; i < universesCount(); i++)
×
1233
    {
1234
        InputPatch* inPatch = inputPatch(i);
×
1235

1236
        /* Plugin name */
1237
        key = QString("/inputmap/universe%1/plugin/").arg(i);
×
1238
        if (inPatch != NULL)
×
1239
            settings.setValue(key, inPatch->pluginName());
×
1240
        else
1241
            settings.setValue(key, KInputNone);
×
1242

1243
        /* Plugin input */
1244
        key = QString("/inputmap/universe%1/input/").arg(i);
×
1245
        if (inPatch != NULL)
×
1246
            settings.setValue(key, QString::number(inPatch->input()));
×
1247
        else
1248
            settings.setValue(key, KInputNone);
×
1249

1250
        /* Input profile */
1251
        key = QString("/inputmap/universe%1/profile/").arg(i);
×
1252
        if (inPatch != NULL)
×
1253
            settings.setValue(key, inPatch->profileName());
×
1254
        else
1255
            settings.setValue(key, KInputNone);
×
1256

1257
        /* Passthrough */
1258
        key = QString("/inputmap/universe%1/passthrough/").arg(i);
×
1259
        bool passthrough = m_universeArray.at(i)->passthrough();
×
1260
        if (passthrough == true)
×
1261
            settings.setValue(key, passthrough);
×
1262
        else
1263
            settings.remove(key);
×
1264
    }
1265

1266
    /* ************************ OUTPUT *********************************** */
1267

1268
    for (quint32 i = 0; i < universesCount(); i++)
×
1269
    {
1270
        OutputPatch* outPatch = outputPatch(i);
×
1271
        OutputPatch* fbPatch = feedbackPatch(i);
×
1272

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

1275
        /* Plugin name */
1276
        if (outPatch != NULL)
×
1277
            settings.setValue(key, outPatch->pluginName());
×
1278
        else
1279
            settings.setValue(key, KOutputNone);
×
1280

1281
        /* Plugin output */
1282
        key = QString("/outputmap/universe%1/output/").arg(i);
×
1283
        if (outPatch != NULL)
×
1284
            settings.setValue(key, outPatch->output());
×
1285
        else
1286
            settings.setValue(key, KOutputNone);
×
1287

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

1290
        /* Feedback plugin name */
1291
        if (fbPatch != NULL)
×
1292
            settings.setValue(key, fbPatch->pluginName());
×
1293
        else
1294
            settings.setValue(key, KOutputNone);
×
1295

1296
        /* Feedback plugin output */
1297
        key = QString("/outputmap/universe%1/feedback/").arg(i);
×
1298
        if (fbPatch != NULL)
×
1299
            settings.setValue(key, QString::number(fbPatch->output()));
×
1300
        else
1301
            settings.setValue(key, KOutputNone);
×
1302
    }
1303
}
×
1304

1305
/*********************************************************************
1306
 * Load & Save
1307
 *********************************************************************/
1308

1309
bool InputOutputMap::loadXML(QXmlStreamReader &root)
×
1310
{
1311
    if (root.name() != KXMLIOMap)
×
1312
    {
1313
        qWarning() << Q_FUNC_INFO << "InputOutputMap node not found";
×
1314
        return false;
×
1315
    }
1316

1317
    /** Reset the current universe list and read the new one */
1318
    removeAllUniverses();
×
1319

1320
    while (root.readNextStartElement())
×
1321
    {
1322
        if (root.name() == KXMLQLCUniverse)
×
1323
        {
1324
            quint32 id = InputOutputMap::invalidUniverse();
×
1325
            if (root.attributes().hasAttribute(KXMLQLCUniverseID))
×
1326
                id = root.attributes().value(KXMLQLCUniverseID).toString().toUInt();
×
1327
            if (addUniverse(id))
×
1328
            {
1329
                Universe *uni = m_universeArray.last();
×
1330
                uni->loadXML(root, m_universeArray.count() - 1, this);
×
1331
            }
1332
        }
1333
        else if (root.name() == KXMLIOBeatGenerator)
×
1334
        {
1335
            QXmlStreamAttributes attrs = root.attributes();
×
1336

1337
            if (attrs.hasAttribute(KXMLIOBeatType))
×
1338
                setBeatGeneratorType(stringToBeatType(attrs.value(KXMLIOBeatType).toString()));
×
1339

1340
            if (attrs.hasAttribute(KXMLIOBeatsPerMinute))
×
1341
                setBpmNumber(attrs.value(KXMLIOBeatsPerMinute).toInt());
×
1342

1343
            root.skipCurrentElement();
×
1344
        }
×
1345
        else
1346
        {
1347
            qWarning() << Q_FUNC_INFO << "Unknown IO Map tag:" << root.name();
×
1348
            root.skipCurrentElement();
×
1349
        }
1350
    }
1351

1352
    return true;
×
1353
}
1354

1355
bool InputOutputMap::saveXML(QXmlStreamWriter *doc) const
1✔
1356
{
1357
    Q_ASSERT(doc != NULL);
1✔
1358

1359
    /* IO Map Instance entry */
1360
    doc->writeStartElement(KXMLIOMap);
2✔
1361

1362
    doc->writeStartElement(KXMLIOBeatGenerator);
2✔
1363
    doc->writeAttribute(KXMLIOBeatType, beatTypeToString(m_beatGeneratorType));
2✔
1364
    doc->writeAttribute(KXMLIOBeatsPerMinute, QString::number(m_currentBPM));
2✔
1365
    doc->writeEndElement();
1✔
1366

1367
    foreach (Universe *uni, m_universeArray)
5✔
1368
        uni->saveXML(doc);
5✔
1369

1370
    doc->writeEndElement();
1✔
1371

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

© 2025 Coveralls, Inc