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