void GenericFader_Test::addRemove() { GenericFader fader(m_doc); FadeChannel fc; fc.setFixture(0); fc.setChannel(0); FadeChannel wrong; fc.setFixture(0); QCOMPARE(fader.m_channels.count(), 0); QVERIFY(fader.m_channels.contains(fc) == false); fader.add(fc); QVERIFY(fader.m_channels.contains(fc) == true); QCOMPARE(fader.m_channels.count(), 1); fader.remove(wrong); QVERIFY(fader.m_channels.contains(fc) == true); QCOMPARE(fader.m_channels.count(), 1); fader.remove(fc); QVERIFY(fader.m_channels.contains(fc) == false); QCOMPARE(fader.m_channels.count(), 0); fc.setChannel(0); fader.add(fc); QVERIFY(fader.m_channels.contains(fc) == true); fc.setChannel(1); fader.add(fc); QVERIFY(fader.m_channels.contains(fc) == true); fc.setChannel(2); fader.add(fc); QVERIFY(fader.m_channels.contains(fc) == true); QCOMPARE(fader.m_channels.count(), 3); fader.removeAll(); QCOMPARE(fader.m_channels.count(), 0); fc.setFixture(0); fc.setChannel(0); fc.setTarget(127); fader.add(fc); QCOMPARE(fader.m_channels.size(), 1); QCOMPARE(fader.m_channels[fc].target(), uchar(127)); fc.setTarget(63); fader.add(fc); QCOMPARE(fader.m_channels.size(), 1); QCOMPARE(fader.m_channels[fc].target(), uchar(63)); fc.setCurrent(63); fader.add(fc); QCOMPARE(fader.m_channels.size(), 1); QCOMPARE(fader.m_channels[fc].target(), uchar(63)); }
void CueStack_Test::insertStartValue() { QList<Universe*> ua; ua.append(new Universe(0, new GrandMaster())); ua.at(0)->setChannelCapability(0, QLCChannel::Pan); CueStack cs(m_doc); cs.preRun(); FadeChannel fc; fc.setChannel(0); fc.setStart(0); fc.setTarget(255); fc.setCurrent(127); cs.m_fader->add(fc); fc.setTarget(64); cs.insertStartValue(fc, ua); QCOMPARE(fc.start(), uchar(127)); QCOMPARE(fc.current(), uchar(127)); cs.m_fader->remove(fc); // HTP channel in universes ua[0]->write(0, 192); cs.insertStartValue(fc, ua); QCOMPARE(fc.start(), uchar(0)); QCOMPARE(fc.current(), uchar(0)); QLCFixtureDef* def = m_doc->fixtureDefCache()->fixtureDef("Futurelight", "DJScan250"); QVERIFY(def != NULL); QLCFixtureMode* mode = def->modes().first(); QVERIFY(mode != NULL); Fixture* fxi = new Fixture(m_doc); fxi->setFixtureDefinition(def, mode); fxi->setName("Test Scanner"); fxi->setAddress(0); fxi->setUniverse(0); m_doc->addFixture(fxi); // LTP channel (Pan) in universes ua[0]->write(0, 192); cs.insertStartValue(fc, ua); QCOMPARE(fc.start(), uchar(192)); QCOMPARE(fc.current(), uchar(192)); MasterTimer mt(m_doc); cs.postRun(&mt); }
void CueStack::switchCue(int from, int to, const QList<Universe *> ua) { qDebug() << Q_FUNC_INFO; Cue newCue; Cue oldCue; m_mutex.lock(); if (to >= 0 && to < m_cues.size()) newCue = m_cues[to]; if (from >= 0 && from < m_cues.size()) oldCue = m_cues[from]; m_mutex.unlock(); // Fade out the HTP channels of the previous cue QHashIterator <uint,uchar> oldit(oldCue.values()); while (oldit.hasNext() == true) { oldit.next(); FadeChannel fc; fc.setFixture(doc(), Fixture::invalidId()); fc.setChannel(oldit.key()); if (fc.group(doc()) == QLCChannel::Intensity) { fc.setElapsed(0); fc.setReady(false); fc.setTarget(0); fc.setFadeTime(oldCue.fadeOutSpeed()); insertStartValue(fc, ua); m_fader->add(fc); } } // Fade in all channels of the new cue QHashIterator <uint,uchar> newit(newCue.values()); while (newit.hasNext() == true) { newit.next(); FadeChannel fc; fc.setFixture(doc(), Fixture::invalidId()); fc.setChannel(newit.key()); fc.setTarget(newit.value()); fc.setElapsed(0); fc.setReady(false); fc.setFadeTime(newCue.fadeInSpeed()); insertStartValue(fc, ua); m_fader->add(fc); } }
void RGBMatrix::postRun(MasterTimer* timer, QList<Universe *> universes) { if (m_fader != NULL) { QHashIterator <FadeChannel,FadeChannel> it(m_fader->channels()); while (it.hasNext() == true) { it.next(); FadeChannel fc = it.value(); // fade out only intensity channels if (fc.group(doc()) != QLCChannel::Intensity) continue; bool canFade = true; Fixture *fixture = doc()->fixture(fc.fixture()); if (fixture != NULL) canFade = fixture->channelCanFade(fc.channel()); fc.setStart(fc.current(getAttributeValue(Intensity))); fc.setCurrent(fc.current(getAttributeValue(Intensity))); fc.setElapsed(0); fc.setReady(false); if (canFade == false) { fc.setFadeTime(0); fc.setTarget(fc.current(getAttributeValue(Intensity))); } else { if (overrideFadeOutSpeed() == defaultSpeed()) fc.setFadeTime(fadeOutSpeed()); else fc.setFadeTime(overrideFadeOutSpeed()); fc.setTarget(0); } timer->faderAdd(fc); } delete m_fader; m_fader = NULL; } { QMutexLocker algorithmLocker(&m_algorithmMutex); if (m_algorithm != NULL) m_algorithm->postRun(); } Function::postRun(timer, universes); }
void EFXFixture::stop(MasterTimer* timer, QList<Universe *> universes) { Q_UNUSED(universes); if (fadeIntensity() > 0 && m_started == true) { Fixture* fxi = doc()->fixture(head().fxi); Q_ASSERT(fxi != NULL); if (fxi->masterIntensityChannel(head().head) != QLCChannel::invalid()) { FadeChannel fc; fc.setFixture(doc(), head().fxi); fc.setChannel(fxi->masterIntensityChannel(head().head)); if (m_parent->overrideFadeOutSpeed() != Function::defaultSpeed()) fc.setFadeTime(m_parent->overrideFadeOutSpeed()); else fc.setFadeTime(m_parent->fadeOutSpeed()); fc.setStart(uchar(floor((float(fadeIntensity()) * intensity()) + 0.5))); fc.setCurrent(fc.start()); fc.setTarget(0); // Give zero-fading to MasterTimer because EFX will stop after this call timer->faderAdd(fc); // Remove the previously up-faded channel from EFX's internal fader to allow // MasterTimer's fader take HTP precedence. m_parent->m_fader->remove(fc); } } m_started = false; }
void EFXFixture::start(MasterTimer* timer, QList<Universe *> universes) { Q_UNUSED(universes); Q_UNUSED(timer); if (fadeIntensity() > 0 && m_started == false) { Fixture* fxi = doc()->fixture(head().fxi); Q_ASSERT(fxi != NULL); if (fxi->masterIntensityChannel(head().head) != QLCChannel::invalid()) { FadeChannel fc; fc.setFixture(doc(), head().fxi); fc.setChannel(fxi->masterIntensityChannel(head().head)); if (m_parent->overrideFadeInSpeed() != Function::defaultSpeed()) fc.setFadeTime(m_parent->overrideFadeInSpeed()); else fc.setFadeTime(m_parent->fadeInSpeed()); fc.setStart(0); fc.setCurrent(fc.start()); // Don't use intensity() multiplier because EFX's GenericFader takes care of that fc.setTarget(fadeIntensity()); // Fade channel up with EFX's own GenericFader to allow manual intensity control m_parent->m_fader->add(fc); } } m_started = true; }
void GenericFader_Test::writeLoop() { UniverseArray ua(512); GenericFader fader(m_doc); FadeChannel fc; fc.setFixture(0); fc.setChannel(5); fc.setStart(0); fc.setTarget(250); fc.setFadeTime(1000); fader.add(fc); QCOMPARE(ua.preGMValues()[15], (char) 0); int expected = 0; for (int i = MasterTimer::tick(); i <= 1000; i += MasterTimer::tick()) { ua.zeroIntensityChannels(); fader.write(&ua); int actual = uchar(ua.preGMValues()[15]); expected += 5; QCOMPARE(actual, expected); } }
void CueStack::postRun(MasterTimer* timer) { qDebug() << Q_FUNC_INFO; Q_ASSERT(timer != NULL); Q_ASSERT(m_fader != NULL); // Bounce all intensity channels to MasterTimer's fader for zeroing QHashIterator <FadeChannel,FadeChannel> it(m_fader->channels()); while (it.hasNext() == true) { it.next(); FadeChannel fc = it.value(); if (fc.group(doc()) == QLCChannel::Intensity) { fc.setStart(fc.current(intensity())); fc.setTarget(0); fc.setElapsed(0); fc.setReady(false); fc.setFadeTime(fadeOutSpeed()); timer->fader()->add(fc); } } m_currentIndex = -1; delete m_fader; m_fader = NULL; emit currentCueChanged(m_currentIndex); emit stopped(); }
/***************************************************************************** * Helper Function *****************************************************************************/ void EFXFixture::setFadeChannel(quint32 nChannel, uchar val) { FadeChannel fc; fc.setFixture(doc(), head().fxi); fc.setChannel(nChannel); fc.setTarget(val); m_parent->m_fader->forceAdd(fc); }
void FadeChannel_Test::target() { FadeChannel fch; QCOMPARE(fch.target(), uchar(0)); for (uint i = 0; i <= 255; i++) { fch.setTarget(i); QCOMPARE(fch.target(), uchar(i)); } }
void GenericFader_Test::writeZeroFade() { UniverseArray ua(512); GenericFader fader(m_doc); FadeChannel fc; fc.setFixture(0); fc.setChannel(5); fc.setStart(0); fc.setTarget(255); fc.setFadeTime(0); fader.add(fc); QCOMPARE(ua.preGMValues()[15], (char) 0); fader.write(&ua); QCOMPARE(ua.preGMValues()[15], (char) 255); }
void CueStack::writeDMX(MasterTimer* timer, QList<Universe*> ua) { Q_UNUSED(timer); if (isFlashing() == true && m_cues.size() > 0) { QHashIterator <uint,uchar> it(m_cues.first().values()); while (it.hasNext() == true) { it.next(); FadeChannel fc; fc.setChannel(it.key()); fc.setTarget(it.value()); int uni = floor(fc.channel() / 512); if (uni < ua.size()) ua[uni]->write(fc.channel() - (uni * 512), fc.target()); } } }
void GenericFader_Test::adjustIntensity() { UniverseArray ua(512); GenericFader fader(m_doc); FadeChannel fc; // HTP channel fc.setFixture(0); fc.setChannel(5); fc.setStart(0); fc.setTarget(250); fc.setFadeTime(1000); fader.add(fc); // LTP channel fc.setChannel(0); fader.add(fc); qreal intensity = 0.5; fader.adjustIntensity(intensity); QCOMPARE(fader.intensity(), intensity); int expected = 0; for (int i = MasterTimer::tick(); i <= 1000; i += MasterTimer::tick()) { ua.zeroIntensityChannels(); fader.write(&ua); expected += 5; // GenericFader should apply intensity only to HTP channels int actual = uchar(ua.preGMValues()[15]); int expectedWithIntensity = floor((qreal(expected) * intensity) + 0.5); QVERIFY(actual == expectedWithIntensity); // No intensity adjustment on LTP channels actual = uchar(ua.preGMValues()[10]); QVERIFY(actual == expected); } }
QMap <quint32,FadeChannel> ChaserRunner::createFadeChannels(const UniverseArray* universes, QMap <quint32,FadeChannel>& zeroChannels) const { QMap <quint32,FadeChannel> map; if (m_currentStep >= m_steps.size() || m_currentStep < 0) return map; Scene* scene = qobject_cast<Scene*> (m_steps.at(m_currentStep)); Q_ASSERT(scene != NULL); // Put all current channels to a map of channels that will be faded to zero. // If the same channels are added to the new channel map, they are removed // from this zero map. zeroChannels = m_channelMap; QListIterator <SceneValue> it(scene->values()); while (it.hasNext() == true) { SceneValue value(it.next()); Fixture* fxi = m_doc->fixture(value.fxi); if (fxi == NULL || fxi->channel(value.channel) == NULL) continue; FadeChannel channel; channel.setAddress(fxi->universeAddress() + value.channel); channel.setGroup(fxi->channel(value.channel)->group()); channel.setTarget(value.value); // Get starting value from universes. For HTP channels it's always 0. channel.setStart(uchar(universes->preGMValues()[channel.address()])); // Transfer last step's current value to current step's starting value. if (m_channelMap.contains(channel.address()) == true) channel.setStart(m_channelMap[channel.address()].current()); channel.setCurrent(channel.start()); // Append the channel to the channel map map[channel.address()] = channel; // Remove the channel from a map of to-be-zeroed channels since now it // has a new value to fade to. zeroChannels.remove(channel.address()); } // All channels that were present in the previous step but are not present // in the current step will go through this zeroing process. QMutableMapIterator <quint32,FadeChannel> zit(zeroChannels); while (zit.hasNext() == true) { zit.next(); FadeChannel& channel(zit.value()); if (channel.current() == 0 || channel.group() != QLCChannel::Intensity) { // Remove all non-HTP channels and such HTP channels that are // already at zero. There's nothing to do for them. zit.remove(); } else { // This HTP channel was present in the previous step, but is absent // in the current. It's nicer that we fade it back to zero, rather // than just let it drop straight to zero. channel.setStart(channel.current()); channel.setTarget(0); } } return map; }
void RGBMatrix::updateMapChannels(const RGBMap& map, const FixtureGroup* grp) { quint32 mdAssigned = QLCChannel::invalid(); quint32 mdFxi = Fixture::invalidId(); uint fadeTime = 0; if (overrideFadeInSpeed() == defaultSpeed()) fadeTime = fadeInSpeed(); else fadeTime = overrideFadeInSpeed(); // Create/modify fade channels for ALL pixels in the color map. for (int y = 0; y < map.size(); y++) { for (int x = 0; x < map[y].size(); x++) { QLCPoint pt(x, y); GroupHead grpHead(grp->head(pt)); Fixture* fxi = doc()->fixture(grpHead.fxi); if (fxi == NULL) continue; if (grpHead.fxi != mdFxi) { mdAssigned = QLCChannel::invalid(); mdFxi = grpHead.fxi; } QLCFixtureHead head = fxi->head(grpHead.head); QVector <quint32> rgb = head.rgbChannels(); QVector <quint32> cmy = head.cmyChannels(); if (rgb.size() == 3) { // RGB color mixing FadeChannel fc; fc.setFixture(doc(), grpHead.fxi); fc.setChannel(rgb.at(0)); fc.setTarget(qRed(map[y][x])); insertStartValues(fc, fadeTime); m_fader->add(fc); fc.setChannel(rgb.at(1)); fc.setTarget(qGreen(map[y][x])); insertStartValues(fc, fadeTime); m_fader->add(fc); fc.setChannel(rgb.at(2)); fc.setTarget(qBlue(map[y][x])); insertStartValues(fc, fadeTime); m_fader->add(fc); } else if (cmy.size() == 3) { // CMY color mixing QColor col(map[y][x]); FadeChannel fc; fc.setFixture(doc(), grpHead.fxi); fc.setChannel(cmy.at(0)); fc.setTarget(col.cyan()); insertStartValues(fc, fadeTime); m_fader->add(fc); fc.setChannel(cmy.at(1)); fc.setTarget(col.magenta()); insertStartValues(fc, fadeTime); m_fader->add(fc); fc.setChannel(cmy.at(2)); fc.setTarget(col.yellow()); insertStartValues(fc, fadeTime); m_fader->add(fc); } if (m_dimmerControl && head.masterIntensityChannel() != QLCChannel::invalid()) { //qDebug() << "RGBMatrix: found dimmer at" << head.masterIntensityChannel(); // Simple intensity (dimmer) channel QColor col(map[y][x]); FadeChannel fc; fc.setFixture(doc(), grpHead.fxi); fc.setChannel(head.masterIntensityChannel()); if (col.value() == 0 && mdAssigned != head.masterIntensityChannel()) fc.setTarget(0); else { fc.setTarget(255); if (mdAssigned == QLCChannel::invalid()) mdAssigned = head.masterIntensityChannel(); } insertStartValues(fc, fadeTime); m_fader->add(fc); } } } }
void FadeChannel_Test::nextStep() { FadeChannel fc; fc.setStart(0); fc.setTarget(250); fc.setFadeTime(1000); for (int i = 5; i < 250; i += 5) { int value = fc.nextStep(MasterTimer::tick()); QCOMPARE(value, i); } fc.setCurrent(0); fc.setReady(false); fc.setFadeTime(0); fc.setElapsed(0); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(250)); fc.setCurrent(0); fc.setReady(false); fc.setFadeTime(MasterTimer::tick() / 5); fc.setElapsed(0); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(250)); fc.setCurrent(0); fc.setReady(false); fc.setFadeTime(1 * MasterTimer::tick()); fc.setElapsed(0); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(250)); fc.setCurrent(0); fc.setReady(false); fc.setFadeTime(2 * MasterTimer::tick()); fc.setElapsed(0); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(125)); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(250)); fc.setCurrent(0); fc.setReady(false); fc.setFadeTime(5 * MasterTimer::tick()); fc.setElapsed(0); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(50)); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(100)); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(150)); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(200)); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(250)); // Maximum elapsed() reached fc.setCurrent(0); fc.setTarget(255); fc.setReady(false); fc.setElapsed(UINT_MAX); fc.setFadeTime(5 * MasterTimer::tick()); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(255)); QCOMPARE(fc.elapsed(), UINT_MAX); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(255)); QCOMPARE(fc.elapsed(), UINT_MAX); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(255)); QCOMPARE(fc.elapsed(), UINT_MAX); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(255)); QCOMPARE(fc.elapsed(), UINT_MAX); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(255)); QCOMPARE(fc.elapsed(), UINT_MAX); // Channel marked as ready fc.setReady(true); fc.setElapsed(0); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(255)); QCOMPARE(fc.elapsed(), MasterTimer::tick() * 1); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(255)); QCOMPARE(fc.elapsed(), MasterTimer::tick() * 2); QCOMPARE(fc.nextStep(MasterTimer::tick()), uchar(255)); QCOMPARE(fc.elapsed(), MasterTimer::tick() * 3); }
void FadeChannel_Test::calculateCurrent() { FadeChannel fch; fch.setStart(0); fch.setTarget(255); // Simple: 255 ticks to fade from 0 to 255 for (uint time = 0; time < 255; time++) QCOMPARE(fch.calculateCurrent(255, time), uchar(time)); // Same thing, but the value should stay at 255 same after 255 ticks for (uint time = 0; time <= 512; time++) QCOMPARE(fch.calculateCurrent(255, time), uchar(MIN(time, 255))); // Simple reverse: 255 ticks to fade from 255 to 0 fch.setStart(255); fch.setTarget(0); for (uint time = 0; time <= 255; time++) QCOMPARE(fch.calculateCurrent(255, time), uchar(255 - time)); // A bit more complex involving decimals that don't produce round integers fch.setStart(13); fch.setTarget(147); QCOMPARE(fch.calculateCurrent(13, 0), uchar(13)); QCOMPARE(fch.calculateCurrent(13, 1), uchar(23)); QCOMPARE(fch.calculateCurrent(13, 2), uchar(33)); QCOMPARE(fch.calculateCurrent(13, 3), uchar(43)); QCOMPARE(fch.calculateCurrent(13, 4), uchar(54)); QCOMPARE(fch.calculateCurrent(13, 5), uchar(64)); QCOMPARE(fch.calculateCurrent(13, 6), uchar(74)); QCOMPARE(fch.calculateCurrent(13, 7), uchar(85)); QCOMPARE(fch.calculateCurrent(13, 8), uchar(95)); QCOMPARE(fch.calculateCurrent(13, 9), uchar(105)); QCOMPARE(fch.calculateCurrent(13, 10), uchar(116)); QCOMPARE(fch.calculateCurrent(13, 11), uchar(126)); QCOMPARE(fch.calculateCurrent(13, 12), uchar(136)); QCOMPARE(fch.calculateCurrent(13, 13), uchar(147)); // One more to check slower operation (200 ticks for 144 steps) fch.setStart(245); fch.setTarget(101); QCOMPARE(fch.calculateCurrent(200, 0), uchar(245)); QCOMPARE(fch.calculateCurrent(200, 1), uchar(245)); QCOMPARE(fch.calculateCurrent(200, 2), uchar(244)); QCOMPARE(fch.calculateCurrent(200, 3), uchar(243)); QCOMPARE(fch.calculateCurrent(200, 4), uchar(243)); QCOMPARE(fch.calculateCurrent(200, 5), uchar(242)); QCOMPARE(fch.calculateCurrent(200, 6), uchar(241)); QCOMPARE(fch.calculateCurrent(200, 7), uchar(240)); QCOMPARE(fch.calculateCurrent(200, 8), uchar(240)); QCOMPARE(fch.calculateCurrent(200, 9), uchar(239)); QCOMPARE(fch.calculateCurrent(200, 10), uchar(238)); QCOMPARE(fch.calculateCurrent(200, 11), uchar(238)); // Skip... QCOMPARE(fch.calculateCurrent(200, 100), uchar(173)); QCOMPARE(fch.calculateCurrent(200, 101), uchar(173)); QCOMPARE(fch.calculateCurrent(200, 102), uchar(172)); // Skip... QCOMPARE(fch.calculateCurrent(200, 198), uchar(103)); QCOMPARE(fch.calculateCurrent(200, 199), uchar(102)); QCOMPARE(fch.calculateCurrent(200, 200), uchar(101)); }
QString Script::handleSetFixture(const QList<QStringList>& tokens, QList<Universe *> universes) { qDebug() << Q_FUNC_INFO; if (tokens.size() > 4) return QString("Too many arguments"); bool ok = false; quint32 id = 0; quint32 ch = 0; uchar value = 0; double time = 0; id = tokens[0][1].toUInt(&ok); if (ok == false) return QString("Invalid fixture (ID: %1)").arg(tokens[0][1]); for (int i = 1; i < tokens.size(); i++) { QStringList list = tokens[i]; list[0] = list[0].toLower().trimmed(); if (list.size() == 2) { ok = false; if (list[0] == "val" || list[0] == "value") value = uchar(list[1].toUInt(&ok)); else if (list[0] == "ch" || list[0] == "channel") ch = list[1].toUInt(&ok); else if (list[0] == "time") time = list[1].toDouble(&ok); else return QString("Unrecognized keyword: %1").arg(list[0]); if (ok == false) return QString("Invalid value (%1) for keyword: %2").arg(list[1]).arg(list[0]); } } Doc* doc = qobject_cast<Doc*> (parent()); Q_ASSERT(doc != NULL); Fixture* fxi = doc->fixture(id); if (fxi != NULL) { if (ch < fxi->channels()) { int address = fxi->address() + ch; if (address < 512) { GenericFader* gf = fader(); Q_ASSERT(gf != NULL); FadeChannel fc; fc.setFixture(doc, fxi->id()); fc.setChannel(ch); fc.setTarget(value); fc.setFadeTime(time); // If the script has used the channel previously, it might still be in // the bowels of GenericFader so get the starting value from there. // Otherwise get it from universes (HTP channels are always 0 then). quint32 uni = fc.universe(); if (gf->channels().contains(fc) == true) fc.setStart(gf->channels()[fc].current()); else fc.setStart(universes[uni]->preGMValue(address)); fc.setCurrent(fc.start()); gf->add(fc); return QString(); } else { return QString("Invalid address: %1").arg(address); } } else { return QString("Fixture (%1) has no channel number %2").arg(fxi->name()).arg(ch); } } else { return QString("No such fixture (ID: %1)").arg(id); } }