1 #include "effects/effectslot.h"
2 
3 #include <QDebug>
4 
5 #include "control/controlencoder.h"
6 #include "control/controlproxy.h"
7 #include "control/controlpushbutton.h"
8 #include "effects/effectxmlelements.h"
9 #include "moc_effectslot.cpp"
10 #include "util/math.h"
11 #include "util/xml.h"
12 
13 // The maximum number of effect parameters we're going to support.
14 const unsigned int kDefaultMaxParameters = 16;
15 
EffectSlot(const QString & group,const unsigned int iChainNumber,const unsigned int iEffectnumber)16 EffectSlot::EffectSlot(const QString& group,
17                        const unsigned int iChainNumber,
18                        const unsigned int iEffectnumber)
19         : m_iChainNumber(iChainNumber),
20           m_iEffectNumber(iEffectnumber),
21           m_group(group) {
22     m_pControlLoaded = new ControlObject(ConfigKey(m_group, "loaded"));
23     m_pControlLoaded->setReadOnly();
24 
25     m_pControlNumParameters = new ControlObject(ConfigKey(m_group, "num_parameters"));
26     m_pControlNumParameters->setReadOnly();
27 
28     m_pControlNumParameterSlots = new ControlObject(ConfigKey(m_group, "num_parameterslots"));
29     m_pControlNumParameterSlots->setReadOnly();
30 
31     m_pControlNumButtonParameters = new ControlObject(ConfigKey(m_group, "num_button_parameters"));
32     m_pControlNumButtonParameters->setReadOnly();
33 
34     m_pControlNumButtonParameterSlots = new ControlObject(ConfigKey(m_group, "num_button_parameterslots"));
35     m_pControlNumButtonParameterSlots->setReadOnly();
36 
37     // Default to disabled to prevent accidental activation of effects
38     // at the beginning of a set.
39     m_pControlEnabled = new ControlPushButton(ConfigKey(m_group, "enabled"));
40     m_pControlEnabled->setButtonMode(ControlPushButton::POWERWINDOW);
41     connect(m_pControlEnabled, &ControlPushButton::valueChanged, this, &EffectSlot::slotEnabled);
42 
43     m_pControlNextEffect = new ControlPushButton(ConfigKey(m_group, "next_effect"));
44     connect(m_pControlNextEffect,
45             &ControlPushButton::valueChanged,
46             this,
47             &EffectSlot::slotNextEffect);
48 
49     m_pControlPrevEffect = new ControlPushButton(ConfigKey(m_group, "prev_effect"));
50     connect(m_pControlPrevEffect,
51             &ControlPushButton::valueChanged,
52             this,
53             &EffectSlot::slotPrevEffect);
54 
55     // Ignoring no-ops is important since this is for +/- tickers.
56     m_pControlEffectSelector = new ControlEncoder(ConfigKey(m_group, "effect_selector"), false);
57     connect(m_pControlEffectSelector,
58             &ControlEncoder::valueChanged,
59             this,
60             &EffectSlot::slotEffectSelector);
61 
62     m_pControlClear = new ControlPushButton(ConfigKey(m_group, "clear"));
63     connect(m_pControlClear, &ControlPushButton::valueChanged, this, &EffectSlot::slotClear);
64 
65     for (unsigned int i = 0; i < kDefaultMaxParameters; ++i) {
66         addEffectParameterSlot();
67         addEffectButtonParameterSlot();
68     }
69 
70     m_pControlMetaParameter = new ControlPotmeter(ConfigKey(m_group, "meta"), 0.0, 1.0);
71     connect(m_pControlMetaParameter, &ControlPotmeter::valueChanged, this, [this](double value) {
72         slotEffectMetaParameter(value, false);
73     });
74     m_pControlMetaParameter->set(0.0);
75     m_pControlMetaParameter->setDefaultValue(0.0);
76 
77     m_pSoftTakeover = new SoftTakeover();
78 
79     clear();
80 }
81 
~EffectSlot()82 EffectSlot::~EffectSlot() {
83     //qDebug() << debugString() << "destroyed";
84     clear();
85 
86     delete m_pControlLoaded;
87     delete m_pControlNumParameters;
88     delete m_pControlNumParameterSlots;
89     delete m_pControlNumButtonParameters;
90     delete m_pControlNumButtonParameterSlots;
91     delete m_pControlNextEffect;
92     delete m_pControlPrevEffect;
93     delete m_pControlEffectSelector;
94     delete m_pControlClear;
95     delete m_pControlEnabled;
96     delete m_pControlMetaParameter;
97     delete m_pSoftTakeover;
98 }
99 
addEffectParameterSlot()100 EffectParameterSlotPointer EffectSlot::addEffectParameterSlot() {
101     EffectParameterSlotPointer pParameter = EffectParameterSlotPointer(
102             new EffectParameterSlot(m_group, m_parameters.size()));
103     m_parameters.append(pParameter);
104     m_pControlNumParameterSlots->forceSet(
105             m_pControlNumParameterSlots->get() + 1);
106     return pParameter;
107 }
108 
addEffectButtonParameterSlot()109 EffectButtonParameterSlotPointer EffectSlot::addEffectButtonParameterSlot() {
110     EffectButtonParameterSlotPointer pParameter = EffectButtonParameterSlotPointer(
111             new EffectButtonParameterSlot(m_group, m_buttonParameters.size()));
112     m_buttonParameters.append(pParameter);
113     m_pControlNumButtonParameterSlots->forceSet(
114             m_pControlNumButtonParameterSlots->get() + 1);
115     return pParameter;
116 }
117 
getEffect() const118 EffectPointer EffectSlot::getEffect() const {
119     return m_pEffect;
120 }
121 
numParameterSlots() const122 unsigned int EffectSlot::numParameterSlots() const {
123     return m_parameters.size();
124 }
125 
numButtonParameterSlots() const126 unsigned int EffectSlot::numButtonParameterSlots() const {
127     return m_buttonParameters.size();
128 }
129 
slotEnabled(double v)130 void EffectSlot::slotEnabled(double v) {
131     //qDebug() << debugString() << "slotEnabled" << v;
132     if (m_pEffect) {
133         m_pEffect->setEnabled(v > 0);
134     }
135 }
136 
slotEffectEnabledChanged(bool enabled)137 void EffectSlot::slotEffectEnabledChanged(bool enabled) {
138     m_pControlEnabled->set(enabled);
139 }
140 
getEffectParameterSlot(unsigned int slotNumber)141 EffectParameterSlotPointer EffectSlot::getEffectParameterSlot(unsigned int slotNumber) {
142     //qDebug() << debugString() << "getEffectParameterSlot" << slotNumber;
143     if (slotNumber >= static_cast<unsigned int>(m_parameters.size())) {
144         qWarning() << "WARNING: slotNumber out of range";
145         return EffectParameterSlotPointer();
146     }
147     return m_parameters[slotNumber];
148 }
149 
getEffectButtonParameterSlot(unsigned int slotNumber)150 EffectButtonParameterSlotPointer EffectSlot::getEffectButtonParameterSlot(unsigned int slotNumber) {
151     //qDebug() << debugString() << "getEffectParameterSlot" << slotNumber;
152     if (slotNumber >= static_cast<unsigned int>(m_buttonParameters.size())) {
153         qWarning() << "WARNING: slotNumber out of range";
154         return EffectButtonParameterSlotPointer();
155     }
156     return m_buttonParameters[slotNumber];
157 }
158 
loadEffect(EffectPointer pEffect,bool adoptMetaknobPosition)159 void EffectSlot::loadEffect(EffectPointer pEffect, bool adoptMetaknobPosition) {
160     //qDebug() << debugString() << "loadEffect"
161     //         << (pEffect ? pEffect->getManifest().name() : "(null)");
162     if (pEffect) {
163         m_pEffect = pEffect;
164         m_pControlLoaded->forceSet(1.0);
165         m_pControlNumParameters->forceSet(pEffect->numKnobParameters());
166         m_pControlNumButtonParameters->forceSet(pEffect->numButtonParameters());
167 
168         // The enabled status persists in the EffectSlot when loading a new
169         // EffectPointer to the EffectSlot. Effects and EngineEffects default to
170         // disabled, so if this EffectSlot was enabled, enable the Effect and EngineEffect.
171         pEffect->setEnabled(m_pControlEnabled->toBool());
172 
173         connect(pEffect.data(),
174                 &Effect::enabledChanged,
175                 this,
176                 &EffectSlot::slotEffectEnabledChanged);
177 
178         while (static_cast<unsigned int>(m_parameters.size())
179                 < pEffect->numKnobParameters()) {
180             addEffectParameterSlot();
181         }
182 
183         while (static_cast<unsigned int>(m_buttonParameters.size())
184                 < pEffect->numButtonParameters()) {
185             addEffectButtonParameterSlot();
186         }
187 
188         for (const auto& pParameter : qAsConst(m_parameters)) {
189             pParameter->loadEffect(pEffect);
190         }
191         for (const auto& pParameter : qAsConst(m_buttonParameters)) {
192             pParameter->loadEffect(pEffect);
193         }
194 
195         if (adoptMetaknobPosition) {
196             slotEffectMetaParameter(m_pControlMetaParameter->get(), true);
197         } else {
198             m_pControlMetaParameter->set(pEffect->getMetaknobDefault());
199             slotEffectMetaParameter(pEffect->getMetaknobDefault(), true);
200         }
201 
202         emit effectLoaded(pEffect, m_iEffectNumber);
203     } else {
204         clear();
205         // Broadcasts a null effect pointer
206         emit effectLoaded(EffectPointer(), m_iEffectNumber);
207     }
208     emit updated();
209 }
210 
clear()211 void EffectSlot::clear() {
212     if (m_pEffect) {
213         m_pEffect->disconnect(this);
214     }
215     m_pControlLoaded->forceSet(0.0);
216     m_pControlNumParameters->forceSet(0.0);
217     m_pControlNumButtonParameters->forceSet(0.0);
218     for (const auto& pParameter : qAsConst(m_parameters)) {
219         pParameter->clear();
220     }
221     for (const auto& pParameter : qAsConst(m_buttonParameters)) {
222         pParameter->clear();
223     }
224     m_pEffect.clear();
225     emit updated();
226 }
227 
slotPrevEffect(double v)228 void EffectSlot::slotPrevEffect(double v) {
229     if (v > 0) {
230         slotEffectSelector(-1);
231     }
232 }
233 
slotNextEffect(double v)234 void EffectSlot::slotNextEffect(double v) {
235     if (v > 0) {
236         slotEffectSelector(1);
237     }
238 }
239 
slotEffectSelector(double v)240 void EffectSlot::slotEffectSelector(double v) {
241     if (v > 0) {
242         emit nextEffect(m_iChainNumber, m_iEffectNumber, m_pEffect);
243     } else if (v < 0) {
244         emit prevEffect(m_iChainNumber, m_iEffectNumber, m_pEffect);
245     }
246 }
247 
slotClear(double v)248 void EffectSlot::slotClear(double v) {
249     if (v > 0) {
250         emit clearEffect(m_iEffectNumber);
251     }
252 }
253 
syncSofttakeover()254 void EffectSlot::syncSofttakeover() {
255     for (const auto& pParameterSlot : qAsConst(m_parameters)) {
256         pParameterSlot->syncSofttakeover();
257     }
258 }
259 
getMetaParameter() const260 double EffectSlot::getMetaParameter() const {
261     return m_pControlMetaParameter->get();
262 }
263 
264 // This function is for the superknob to update individual effects' meta knobs
265 // slotEffectMetaParameter does not need to update m_pControlMetaParameter's value
setMetaParameter(double v,bool force)266 void EffectSlot::setMetaParameter(double v, bool force) {
267     if (!m_pSoftTakeover->ignore(m_pControlMetaParameter, v)
268             || !m_pControlEnabled->toBool()
269             || force) {
270         m_pControlMetaParameter->set(v);
271         slotEffectMetaParameter(v, force);
272     }
273 }
274 
slotEffectMetaParameter(double v,bool force)275 void EffectSlot::slotEffectMetaParameter(double v, bool force) {
276     // Clamp to [0.0, 1.0]
277     if (v < 0.0 || v > 1.0) {
278         qWarning() << debugString() << "value out of limits";
279         v = math_clamp(v, 0.0, 1.0);
280         m_pControlMetaParameter->set(v);
281     }
282     if (!m_pControlEnabled->toBool()) {
283         force = true;
284     }
285     for (const auto& pParameterSlot : qAsConst(m_parameters)) {
286         pParameterSlot->onEffectMetaParameterChanged(v, force);
287     }
288 }
289 
toXml(QDomDocument * doc) const290 QDomElement EffectSlot::toXml(QDomDocument* doc) const {
291     QDomElement effectElement = doc->createElement(EffectXml::Effect);
292     if (!m_pEffect) {
293         return effectElement;
294     }
295 
296     QDomElement metaKnobElement = doc->createElement(EffectXml::EffectMetaParameter);
297     XmlParse::addElement(*doc, effectElement,
298                          EffectXml::EffectMetaParameter,
299                          QString::number(m_pControlMetaParameter->get()));
300     EffectManifestPointer pManifest = m_pEffect->getManifest();
301     XmlParse::addElement(*doc, effectElement,
302                          EffectXml::EffectId, pManifest->id());
303     XmlParse::addElement(*doc, effectElement,
304                          EffectXml::EffectVersion, pManifest->version());
305 
306     QDomElement parametersElement = doc->createElement(EffectXml::ParametersRoot);
307 
308     for (const auto& pParameter : m_parameters) {
309         QDomElement parameterElement = pParameter->toXml(doc);
310         if (!parameterElement.hasChildNodes()) {
311             continue;
312         }
313         EffectManifestParameterPointer manifest = pParameter->getManifest();
314         if (!manifest) {
315             continue;
316         }
317         XmlParse::addElement(*doc, parameterElement,
318                              EffectXml::ParameterId,
319                              manifest->id());
320         parametersElement.appendChild(parameterElement);
321     }
322     for (const auto& pParameter : m_buttonParameters) {
323         QDomElement parameterElement = pParameter->toXml(doc);
324         if (!parameterElement.hasChildNodes()) {
325             continue;
326         }
327         EffectManifestParameterPointer manifest = pParameter->getManifest();
328         if (!manifest) {
329             continue;
330         }
331         XmlParse::addElement(*doc, parameterElement,
332                              EffectXml::ParameterId,
333                              pParameter->getManifest()->id());
334         parametersElement.appendChild(parameterElement);
335     }
336 
337     effectElement.appendChild(parametersElement);
338 
339     return effectElement;
340 }
341 
loadEffectSlotFromXml(const QDomElement & effectElement)342 void EffectSlot::loadEffectSlotFromXml(const QDomElement& effectElement) {
343     if (!m_pEffect) {
344         return;
345     }
346 
347     if (!effectElement.hasChildNodes()) {
348         return;
349     }
350 
351     QDomElement effectIdElement = XmlParse::selectElement(effectElement,
352                                                           EffectXml::EffectId);
353     if (m_pEffect->getManifest()->id() != effectIdElement.text()) {
354         qWarning() << "EffectSlot::loadEffectSlotFromXml"
355                    << "effect ID in XML does not match presently loaded effect, ignoring.";
356         return;
357     }
358 
359     m_pControlMetaParameter->set(XmlParse::selectNodeDouble(
360             effectElement, EffectXml::EffectMetaParameter));
361     QDomElement parametersElement = XmlParse::selectElement(
362             effectElement, EffectXml::ParametersRoot);
363     if (!parametersElement.hasChildNodes()) {
364         return;
365     }
366 
367     QMap<QString, EffectParameterSlotBasePointer> parametersById;
368     for (const auto& pParameter : qAsConst(m_parameters)) {
369         EffectManifestParameterPointer manifest = pParameter->getManifest();
370         if (manifest) {
371             parametersById.insert(manifest->id(), pParameter);
372         }
373     }
374     for (const auto& pParameter : qAsConst(m_buttonParameters)) {
375         EffectManifestParameterPointer manifest = pParameter->getManifest();
376         if (manifest) {
377             parametersById.insert(manifest->id(), pParameter);
378         }
379     }
380 
381     QDomNodeList parametersNodeList = parametersElement.childNodes();
382     for (int i = 0; i < parametersNodeList.size(); ++i) {
383         QDomNode parameterNode = parametersNodeList.at(i);
384         if (parameterNode.isElement()) {
385             const QString id = XmlParse::selectNodeQString(parameterNode,
386                                                            EffectXml::ParameterId);
387             EffectParameterSlotBasePointer pParameterSlot = parametersById.value(id);
388             if (pParameterSlot != nullptr) {
389                 pParameterSlot->loadParameterSlotFromXml(parameterNode.toElement());
390             }
391         }
392     }
393 }
394