1 #include "effects/effectchainslot.h"
2 
3 #include "control/controlencoder.h"
4 #include "control/controlpotmeter.h"
5 #include "control/controlpushbutton.h"
6 #include "effects/effectrack.h"
7 #include "effects/effectslot.h"
8 #include "effects/effectxmlelements.h"
9 #include "mixer/playermanager.h"
10 #include "moc_effectchainslot.cpp"
11 #include "util/math.h"
12 #include "util/xml.h"
13 
EffectChainSlot(EffectRack * pRack,const QString & group,unsigned int iChainNumber)14 EffectChainSlot::EffectChainSlot(EffectRack* pRack, const QString& group,
15                                  unsigned int iChainNumber)
16         : m_iChainSlotNumber(iChainNumber),
17           // The control group names are 1-indexed while internally everything
18           // is 0-indexed.
19           m_group(group),
20           m_pEffectRack(pRack) {
21     m_pControlClear = new ControlPushButton(ConfigKey(m_group, "clear"));
22     connect(m_pControlClear,
23             &ControlPushButton::valueChanged,
24             this,
25             &EffectChainSlot::slotControlClear);
26 
27     m_pControlNumEffects = new ControlObject(ConfigKey(m_group, "num_effects"));
28     m_pControlNumEffects->setReadOnly();
29 
30     m_pControlNumEffectSlots = new ControlObject(ConfigKey(m_group, "num_effectslots"));
31     m_pControlNumEffectSlots->setReadOnly();
32 
33     m_pControlChainLoaded = new ControlObject(ConfigKey(m_group, "loaded"));
34     m_pControlChainLoaded->setReadOnly();
35 
36     m_pControlChainEnabled = new ControlPushButton(ConfigKey(m_group, "enabled"));
37     m_pControlChainEnabled->setButtonMode(ControlPushButton::POWERWINDOW);
38     // Default to enabled. The skin might not show these buttons.
39     m_pControlChainEnabled->setDefaultValue(true);
40     m_pControlChainEnabled->set(true);
41     connect(m_pControlChainEnabled,
42             &ControlPushButton::valueChanged,
43             this,
44             &EffectChainSlot::slotControlChainEnabled);
45 
46     m_pControlChainMix = new ControlPotmeter(ConfigKey(m_group, "mix"), 0.0, 1.0,
47                                              false, true, false, true, 1.0);
48     connect(m_pControlChainMix,
49             &ControlPotmeter::valueChanged,
50             this,
51             &EffectChainSlot::slotControlChainMix);
52 
53     m_pControlChainSuperParameter = new ControlPotmeter(ConfigKey(m_group, "super1"), 0.0, 1.0);
54     connect(m_pControlChainSuperParameter,
55             &ControlPotmeter::valueChanged,
56             this,
57             [this](double value) {
58                 slotControlChainSuperParameter(value, false);
59             });
60     m_pControlChainSuperParameter->set(0.0);
61     m_pControlChainSuperParameter->setDefaultValue(0.0);
62 
63     m_pControlChainMixMode = new ControlPushButton(ConfigKey(m_group, "mix_mode"));
64     m_pControlChainMixMode->setButtonMode(ControlPushButton::TOGGLE);
65     m_pControlChainMixMode->setStates(static_cast<int>(EffectChainMixMode::NumMixModes));
66     connect(m_pControlChainMixMode,
67             &ControlPushButton::valueChanged,
68             this,
69             &EffectChainSlot::slotControlChainMixMode);
70 
71     m_pControlChainNextPreset = new ControlPushButton(ConfigKey(m_group, "next_chain"));
72     connect(m_pControlChainNextPreset,
73             &ControlPushButton::valueChanged,
74             this,
75             &EffectChainSlot::slotControlChainNextPreset);
76 
77     m_pControlChainPrevPreset = new ControlPushButton(ConfigKey(m_group, "prev_chain"));
78     connect(m_pControlChainPrevPreset,
79             &ControlPushButton::valueChanged,
80             this,
81             &EffectChainSlot::slotControlChainPrevPreset);
82 
83     // Ignoring no-ops is important since this is for +/- tickers.
84     m_pControlChainSelector = new ControlEncoder(ConfigKey(m_group, "chain_selector"), false);
85     connect(m_pControlChainSelector,
86             &ControlEncoder::valueChanged,
87             this,
88             &EffectChainSlot::slotControlChainSelector);
89 
90     // ControlObjects for skin <-> controller mapping interaction.
91     // Refer to comment in header for full explanation.
92     m_pControlChainShowFocus = new ControlPushButton(
93                                    ConfigKey(m_group, "show_focus"));
94     m_pControlChainShowFocus->setButtonMode(ControlPushButton::TOGGLE);
95 
96     m_pControlChainHasControllerFocus = new ControlPushButton(
97                                    ConfigKey(m_group, "controller_input_active"));
98     m_pControlChainHasControllerFocus->setButtonMode(ControlPushButton::TOGGLE);
99 
100     m_pControlChainShowParameters = new ControlPushButton(
101                                         ConfigKey(m_group, "show_parameters"),
102                                         true);
103     m_pControlChainShowParameters->setButtonMode(ControlPushButton::TOGGLE);
104 
105     m_pControlChainFocusedEffect = new ControlPushButton(
106                                        ConfigKey(m_group, "focused_effect"),
107                                        true);
108     m_pControlChainFocusedEffect->setButtonMode(ControlPushButton::TOGGLE);
109 }
110 
~EffectChainSlot()111 EffectChainSlot::~EffectChainSlot() {
112     //qDebug() << debugString() << "destroyed";
113     clear();
114     delete m_pControlClear;
115     delete m_pControlNumEffects;
116     delete m_pControlNumEffectSlots;
117     delete m_pControlChainLoaded;
118     delete m_pControlChainEnabled;
119     delete m_pControlChainMix;
120     delete m_pControlChainSuperParameter;
121     delete m_pControlChainMixMode;
122     delete m_pControlChainPrevPreset;
123     delete m_pControlChainNextPreset;
124     delete m_pControlChainSelector;
125     delete m_pControlChainShowFocus;
126     delete m_pControlChainHasControllerFocus;
127     delete m_pControlChainShowParameters;
128     delete m_pControlChainFocusedEffect;
129 
130     for (QMap<QString, ChannelInfo*>::iterator it = m_channelInfoByName.begin();
131          it != m_channelInfoByName.end();) {
132         delete it.value();
133         it = m_channelInfoByName.erase(it);
134     }
135 
136     m_slots.clear();
137     m_pEffectChain.clear();
138 }
139 
id() const140 QString EffectChainSlot::id() const {
141     if (m_pEffectChain) {
142         return m_pEffectChain->id();
143     }
144     return "";
145 }
146 
getSuperParameter() const147 double EffectChainSlot::getSuperParameter() const {
148     return m_pControlChainSuperParameter->get();
149 }
150 
setSuperParameter(double value,bool force)151 void EffectChainSlot::setSuperParameter(double value, bool force) {
152     m_pControlChainSuperParameter->set(value);
153     slotControlChainSuperParameter(value, force);
154 }
155 
setSuperParameterDefaultValue(double value)156 void EffectChainSlot::setSuperParameterDefaultValue(double value) {
157     m_pControlChainSuperParameter->setDefaultValue(value);
158 }
159 
slotChainNameChanged(const QString &)160 void EffectChainSlot::slotChainNameChanged(const QString&) {
161     emit updated();
162 }
163 
slotChainEnabledChanged(bool bEnabled)164 void EffectChainSlot::slotChainEnabledChanged(bool bEnabled) {
165     m_pControlChainEnabled->set(bEnabled);
166     emit updated();
167 }
168 
slotChainMixChanged(double mix)169 void EffectChainSlot::slotChainMixChanged(double mix) {
170     m_pControlChainMix->set(mix);
171     emit updated();
172 }
173 
slotChainMixModeChanged(EffectChainMixMode mixMode)174 void EffectChainSlot::slotChainMixModeChanged(EffectChainMixMode mixMode) {
175     m_pControlChainMixMode->set(static_cast<double>(mixMode));
176     emit updated();
177 }
178 
slotChainChannelStatusChanged(const QString & group,bool enabled)179 void EffectChainSlot::slotChainChannelStatusChanged(const QString& group,
180                                                     bool enabled) {
181     ChannelInfo* pInfo = m_channelInfoByName.value(group, NULL);
182     if (pInfo != nullptr && pInfo->pEnabled != nullptr) {
183         pInfo->pEnabled->set(enabled);
184         emit updated();
185     }
186 }
187 
slotChainEffectChanged(unsigned int effectSlotNumber,bool shouldEmit)188 void EffectChainSlot::slotChainEffectChanged(unsigned int effectSlotNumber,
189                                              bool shouldEmit) {
190     //qDebug() << debugString() << "slotChainEffectChanged" << effectSlotNumber;
191     if (m_pEffectChain) {
192         const QList<EffectPointer> effects = m_pEffectChain->effects();
193         EffectSlotPointer pSlot;
194         EffectPointer pEffect;
195 
196         if (effects.size() > m_slots.size()) {
197             qWarning() << debugString() << "has too few slots for effect";
198         }
199 
200         if (effectSlotNumber < (unsigned) m_slots.size()) {
201             pSlot = m_slots.at(effectSlotNumber);
202         }
203         if (effectSlotNumber < (unsigned) effects.size()) {
204             pEffect = effects.at(effectSlotNumber);
205         }
206         if (pSlot != nullptr) {
207             pSlot->loadEffect(pEffect, m_pEffectRack->isAdoptMetaknobValueEnabled());
208         }
209 
210         m_pControlNumEffects->forceSet(math_min(
211                 static_cast<unsigned int>(m_slots.size()),
212                 m_pEffectChain->numEffects()));
213 
214         if (shouldEmit) {
215             emit updated();
216         }
217     }
218 }
219 
loadEffectChainToSlot(EffectChainPointer pEffectChain)220 void EffectChainSlot::loadEffectChainToSlot(EffectChainPointer pEffectChain) {
221     //qDebug() << debugString() << "loadEffectChainToSlot" << (pEffectChain ? pEffectChain->id() : "(null)");
222     clear();
223 
224     if (pEffectChain) {
225         m_pEffectChain = pEffectChain;
226 
227         connect(m_pEffectChain.data(),
228                 &EffectChain::effectChanged,
229                 this,
230                 [this](unsigned int value) {
231                     slotChainEffectChanged(value, true);
232                 });
233         connect(m_pEffectChain.data(),
234                 &EffectChain::nameChanged,
235                 this,
236                 &EffectChainSlot::slotChainNameChanged);
237         connect(m_pEffectChain.data(),
238                 &EffectChain::enabledChanged,
239                 this,
240                 &EffectChainSlot::slotChainEnabledChanged);
241         connect(m_pEffectChain.data(),
242                 &EffectChain::mixChanged,
243                 this,
244                 &EffectChainSlot::slotChainMixChanged);
245         connect(m_pEffectChain.data(),
246                 &EffectChain::mixModeChanged,
247                 this,
248                 &EffectChainSlot::slotChainMixModeChanged);
249         connect(m_pEffectChain.data(),
250                 &EffectChain::channelStatusChanged,
251                 this,
252                 &EffectChainSlot::slotChainChannelStatusChanged);
253 
254         m_pControlChainLoaded->forceSet(true);
255         m_pControlChainMixMode->set(
256                 static_cast<double>(m_pEffectChain->mixMode()));
257 
258         // Mix and enabled channels are persistent properties of the chain slot,
259         // not of the chain. Propagate the current settings to the chain.
260         m_pEffectChain->setMix(m_pControlChainMix->get());
261         m_pEffectChain->setEnabled(m_pControlChainEnabled->get() > 0.0);
262 
263         // Don't emit because we will below.
264         for (int i = 0; i < m_slots.size(); ++i) {
265             slotChainEffectChanged(i, false);
266         }
267     }
268 
269     emit effectChainLoaded(pEffectChain);
270     emit updated();
271 }
272 
updateRoutingSwitches()273 void EffectChainSlot::updateRoutingSwitches() {
274     VERIFY_OR_DEBUG_ASSERT(m_pEffectChain) {
275         return;
276     }
277     for (const ChannelInfo* pChannelInfo : qAsConst(m_channelInfoByName)) {
278         if (pChannelInfo->pEnabled->toBool()) {
279             m_pEffectChain->enableForInputChannel(pChannelInfo->handle_group);
280         } else {
281             m_pEffectChain->disableForInputChannel(pChannelInfo->handle_group);
282         }
283     }
284 }
285 
getEffectChain() const286 EffectChainPointer EffectChainSlot::getEffectChain() const {
287     return m_pEffectChain;
288 }
289 
getOrCreateEffectChain(EffectsManager * pEffectsManager)290 EffectChainPointer EffectChainSlot::getOrCreateEffectChain(
291         EffectsManager* pEffectsManager) {
292     if (!m_pEffectChain) {
293         EffectChainPointer pEffectChain(
294                 new EffectChain(pEffectsManager, QString()));
295         //: Name for an empty effect chain, that is created after eject
296         pEffectChain->setName(tr("Empty Chain"));
297         loadEffectChainToSlot(pEffectChain);
298         pEffectChain->addToEngine(m_pEffectRack->getEngineEffectRack(), m_iChainSlotNumber);
299         pEffectChain->updateEngineState();
300         updateRoutingSwitches();
301     }
302     return m_pEffectChain;
303 }
304 
clear()305 void EffectChainSlot::clear() {
306     // Stop listening to signals from any loaded effect
307     if (m_pEffectChain) {
308         m_pEffectChain->removeFromEngine(m_pEffectRack->getEngineEffectRack(),
309                                          m_iChainSlotNumber);
310         for (EffectSlotPointer pSlot : qAsConst(m_slots)) {
311             pSlot->clear();
312         }
313         m_pEffectChain->disconnect(this);
314         m_pEffectChain.clear();
315     }
316     m_pControlNumEffects->forceSet(0.0);
317     m_pControlChainLoaded->forceSet(0.0);
318     m_pControlChainMixMode->set(
319             static_cast<double>(EffectChainMixMode::DrySlashWet));
320     emit updated();
321 }
322 
numSlots() const323 unsigned int EffectChainSlot::numSlots() const {
324     //qDebug() << debugString() << "numSlots";
325     return m_slots.size();
326 }
327 
addEffectSlot(const QString & group)328 EffectSlotPointer EffectChainSlot::addEffectSlot(const QString& group) {
329     //qDebug() << debugString() << "addEffectSlot" << group;
330 
331     EffectSlot* pEffectSlot = new EffectSlot(group, m_iChainSlotNumber,
332                                              m_slots.size());
333     // Rebroadcast effectLoaded signals
334     connect(pEffectSlot, &EffectSlot::effectLoaded, this, &EffectChainSlot::slotEffectLoaded);
335     connect(pEffectSlot, &EffectSlot::clearEffect, this, &EffectChainSlot::slotClearEffect);
336     connect(pEffectSlot, &EffectSlot::nextEffect, this, &EffectChainSlot::nextEffect);
337     connect(pEffectSlot, &EffectSlot::prevEffect, this, &EffectChainSlot::prevEffect);
338 
339     EffectSlotPointer pSlot(pEffectSlot);
340     m_slots.append(pSlot);
341     int numEffectSlots = static_cast<int>(m_pControlNumEffectSlots->get()) + 1;
342     m_pControlNumEffectSlots->forceSet(numEffectSlots);
343     m_pControlChainFocusedEffect->setStates(numEffectSlots);
344     return pSlot;
345 }
346 
registerInputChannel(const ChannelHandleAndGroup & handle_group)347 void EffectChainSlot::registerInputChannel(const ChannelHandleAndGroup& handle_group) {
348     VERIFY_OR_DEBUG_ASSERT(!m_channelInfoByName.contains(handle_group.name())) {
349         return;
350     }
351 
352     double initialValue = 0.0;
353     int deckNumber;
354     if (PlayerManager::isDeckGroup(handle_group.name(), &deckNumber) &&
355         (m_iChainSlotNumber + 1) == (unsigned) deckNumber) {
356         initialValue = 1.0;
357     }
358     ControlPushButton* pEnableControl = new ControlPushButton(
359             ConfigKey(m_group, QString("group_%1_enable").arg(handle_group.name())),
360             true, initialValue);
361     pEnableControl->setButtonMode(ControlPushButton::POWERWINDOW);
362 
363     ChannelInfo* pInfo = new ChannelInfo(handle_group, pEnableControl);
364     m_channelInfoByName[handle_group.name()] = pInfo;
365     connect(pEnableControl, &ControlPushButton::valueChanged,
366             this, [this, handle_group] { slotChannelStatusChanged(handle_group.name()); });
367 
368 }
369 
slotEffectLoaded(EffectPointer pEffect,unsigned int slotNumber)370 void EffectChainSlot::slotEffectLoaded(EffectPointer pEffect, unsigned int slotNumber) {
371     // const int is a safe read... don't bother locking
372     emit effectLoaded(pEffect, m_iChainSlotNumber, slotNumber);
373 }
374 
slotClearEffect(unsigned int iEffectSlotNumber)375 void EffectChainSlot::slotClearEffect(unsigned int iEffectSlotNumber) {
376     if (m_pEffectChain) {
377         m_pEffectChain->removeEffect(iEffectSlotNumber);
378     }
379 }
380 
getEffectSlot(unsigned int slotNumber)381 EffectSlotPointer EffectChainSlot::getEffectSlot(unsigned int slotNumber) {
382     //qDebug() << debugString() << "getEffectSlot" << slotNumber;
383     if (slotNumber >= static_cast<unsigned int>(m_slots.size())) {
384         qWarning() << "WARNING: slotNumber out of range";
385         return EffectSlotPointer();
386     }
387     return m_slots[slotNumber];
388 }
389 
slotControlClear(double v)390 void EffectChainSlot::slotControlClear(double v) {
391     if (v > 0) {
392         clear();
393     }
394 }
395 
slotControlChainEnabled(double v)396 void EffectChainSlot::slotControlChainEnabled(double v) {
397     //qDebug() << debugString() << "slotControlChainEnabled" << v;
398     if (m_pEffectChain) {
399         m_pEffectChain->setEnabled(v > 0);
400     }
401 }
402 
slotControlChainMix(double v)403 void EffectChainSlot::slotControlChainMix(double v) {
404     //qDebug() << debugString() << "slotControlChainMix" << v;
405 
406     // Clamp to [0.0, 1.0]
407     if (v < 0.0 || v > 1.0) {
408         qWarning() << debugString() << "value out of limits";
409         v = math_clamp(v, 0.0, 1.0);
410         m_pControlChainMix->set(v);
411     }
412     if (m_pEffectChain) {
413         m_pEffectChain->setMix(v);
414     }
415 }
416 
slotControlChainSuperParameter(double v,bool force)417 void EffectChainSlot::slotControlChainSuperParameter(double v, bool force) {
418     //qDebug() << debugString() << "slotControlChainSuperParameter" << v;
419 
420     // Clamp to [0.0, 1.0]
421     if (v < 0.0 || v > 1.0) {
422         qWarning() << debugString() << "value out of limits";
423         v = math_clamp(v, 0.0, 1.0);
424         m_pControlChainSuperParameter->set(v);
425     }
426     for (const auto& pSlot : qAsConst(m_slots)) {
427         pSlot->setMetaParameter(v, force);
428     }
429 }
430 
slotControlChainMixMode(double v)431 void EffectChainSlot::slotControlChainMixMode(double v) {
432     // Intermediate cast to integer is needed for VC++.
433     EffectChainMixMode type = static_cast<EffectChainMixMode>(int(v));
434     (void)v; // this avoids a false warning with g++ 4.8.1
435     if (m_pEffectChain && type < EffectChainMixMode::NumMixModes) {
436         m_pEffectChain->setMixMode(type);
437     }
438 }
439 
slotControlChainSelector(double v)440 void EffectChainSlot::slotControlChainSelector(double v) {
441     //qDebug() << debugString() << "slotControlChainSelector" << v;
442     if (v > 0) {
443         emit nextChain(m_iChainSlotNumber, m_pEffectChain);
444     } else if (v < 0) {
445         emit prevChain(m_iChainSlotNumber, m_pEffectChain);
446     }
447 }
448 
slotControlChainNextPreset(double v)449 void EffectChainSlot::slotControlChainNextPreset(double v) {
450     //qDebug() << debugString() << "slotControlChainNextPreset" << v;
451     if (v > 0) {
452         slotControlChainSelector(1);
453     }
454 }
455 
slotControlChainPrevPreset(double v)456 void EffectChainSlot::slotControlChainPrevPreset(double v) {
457     //qDebug() << debugString() << "slotControlChainPrevPreset" << v;
458     if (v > 0) {
459         slotControlChainSelector(-1);
460     }
461 }
462 
slotChannelStatusChanged(const QString & group)463 void EffectChainSlot::slotChannelStatusChanged(const QString& group) {
464     if (m_pEffectChain) {
465         ChannelInfo* pChannelInfo = m_channelInfoByName.value(group, NULL);
466         if (pChannelInfo != nullptr && pChannelInfo->pEnabled != nullptr) {
467             bool bEnable = pChannelInfo->pEnabled->toBool();
468             if (bEnable) {
469                 m_pEffectChain->enableForInputChannel(pChannelInfo->handle_group);
470             } else {
471                 m_pEffectChain->disableForInputChannel(pChannelInfo->handle_group);
472             }
473         }
474     }
475 }
476 
getChainSlotNumber() const477 unsigned int EffectChainSlot::getChainSlotNumber() const {
478     return m_iChainSlotNumber;
479 }
480 
toXml(QDomDocument * doc) const481 QDomElement EffectChainSlot::toXml(QDomDocument* doc) const {
482     QDomElement chainElement = doc->createElement(EffectXml::Chain);
483     if (m_pEffectChain == nullptr) {
484         // ejected chains are stored empty <EffectChain/>
485         return chainElement;
486     }
487 
488     XmlParse::addElement(*doc, chainElement, EffectXml::ChainId,
489             m_pEffectChain->id());
490     XmlParse::addElement(*doc, chainElement, EffectXml::ChainName,
491             m_pEffectChain->name());
492     XmlParse::addElement(*doc, chainElement, EffectXml::ChainDescription,
493             m_pEffectChain->description());
494     XmlParse::addElement(*doc, chainElement, EffectXml::ChainMixMode,
495             EffectChain::mixModeToString(
496                     static_cast<EffectChainMixMode>(
497                             static_cast<int>(m_pControlChainMixMode->get()))));
498     XmlParse::addElement(*doc, chainElement, EffectXml::ChainSuperParameter,
499             QString::number(m_pControlChainSuperParameter->get()));
500 
501     QDomElement effectsElement = doc->createElement(EffectXml::EffectsRoot);
502     for (const auto& pEffectSlot : m_slots) {
503         QDomElement effectNode;
504         if (pEffectSlot->getEffect()) {
505             effectNode = pEffectSlot->toXml(doc);
506         } else {
507             // Create empty element to ensure effects stay in order
508             // if there are empty slots before loaded slots.
509             effectNode = doc->createElement(EffectXml::Effect);
510         }
511         effectsElement.appendChild(effectNode);
512     }
513     chainElement.appendChild(effectsElement);
514 
515     return chainElement;
516 }
517 
loadChainSlotFromXml(const QDomElement & effectChainElement)518 void EffectChainSlot::loadChainSlotFromXml(const QDomElement& effectChainElement) {
519     if (!effectChainElement.hasChildNodes()) {
520         return;
521     }
522 
523     // FIXME: mix mode is set in EffectChain::createFromXml
524 
525     m_pControlChainSuperParameter->set(XmlParse::selectNodeDouble(
526                                           effectChainElement,
527                                           EffectXml::ChainSuperParameter));
528 
529     QDomElement effectsElement = XmlParse::selectElement(effectChainElement,
530                                                          EffectXml::EffectsRoot);
531     QDomNodeList effectsNodeList = effectsElement.childNodes();
532     for (int i = 0; i < m_slots.size(); ++i) {
533         if (m_slots[i] != nullptr) {
534             QDomNode effectNode = effectsNodeList.at(i);
535             if (effectNode.isElement()) {
536                 QDomElement effectElement = effectNode.toElement();
537                 m_slots[i]->loadEffectSlotFromXml(effectElement);
538             }
539         }
540     }
541 }
542