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