1 /*
2 * Carla Plugin
3 * Copyright (C) 2011-2020 Filipe Coelho <falktx@falktx.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
16 */
17
18 #include "CarlaPluginInternal.hpp"
19 #include "CarlaEngine.hpp"
20
21 #include "CarlaBackendUtils.hpp"
22 #include "CarlaBase64Utils.hpp"
23 #include "CarlaMathUtils.hpp"
24 #include "CarlaMIDI.h"
25 #include "CarlaPluginUI.hpp"
26 #include "CarlaScopeUtils.hpp"
27 #include "CarlaStringList.hpp"
28
29 #include <ctime>
30
31 #include "water/files/File.h"
32 #include "water/streams/MemoryOutputStream.h"
33 #include "water/xml/XmlDocument.h"
34 #include "water/xml/XmlElement.h"
35
36 using water::CharPointer_UTF8;
37 using water::File;
38 using water::MemoryOutputStream;
39 using water::Result;
40 using water::String;
41 using water::XmlDocument;
42 using water::XmlElement;
43
44 CARLA_BACKEND_START_NAMESPACE
45
46 // -------------------------------------------------------------------------------------------------------------------
47 // Fallback data
48
49 static const ParameterData kParameterDataNull = { PARAMETER_UNKNOWN, 0x0, PARAMETER_NULL, -1, 0, CONTROL_INDEX_NONE, 0.0f, 1.0f, 0x0 };
50 static const ParameterRanges kParameterRangesNull = { 0.0f, 0.0f, 1.0f, 0.01f, 0.0001f, 0.1f };
51 static const MidiProgramData kMidiProgramDataNull = { 0, 0, nullptr };
52
53 static const CustomData kCustomDataFallback = { nullptr, nullptr, nullptr };
54 static /* */ CustomData kCustomDataFallbackNC = { nullptr, nullptr, nullptr };
55 static const PluginPostRtEvent kPluginPostRtEventFallback = { kPluginPostRtEventNull, false, {} };
56
57 // -------------------------------------------------------------------------------------------------------------------
58 // ParamSymbol struct, needed for CarlaPlugin::loadStateSave()
59
60 struct ParamSymbol {
61 int32_t index;
62 const char* symbol;
63
ParamSymbolParamSymbol64 ParamSymbol(const uint32_t i, const char* const s)
65 : index(static_cast<int32_t>(i)),
66 symbol(carla_strdup(s)) {}
67
~ParamSymbolParamSymbol68 ~ParamSymbol() noexcept
69 {
70 CARLA_SAFE_ASSERT_RETURN(symbol != nullptr,)
71
72 delete[] symbol;
73 symbol = nullptr;
74 }
75
76 #ifdef CARLA_PROPER_CPP11_SUPPORT
77 ParamSymbol() = delete;
78 CARLA_DECLARE_NON_COPY_STRUCT(ParamSymbol)
79 #endif
80 };
81
82 // -------------------------------------------------------------------------------------------------------------------
83 // Constructor and destructor
84
CarlaPlugin(CarlaEngine * const engine,const uint id)85 CarlaPlugin::CarlaPlugin(CarlaEngine* const engine, const uint id)
86 : pData(new ProtectedData(engine, id))
87 {
88 CARLA_SAFE_ASSERT_RETURN(engine != nullptr,);
89 CARLA_SAFE_ASSERT(id < engine->getMaxPluginNumber());
90 carla_debug("CarlaPlugin::CarlaPlugin(%p, %i)", engine, id);
91
92 switch (engine->getProccessMode())
93 {
94 case ENGINE_PROCESS_MODE_SINGLE_CLIENT:
95 case ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS:
96 CARLA_SAFE_ASSERT(id < MAX_DEFAULT_PLUGINS);
97 break;
98
99 case ENGINE_PROCESS_MODE_CONTINUOUS_RACK:
100 CARLA_SAFE_ASSERT(id < MAX_RACK_PLUGINS);
101 break;
102
103 case ENGINE_PROCESS_MODE_PATCHBAY:
104 CARLA_SAFE_ASSERT(id < MAX_PATCHBAY_PLUGINS);
105 break;
106
107 case ENGINE_PROCESS_MODE_BRIDGE:
108 CARLA_SAFE_ASSERT(id == 0);
109 break;
110 }
111 }
112
~CarlaPlugin()113 CarlaPlugin::~CarlaPlugin()
114 {
115 carla_debug("CarlaPlugin::~CarlaPlugin()");
116
117 delete pData;
118 }
119
120 // -------------------------------------------------------------------
121 // Information (base)
122
getId() const123 uint CarlaPlugin::getId() const noexcept
124 {
125 return pData->id;
126 }
127
getHints() const128 uint CarlaPlugin::getHints() const noexcept
129 {
130 return pData->hints;
131 }
132
getOptionsEnabled() const133 uint CarlaPlugin::getOptionsEnabled() const noexcept
134 {
135 return pData->options;
136 }
137
isEnabled() const138 bool CarlaPlugin::isEnabled() const noexcept
139 {
140 return pData->enabled;
141 }
142
getName() const143 const char* CarlaPlugin::getName() const noexcept
144 {
145 return pData->name;
146 }
147
getFilename() const148 const char* CarlaPlugin::getFilename() const noexcept
149 {
150 return pData->filename;
151 }
152
getIconName() const153 const char* CarlaPlugin::getIconName() const noexcept
154 {
155 return pData->iconName;
156 }
157
getCategory() const158 PluginCategory CarlaPlugin::getCategory() const noexcept
159 {
160 return getPluginCategoryFromName(pData->name);
161 }
162
getUniqueId() const163 int64_t CarlaPlugin::getUniqueId() const noexcept
164 {
165 return 0;
166 }
167
getLatencyInFrames() const168 uint32_t CarlaPlugin::getLatencyInFrames() const noexcept
169 {
170 return 0;
171 }
172
173 // -------------------------------------------------------------------
174 // Information (count)
175
getAudioInCount() const176 uint32_t CarlaPlugin::getAudioInCount() const noexcept
177 {
178 return pData->audioIn.count;
179 }
180
getAudioOutCount() const181 uint32_t CarlaPlugin::getAudioOutCount() const noexcept
182 {
183 return pData->audioOut.count;
184 }
185
getCVInCount() const186 uint32_t CarlaPlugin::getCVInCount() const noexcept
187 {
188 return pData->cvIn.count;
189 }
190
getCVOutCount() const191 uint32_t CarlaPlugin::getCVOutCount() const noexcept
192 {
193 return pData->cvOut.count;
194 }
195
getMidiInCount() const196 uint32_t CarlaPlugin::getMidiInCount() const noexcept
197 {
198 return (pData->extraHints & PLUGIN_EXTRA_HINT_HAS_MIDI_IN) ? 1 : 0;
199 }
200
getMidiOutCount() const201 uint32_t CarlaPlugin::getMidiOutCount() const noexcept
202 {
203 return (pData->extraHints & PLUGIN_EXTRA_HINT_HAS_MIDI_OUT) ? 1 : 0;
204 }
205
getParameterCount() const206 uint32_t CarlaPlugin::getParameterCount() const noexcept
207 {
208 return pData->param.count;
209 }
210
getParameterScalePointCount(const uint32_t parameterId) const211 uint32_t CarlaPlugin::getParameterScalePointCount(const uint32_t parameterId) const noexcept
212 {
213 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, 0);
214 return 0;
215 }
216
getProgramCount() const217 uint32_t CarlaPlugin::getProgramCount() const noexcept
218 {
219 return pData->prog.count;
220 }
221
getMidiProgramCount() const222 uint32_t CarlaPlugin::getMidiProgramCount() const noexcept
223 {
224 return pData->midiprog.count;
225 }
226
getCustomDataCount() const227 uint32_t CarlaPlugin::getCustomDataCount() const noexcept
228 {
229 return static_cast<uint32_t>(pData->custom.count());
230 }
231
232 // -------------------------------------------------------------------
233 // Information (current data)
234
getCurrentProgram() const235 int32_t CarlaPlugin::getCurrentProgram() const noexcept
236 {
237 return pData->prog.current;
238 }
239
getCurrentMidiProgram() const240 int32_t CarlaPlugin::getCurrentMidiProgram() const noexcept
241 {
242 return pData->midiprog.current;
243 }
244
getParameterData(const uint32_t parameterId) const245 const ParameterData& CarlaPlugin::getParameterData(const uint32_t parameterId) const noexcept
246 {
247 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, kParameterDataNull);
248 return pData->param.data[parameterId];
249 }
250
getParameterRanges(const uint32_t parameterId) const251 const ParameterRanges& CarlaPlugin::getParameterRanges(const uint32_t parameterId) const noexcept
252 {
253 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, kParameterRangesNull);
254 return pData->param.ranges[parameterId];
255 }
256
isParameterOutput(const uint32_t parameterId) const257 bool CarlaPlugin::isParameterOutput(const uint32_t parameterId) const noexcept
258 {
259 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, false);
260 return (pData->param.data[parameterId].type == PARAMETER_OUTPUT);
261 }
262
getMidiProgramData(const uint32_t index) const263 const MidiProgramData& CarlaPlugin::getMidiProgramData(const uint32_t index) const noexcept
264 {
265 CARLA_SAFE_ASSERT_RETURN(index < pData->midiprog.count, kMidiProgramDataNull);
266 return pData->midiprog.data[index];
267 }
268
getCustomData(const uint32_t index) const269 const CustomData& CarlaPlugin::getCustomData(const uint32_t index) const noexcept
270 {
271 return pData->custom.getAt(index, kCustomDataFallback);
272 }
273
getChunkData(void ** const dataPtr)274 std::size_t CarlaPlugin::getChunkData(void** const dataPtr) noexcept
275 {
276 CARLA_SAFE_ASSERT_RETURN(dataPtr != nullptr, 0);
277 CARLA_SAFE_ASSERT(false); // this should never happen
278 return 0;
279 }
280
281 // -------------------------------------------------------------------
282 // Information (per-plugin data)
283
getOptionsAvailable() const284 uint CarlaPlugin::getOptionsAvailable() const noexcept
285 {
286 CARLA_SAFE_ASSERT(false); // this should never happen
287 return 0x0;
288 }
289
getParameterValue(const uint32_t parameterId) const290 float CarlaPlugin::getParameterValue(const uint32_t parameterId) const noexcept
291 {
292 CARLA_SAFE_ASSERT_RETURN(parameterId < getParameterCount(), 0.0f);
293 CARLA_SAFE_ASSERT(false); // this should never happen
294 return 0.0f;
295 }
296
getParameterScalePointValue(const uint32_t parameterId,const uint32_t scalePointId) const297 float CarlaPlugin::getParameterScalePointValue(const uint32_t parameterId, const uint32_t scalePointId) const noexcept
298 {
299 CARLA_SAFE_ASSERT_RETURN(parameterId < getParameterCount(), 0.0f);
300 CARLA_SAFE_ASSERT_RETURN(scalePointId < getParameterScalePointCount(parameterId), 0.0f);
301 CARLA_SAFE_ASSERT(false); // this should never happen
302 return 0.0f;
303 }
304
getLabel(char * const strBuf) const305 bool CarlaPlugin::getLabel(char* const strBuf) const noexcept
306 {
307 strBuf[0] = '\0';
308 return false;
309 }
310
getMaker(char * const strBuf) const311 bool CarlaPlugin::getMaker(char* const strBuf) const noexcept
312 {
313 strBuf[0] = '\0';
314 return false;
315 }
316
getCopyright(char * const strBuf) const317 bool CarlaPlugin::getCopyright(char* const strBuf) const noexcept
318 {
319 strBuf[0] = '\0';
320 return false;
321 }
322
getRealName(char * const strBuf) const323 bool CarlaPlugin::getRealName(char* const strBuf) const noexcept
324 {
325 strBuf[0] = '\0';
326 return false;
327 }
328
getParameterName(const uint32_t parameterId,char * const strBuf) const329 bool CarlaPlugin::getParameterName(const uint32_t parameterId, char* const strBuf) const noexcept
330 {
331 CARLA_SAFE_ASSERT_RETURN(parameterId < getParameterCount(), false);
332 CARLA_SAFE_ASSERT(false); // this should never happen
333 strBuf[0] = '\0';
334 return false;
335 }
336
getParameterSymbol(const uint32_t parameterId,char * const strBuf) const337 bool CarlaPlugin::getParameterSymbol(const uint32_t parameterId, char* const strBuf) const noexcept
338 {
339 CARLA_SAFE_ASSERT_RETURN(parameterId < getParameterCount(), false);
340 strBuf[0] = '\0';
341 return false;
342 }
343
getParameterText(const uint32_t parameterId,char * const strBuf)344 bool CarlaPlugin::getParameterText(const uint32_t parameterId, char* const strBuf) noexcept
345 {
346 CARLA_SAFE_ASSERT_RETURN(parameterId < getParameterCount(), false);
347 CARLA_SAFE_ASSERT(false); // this should never happen
348 strBuf[0] = '\0';
349 return false;
350 }
351
getParameterUnit(const uint32_t parameterId,char * const strBuf) const352 bool CarlaPlugin::getParameterUnit(const uint32_t parameterId, char* const strBuf) const noexcept
353 {
354 CARLA_SAFE_ASSERT_RETURN(parameterId < getParameterCount(), false);
355 strBuf[0] = '\0';
356 return false;
357 }
358
getParameterComment(const uint32_t parameterId,char * const strBuf) const359 bool CarlaPlugin::getParameterComment(const uint32_t parameterId, char* const strBuf) const noexcept
360 {
361 CARLA_SAFE_ASSERT_RETURN(parameterId < getParameterCount(), false);
362 strBuf[0] = '\0';
363 return false;
364 }
365
getParameterGroupName(const uint32_t parameterId,char * const strBuf) const366 bool CarlaPlugin::getParameterGroupName(const uint32_t parameterId, char* const strBuf) const noexcept
367 {
368 CARLA_SAFE_ASSERT_RETURN(parameterId < getParameterCount(), false);
369 strBuf[0] = '\0';
370 return false;
371 }
372
getParameterScalePointLabel(const uint32_t parameterId,const uint32_t scalePointId,char * const strBuf) const373 bool CarlaPlugin::getParameterScalePointLabel(const uint32_t parameterId, const uint32_t scalePointId, char* const strBuf) const noexcept
374 {
375 CARLA_SAFE_ASSERT_RETURN(parameterId < getParameterCount(), false);
376 CARLA_SAFE_ASSERT_RETURN(scalePointId < getParameterScalePointCount(parameterId), false);
377 CARLA_SAFE_ASSERT(false); // this should never happen
378 strBuf[0] = '\0';
379 return false;
380 }
381
getInternalParameterValue(const int32_t parameterId) const382 float CarlaPlugin::getInternalParameterValue(const int32_t parameterId) const noexcept
383 {
384 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
385 CARLA_SAFE_ASSERT_RETURN(parameterId != PARAMETER_NULL && parameterId > PARAMETER_MAX, 0.0f);
386
387 switch (parameterId)
388 {
389 case PARAMETER_ACTIVE:
390 return pData->active;
391 case PARAMETER_CTRL_CHANNEL:
392 return pData->ctrlChannel;
393 case PARAMETER_DRYWET:
394 return pData->postProc.dryWet;
395 case PARAMETER_VOLUME:
396 return pData->postProc.volume;
397 case PARAMETER_BALANCE_LEFT:
398 return pData->postProc.balanceLeft;
399 case PARAMETER_BALANCE_RIGHT:
400 return pData->postProc.balanceRight;
401 case PARAMETER_PANNING:
402 return pData->postProc.panning;
403 };
404 #endif
405 CARLA_SAFE_ASSERT_RETURN(parameterId >= 0, 0.0f);
406
407 return getParameterValue(static_cast<uint32_t>(parameterId));
408 }
409
getProgramName(const uint32_t index,char * const strBuf) const410 bool CarlaPlugin::getProgramName(const uint32_t index, char* const strBuf) const noexcept
411 {
412 CARLA_SAFE_ASSERT_RETURN(index < pData->prog.count, false);
413 CARLA_SAFE_ASSERT_RETURN(pData->prog.names[index] != nullptr, false);
414 std::strncpy(strBuf, pData->prog.names[index], STR_MAX);
415 return true;
416 }
417
getMidiProgramName(const uint32_t index,char * const strBuf) const418 bool CarlaPlugin::getMidiProgramName(const uint32_t index, char* const strBuf) const noexcept
419 {
420 CARLA_SAFE_ASSERT_RETURN(index < pData->midiprog.count, false);
421 CARLA_SAFE_ASSERT_RETURN(pData->midiprog.data[index].name != nullptr, false);
422 std::strncpy(strBuf, pData->midiprog.data[index].name, STR_MAX);
423 return true;
424 }
425
getParameterCountInfo(uint32_t & ins,uint32_t & outs) const426 void CarlaPlugin::getParameterCountInfo(uint32_t& ins, uint32_t& outs) const noexcept
427 {
428 ins = 0;
429 outs = 0;
430
431 for (uint32_t i=0; i < pData->param.count; ++i)
432 {
433 if (pData->param.data[i].type == PARAMETER_INPUT)
434 ++ins;
435 else if (pData->param.data[i].type == PARAMETER_OUTPUT)
436 ++outs;
437 }
438 }
439
440 // -------------------------------------------------------------------
441 // Set data (state)
442
prepareForSave(bool)443 void CarlaPlugin::prepareForSave(bool)
444 {
445 }
446
resetParameters()447 void CarlaPlugin::resetParameters() noexcept
448 {
449 for (uint i=0; i < pData->param.count; ++i)
450 {
451 const ParameterData& paramData(pData->param.data[i]);
452 const ParameterRanges& paramRanges(pData->param.ranges[i]);
453
454 if (paramData.type != PARAMETER_INPUT)
455 continue;
456 if ((paramData.hints & PARAMETER_IS_ENABLED) == 0)
457 continue;
458
459 setParameterValue(i, paramRanges.def, true, true, true);
460 }
461 }
462
randomizeParameters()463 void CarlaPlugin::randomizeParameters() noexcept
464 {
465 float value, random;
466
467 char strBuf[STR_MAX+1];
468 strBuf[STR_MAX] = '\0';
469
470 std::srand(static_cast<uint>(std::time(nullptr)));
471
472 for (uint i=0; i < pData->param.count; ++i)
473 {
474 const ParameterData& paramData(pData->param.data[i]);
475
476 if (paramData.type != PARAMETER_INPUT)
477 continue;
478 if ((paramData.hints & PARAMETER_IS_ENABLED) == 0)
479 continue;
480
481 if (! getParameterName(i, strBuf))
482 strBuf[0] = '\0';
483
484 if (std::strstr(strBuf, "olume") != nullptr)
485 continue;
486 if (std::strstr(strBuf, "Master") != nullptr)
487 continue;
488
489 const ParameterRanges& paramRanges(pData->param.ranges[i]);
490
491 if (paramData.hints & PARAMETER_IS_BOOLEAN)
492 {
493 random = static_cast<float>(std::rand()) / static_cast<float>(RAND_MAX);
494 value = random > 0.5f ? paramRanges.max : paramRanges.min;
495 }
496 else
497 {
498 random = static_cast<float>(std::rand()) / static_cast<float>(RAND_MAX);
499 value = random * (paramRanges.max - paramRanges.min) + paramRanges.min;
500
501 if (paramData.hints & PARAMETER_IS_INTEGER)
502 value = std::rint(value);
503 }
504
505 setParameterValue(i, value, true, true, true);
506 }
507 }
508
getStateSave(const bool callPrepareForSave)509 const CarlaStateSave& CarlaPlugin::getStateSave(const bool callPrepareForSave)
510 {
511 pData->stateSave.clear();
512
513 if (callPrepareForSave)
514 {
515 pData->stateSave.temporary = true;
516 prepareForSave(true);
517 }
518
519 const PluginType pluginType(getType());
520
521 char strBuf[STR_MAX+1];
522 carla_zeroChars(strBuf, STR_MAX+1);
523
524 // ---------------------------------------------------------------
525 // Basic info
526
527 if (! getLabel(strBuf))
528 strBuf[0] = '\0';
529
530 pData->stateSave.type = carla_strdup(getPluginTypeAsString(pluginType));
531 pData->stateSave.name = carla_strdup(pData->name);
532 pData->stateSave.label = carla_strdup(strBuf);
533 pData->stateSave.uniqueId = getUniqueId();
534 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
535 pData->stateSave.options = pData->options;
536 #endif
537
538 if (pData->filename != nullptr)
539 pData->stateSave.binary = carla_strdup(pData->filename);
540
541 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
542 // ---------------------------------------------------------------
543 // Internals
544
545 pData->stateSave.active = pData->active;
546 pData->stateSave.dryWet = pData->postProc.dryWet;
547 pData->stateSave.volume = pData->postProc.volume;
548 pData->stateSave.balanceLeft = pData->postProc.balanceLeft;
549 pData->stateSave.balanceRight = pData->postProc.balanceRight;
550 pData->stateSave.panning = pData->postProc.panning;
551 pData->stateSave.ctrlChannel = pData->ctrlChannel;
552 #endif
553
554 if (pData->hints & PLUGIN_IS_BRIDGE)
555 waitForBridgeSaveSignal();
556
557 // ---------------------------------------------------------------
558 // Chunk
559
560 bool usingChunk = false;
561
562 if (pData->options & PLUGIN_OPTION_USE_CHUNKS)
563 {
564 void* data = nullptr;
565 const std::size_t dataSize(getChunkData(&data));
566
567 if (data != nullptr && dataSize > 0)
568 {
569 pData->stateSave.chunk = CarlaString::asBase64(data, dataSize).dup();
570
571 if (pluginType != PLUGIN_INTERNAL)
572 usingChunk = true;
573 }
574 }
575
576 // ---------------------------------------------------------------
577 // Current Program
578
579 if (pData->prog.current >= 0 && pluginType != PLUGIN_LV2)
580 {
581 pData->stateSave.currentProgramIndex = pData->prog.current;
582 pData->stateSave.currentProgramName = carla_strdup(pData->prog.names[pData->prog.current]);
583 }
584
585 // ---------------------------------------------------------------
586 // Current MIDI Program
587
588 if (pData->midiprog.current >= 0 && pluginType != PLUGIN_LV2 && pluginType != PLUGIN_SF2)
589 {
590 const MidiProgramData& mpData(pData->midiprog.getCurrent());
591
592 pData->stateSave.currentMidiBank = static_cast<int32_t>(mpData.bank);
593 pData->stateSave.currentMidiProgram = static_cast<int32_t>(mpData.program);
594 }
595
596 // ---------------------------------------------------------------
597 // Parameters
598
599 const float sampleRate(static_cast<float>(pData->engine->getSampleRate()));
600
601 for (uint32_t i=0; i < pData->param.count; ++i)
602 {
603 const ParameterData& paramData(pData->param.data[i]);
604
605 if ((paramData.hints & PARAMETER_IS_ENABLED) == 0)
606 continue;
607 if (paramData.hints & PARAMETER_IS_NOT_SAVED)
608 continue;
609
610 const bool dummy = paramData.type != PARAMETER_INPUT || usingChunk;
611
612 if (dummy && paramData.mappedControlIndex <= CONTROL_INDEX_NONE)
613 continue;
614
615 CarlaStateSave::Parameter* const stateParameter(new CarlaStateSave::Parameter());
616
617 stateParameter->dummy = dummy;
618 stateParameter->index = paramData.index;
619 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
620 if (paramData.mappedControlIndex != CONTROL_INDEX_MIDI_LEARN)
621 {
622 stateParameter->mappedControlIndex = paramData.mappedControlIndex;
623 stateParameter->midiChannel = paramData.midiChannel;
624
625 if (paramData.hints & PARAMETER_MAPPED_RANGES_SET)
626 {
627 stateParameter->mappedMinimum = paramData.mappedMinimum;
628 stateParameter->mappedMaximum = paramData.mappedMaximum;
629 stateParameter->mappedRangeValid = true;
630
631 if (paramData.hints & PARAMETER_USES_SAMPLERATE)
632 {
633 stateParameter->mappedMinimum /= sampleRate;
634 stateParameter->mappedMaximum /= sampleRate;
635 }
636 }
637 }
638 #endif
639
640 if (! getParameterName(i, strBuf))
641 strBuf[0] = '\0';
642 stateParameter->name = carla_strdup(strBuf);
643
644 if (! getParameterSymbol(i, strBuf))
645 strBuf[0] = '\0';
646 stateParameter->symbol = carla_strdup(strBuf);;
647
648 if (! dummy)
649 {
650 stateParameter->value = getParameterValue(i);
651
652 if (paramData.hints & PARAMETER_USES_SAMPLERATE)
653 stateParameter->value /= sampleRate;
654 }
655
656 pData->stateSave.parameters.append(stateParameter);
657 }
658
659 // ---------------------------------------------------------------
660 // Custom Data
661
662 for (LinkedList<CustomData>::Itenerator it = pData->custom.begin2(); it.valid(); it.next())
663 {
664 const CustomData& cData(it.getValue(kCustomDataFallback));
665 CARLA_SAFE_ASSERT_CONTINUE(cData.isValid());
666
667 CarlaStateSave::CustomData* stateCustomData(new CarlaStateSave::CustomData());
668
669 stateCustomData->type = carla_strdup(cData.type);
670 stateCustomData->key = carla_strdup(cData.key);
671 stateCustomData->value = carla_strdup(cData.value);
672
673 pData->stateSave.customData.append(stateCustomData);
674 }
675
676 return pData->stateSave;
677 }
678
loadStateSave(const CarlaStateSave & stateSave)679 void CarlaPlugin::loadStateSave(const CarlaStateSave& stateSave)
680 {
681 const bool usesMultiProgs(pData->hints & PLUGIN_USES_MULTI_PROGS);
682 const PluginType pluginType(getType());
683
684 char strBuf[STR_MAX+1];
685 carla_zeroChars(strBuf, STR_MAX+1);
686
687 // ---------------------------------------------------------------
688 // Part 1 - PRE-set custom data (only those which reload programs)
689
690 for (CarlaStateSave::CustomDataItenerator it = stateSave.customData.begin2(); it.valid(); it.next())
691 {
692 const CarlaStateSave::CustomData* const stateCustomData(it.getValue(nullptr));
693 CARLA_SAFE_ASSERT_CONTINUE(stateCustomData != nullptr);
694 CARLA_SAFE_ASSERT_CONTINUE(stateCustomData->isValid());
695
696 const char* const key(stateCustomData->key);
697
698 /**/ if (pluginType == PLUGIN_DSSI && (std::strcmp (key, "reloadprograms") == 0 ||
699 std::strcmp (key, "load" ) == 0 ||
700 std::strncmp(key, "patches", 7) == 0 ))
701 pass();
702 else if (usesMultiProgs && std::strcmp(key, "midiPrograms") == 0)
703 pass();
704 else
705 continue;
706
707 setCustomData(stateCustomData->type, key, stateCustomData->value, true);
708 }
709
710 // ---------------------------------------------------------------
711 // Part 2 - set program
712
713 if (stateSave.currentProgramIndex >= 0 && stateSave.currentProgramName != nullptr)
714 {
715 int32_t programId = -1;
716
717 // index < count
718 if (stateSave.currentProgramIndex < static_cast<int32_t>(pData->prog.count))
719 {
720 programId = stateSave.currentProgramIndex;
721 }
722 // index not valid, try to find by name
723 else
724 {
725 for (uint32_t i=0; i < pData->prog.count; ++i)
726 {
727 if (getProgramName(i, strBuf) && std::strcmp(stateSave.currentProgramName, strBuf) == 0)
728 {
729 programId = static_cast<int32_t>(i);
730 break;
731 }
732 }
733 }
734
735 // set program now, if valid
736 if (programId >= 0)
737 setProgram(programId, true, true, true);
738 }
739
740 // ---------------------------------------------------------------
741 // Part 3 - set midi program
742
743 if (stateSave.currentMidiBank >= 0 && stateSave.currentMidiProgram >= 0 && ! usesMultiProgs)
744 setMidiProgramById(static_cast<uint32_t>(stateSave.currentMidiBank), static_cast<uint32_t>(stateSave.currentMidiProgram), true, true, true);
745
746 // ---------------------------------------------------------------
747 // Part 4a - get plugin parameter symbols
748
749 LinkedList<ParamSymbol*> paramSymbols;
750
751 if (pluginType == PLUGIN_LADSPA || pluginType == PLUGIN_LV2)
752 {
753 for (uint32_t i=0; i < pData->param.count; ++i)
754 {
755 if (pData->param.data[i].hints & PARAMETER_IS_NOT_SAVED)
756 continue;
757
758 if (getParameterSymbol(i, strBuf))
759 {
760 ParamSymbol* const paramSymbol(new ParamSymbol(i, strBuf));
761 paramSymbols.append(paramSymbol);
762 }
763 }
764 }
765
766 // ---------------------------------------------------------------
767 // Part 4b - set parameter values (carefully)
768
769 const float sampleRate(static_cast<float>(pData->engine->getSampleRate()));
770
771 for (CarlaStateSave::ParameterItenerator it = stateSave.parameters.begin2(); it.valid(); it.next())
772 {
773 CarlaStateSave::Parameter* const stateParameter(it.getValue(nullptr));
774 CARLA_SAFE_ASSERT_CONTINUE(stateParameter != nullptr);
775
776 int32_t index = -1;
777
778 if (pluginType == PLUGIN_LADSPA)
779 {
780 // Try to set by symbol, otherwise use index
781 if (stateParameter->symbol != nullptr && stateParameter->symbol[0] != '\0')
782 {
783 for (LinkedList<ParamSymbol*>::Itenerator it2 = paramSymbols.begin2(); it2.valid(); it2.next())
784 {
785 ParamSymbol* const paramSymbol(it2.getValue(nullptr));
786 CARLA_SAFE_ASSERT_CONTINUE(paramSymbol != nullptr);
787 CARLA_SAFE_ASSERT_CONTINUE(paramSymbol->symbol != nullptr);
788
789 if (std::strcmp(stateParameter->symbol, paramSymbol->symbol) == 0)
790 {
791 index = paramSymbol->index;
792 break;
793 }
794 }
795 if (index == -1)
796 index = stateParameter->index;
797 }
798 else
799 {
800 index = stateParameter->index;
801 }
802 }
803 else if (pluginType == PLUGIN_LV2)
804 {
805 // Symbol only
806 if (stateParameter->symbol != nullptr && stateParameter->symbol[0] != '\0')
807 {
808 for (LinkedList<ParamSymbol*>::Itenerator it2 = paramSymbols.begin2(); it2.valid(); it2.next())
809 {
810 ParamSymbol* const paramSymbol(it2.getValue(nullptr));
811 CARLA_SAFE_ASSERT_CONTINUE(paramSymbol != nullptr);
812 CARLA_SAFE_ASSERT_CONTINUE(paramSymbol->symbol != nullptr);
813
814 if (std::strcmp(stateParameter->symbol, paramSymbol->symbol) == 0)
815 {
816 index = paramSymbol->index;
817 break;
818 }
819 }
820 if (index == -1)
821 carla_stderr("Failed to find LV2 parameter symbol '%s' for '%s'",
822 stateParameter->symbol, pData->name);
823 }
824 else
825 {
826 carla_stderr("LV2 Plugin parameter '%s' has no symbol", stateParameter->name);
827 }
828 }
829 else
830 {
831 // Index only
832 index = stateParameter->index;
833 }
834
835 // Now set parameter
836 if (index >= 0 && index < static_cast<int32_t>(pData->param.count))
837 {
838 //CARLA_SAFE_ASSERT(stateParameter->isInput == (pData
839
840 if (! stateParameter->dummy)
841 {
842 if (pData->param.data[index].hints & PARAMETER_USES_SAMPLERATE)
843 stateParameter->value *= sampleRate;
844
845 setParameterValue(static_cast<uint32_t>(index), stateParameter->value, true, true, true);
846 }
847
848 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
849 if (stateParameter->mappedRangeValid)
850 {
851 if (pData->param.data[index].hints & PARAMETER_USES_SAMPLERATE)
852 {
853 stateParameter->mappedMinimum *= sampleRate;
854 stateParameter->mappedMaximum *= sampleRate;
855 }
856
857 setParameterMappedRange(static_cast<uint32_t>(index),
858 stateParameter->mappedMinimum,
859 stateParameter->mappedMaximum, true, true);
860 }
861
862 setParameterMappedControlIndex(static_cast<uint32_t>(index),
863 stateParameter->mappedControlIndex, true, true, false);
864 setParameterMidiChannel(static_cast<uint32_t>(index), stateParameter->midiChannel, true, true);
865 #endif
866 }
867 else
868 carla_stderr("Could not set parameter '%s' value for '%s'",
869 stateParameter->name, pData->name);
870 }
871
872 // ---------------------------------------------------------------
873 // Part 4c - clear
874
875 for (LinkedList<ParamSymbol*>::Itenerator it = paramSymbols.begin2(); it.valid(); it.next())
876 {
877 ParamSymbol* const paramSymbol(it.getValue(nullptr));
878 delete paramSymbol;
879 }
880
881 paramSymbols.clear();
882
883 // ---------------------------------------------------------------
884 // Part 5 - set custom data
885
886 for (CarlaStateSave::CustomDataItenerator it = stateSave.customData.begin2(); it.valid(); it.next())
887 {
888 const CarlaStateSave::CustomData* const stateCustomData(it.getValue(nullptr));
889 CARLA_SAFE_ASSERT_CONTINUE(stateCustomData != nullptr);
890 CARLA_SAFE_ASSERT_CONTINUE(stateCustomData->isValid());
891
892 const char* const key(stateCustomData->key);
893
894 if (pluginType == PLUGIN_DSSI && (std::strcmp (key, "reloadprograms") == 0 ||
895 std::strcmp (key, "load" ) == 0 ||
896 std::strncmp(key, "patches", 7) == 0 ))
897 continue;
898 if (usesMultiProgs && std::strcmp(key, "midiPrograms") == 0)
899 continue;
900
901 setCustomData(stateCustomData->type, key, stateCustomData->value, true);
902 }
903
904 // ---------------------------------------------------------------
905 // Part 5x - set lv2 state
906
907 if (pluginType == PLUGIN_LV2)
908 {
909 for (LinkedList<CustomData>::Itenerator it = pData->custom.begin2(); it.valid(); it.next())
910 {
911 const CustomData& customData(it.getValue(kCustomDataFallback));
912 CARLA_SAFE_ASSERT_CONTINUE(customData.isValid());
913
914 if (std::strcmp(customData.type, CUSTOM_DATA_TYPE_PROPERTY) == 0)
915 continue;
916
917 restoreLV2State(stateSave.temporary);
918 break;
919 }
920 }
921
922 // ---------------------------------------------------------------
923 // Part 6 - set chunk
924
925 if (stateSave.chunk != nullptr && (pData->options & PLUGIN_OPTION_USE_CHUNKS) != 0)
926 {
927 std::vector<uint8_t> chunk(carla_getChunkFromBase64String(stateSave.chunk));
928 #ifdef CARLA_PROPER_CPP11_SUPPORT
929 setChunkData(chunk.data(), chunk.size());
930 #else
931 setChunkData(&chunk.front(), chunk.size());
932 #endif
933 }
934
935 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
936 // ---------------------------------------------------------------
937 // Part 6 - set internal stuff
938
939 const uint availOptions(getOptionsAvailable());
940
941 for (uint i=0; i<10; ++i) // FIXME - get this value somehow...
942 {
943 const uint option(1u << i);
944
945 if (availOptions & option)
946 setOption(option, (stateSave.options & option) != 0, true);
947 }
948
949 setDryWet(stateSave.dryWet, true, true);
950 setVolume(stateSave.volume, true, true);
951 setBalanceLeft(stateSave.balanceLeft, true, true);
952 setBalanceRight(stateSave.balanceRight, true, true);
953 setPanning(stateSave.panning, true, true);
954 setCtrlChannel(stateSave.ctrlChannel, true, true);
955 setActive(stateSave.active, true, true);
956
957 if (! pData->engine->isLoadingProject())
958 pData->engine->callback(true, true, ENGINE_CALLBACK_UPDATE, pData->id, 0, 0, 0, 0.0f, nullptr);
959 #endif
960 }
961
saveStateToFile(const char * const filename)962 bool CarlaPlugin::saveStateToFile(const char* const filename)
963 {
964 CARLA_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', false);
965 carla_debug("CarlaPlugin::saveStateToFile(\"%s\")", filename);
966
967 MemoryOutputStream out, streamState;
968 getStateSave().dumpToMemoryStream(streamState);
969
970 out << "<?xml version='1.0' encoding='UTF-8'?>\n";
971 out << "<!DOCTYPE CARLA-PRESET>\n";
972 out << "<CARLA-PRESET VERSION='2.0'>\n";
973 out << streamState;
974 out << "</CARLA-PRESET>\n";
975
976 const String jfilename = String(CharPointer_UTF8(filename));
977 File file(jfilename);
978
979 if (file.replaceWithData(out.getData(), out.getDataSize()))
980 return true;
981
982 pData->engine->setLastError("Failed to write file");
983 return false;
984 }
985
loadStateFromFile(const char * const filename)986 bool CarlaPlugin::loadStateFromFile(const char* const filename)
987 {
988 // TODO set errors
989
990 CARLA_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0', false);
991 carla_debug("CarlaPlugin::loadStateFromFile(\"%s\")", filename);
992
993 const String jfilename = String(CharPointer_UTF8(filename));
994 File file(jfilename);
995 CARLA_SAFE_ASSERT_RETURN(file.existsAsFile(), false);
996
997 XmlDocument xml(file);
998 CarlaScopedPointer<XmlElement> xmlElement(xml.getDocumentElement(true));
999 CARLA_SAFE_ASSERT_RETURN(xmlElement != nullptr, false);
1000 CARLA_SAFE_ASSERT_RETURN(xmlElement->getTagName().equalsIgnoreCase("carla-preset"), false);
1001
1002 // completely load file
1003 xmlElement = xml.getDocumentElement(false);
1004 CARLA_SAFE_ASSERT_RETURN(xmlElement != nullptr, false);
1005
1006 if (pData->stateSave.fillFromXmlElement(xmlElement))
1007 {
1008 loadStateSave(pData->stateSave);
1009 return true;
1010 }
1011
1012 return false;
1013 }
1014
exportAsLV2(const char * const lv2path)1015 bool CarlaPlugin::exportAsLV2(const char* const lv2path)
1016 {
1017 CARLA_SAFE_ASSERT_RETURN(lv2path != nullptr && lv2path[0] != '\0', false);
1018 carla_debug("CarlaPlugin::exportAsLV2(\"%s\")", lv2path);
1019
1020 CarlaString bundlepath(lv2path);
1021
1022 if (! bundlepath.endsWith(".lv2"))
1023 bundlepath += ".lv2";
1024
1025 const File bundlefolder(bundlepath.buffer());
1026
1027 if (bundlefolder.existsAsFile())
1028 {
1029 pData->engine->setLastError("Requested filename already exists as file, use a folder instead");
1030 return false;
1031 }
1032
1033 if (! bundlefolder.exists())
1034 {
1035 const Result res(bundlefolder.createDirectory());
1036
1037 if (res.failed())
1038 {
1039 pData->engine->setLastError(res.getErrorMessage().toRawUTF8());
1040 return false;
1041 }
1042 }
1043
1044 CarlaString symbol(pData->name);
1045 symbol.toBasic();
1046
1047 {
1048 const CarlaString pluginFilename(bundlepath + CARLA_OS_SEP_STR + symbol + ".xml");
1049
1050 if (! saveStateToFile(pluginFilename))
1051 return false;
1052 }
1053
1054 {
1055 MemoryOutputStream manifestStream;
1056
1057 manifestStream << "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n";
1058 manifestStream << "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
1059 manifestStream << "@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .\n";
1060 manifestStream << "\n";
1061 manifestStream << "<" << symbol.buffer() << ".ttl>\n";
1062 manifestStream << " a lv2:Plugin ;\n";
1063 manifestStream << " lv2:binary <" << symbol.buffer() << CARLA_LIB_EXT "> ;\n";
1064 manifestStream << " rdfs:seeAlso <" << symbol.buffer() << ".ttl> .\n";
1065 manifestStream << "\n";
1066 manifestStream << "<ext-ui>\n";
1067 manifestStream << " a <http://kxstudio.sf.net/ns/lv2ext/external-ui#Widget> ;\n";
1068 manifestStream << " ui:binary <" << symbol.buffer() << CARLA_LIB_EXT "> ;\n";
1069 manifestStream << " lv2:extensionData <http://lv2plug.in/ns/extensions/ui#idleInterface> ,\n";
1070 manifestStream << " <http://lv2plug.in/ns/extensions/ui#showInterface> ;\n";
1071 manifestStream << " lv2:requiredFeature <http://lv2plug.in/ns/ext/instance-access> .\n";
1072 manifestStream << "\n";
1073
1074 const CarlaString manifestFilename(bundlepath + CARLA_OS_SEP_STR "manifest.ttl");
1075 const File manifestFile(manifestFilename.buffer());
1076
1077 if (! manifestFile.replaceWithData(manifestStream.getData(), manifestStream.getDataSize()))
1078 {
1079 pData->engine->setLastError("Failed to write manifest.ttl file");
1080 return false;
1081 }
1082 }
1083
1084 {
1085 MemoryOutputStream mainStream;
1086
1087 mainStream << "@prefix atom: <http://lv2plug.in/ns/ext/atom#> .\n";
1088 mainStream << "@prefix doap: <http://usefulinc.com/ns/doap#> .\n";
1089 mainStream << "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n";
1090 mainStream << "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n";
1091 mainStream << "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
1092 mainStream << "@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .\n";
1093 mainStream << "\n";
1094 mainStream << "<>\n";
1095 mainStream << " a lv2:Plugin ;\n";
1096 mainStream << "\n";
1097 mainStream << " lv2:requiredFeature <http://lv2plug.in/ns/ext/buf-size#boundedBlockLength> ,\n";
1098 mainStream << " <http://lv2plug.in/ns/ext/options#options> ,\n";
1099 mainStream << " <http://lv2plug.in/ns/ext/urid#map> ;\n";
1100 mainStream << "\n";
1101
1102 if (pData->hints & PLUGIN_HAS_CUSTOM_UI)
1103 {
1104 mainStream << " ui:ui <ext-ui> ;\n";
1105 mainStream << "\n";
1106 }
1107
1108 const uint32_t midiIns = getMidiInCount();
1109 const uint32_t midiOuts = getMidiOutCount();
1110
1111 int portIndex = 0;
1112
1113 if (midiIns > 0)
1114 {
1115 mainStream << " lv2:port [\n";
1116 mainStream << " a lv2:InputPort, atom:AtomPort ;\n";
1117 mainStream << " lv2:index 0 ;\n";
1118 mainStream << " lv2:symbol \"clv2_events_in\" ;\n";
1119 mainStream << " lv2:name \"Events Input\" ;\n";
1120 mainStream << " atom:bufferType atom:Sequence ;\n";
1121 mainStream << " atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> ,\n";
1122 mainStream << " <http://lv2plug.in/ns/ext/time#Position> ;\n";
1123 mainStream << " ] ;\n";
1124 ++portIndex;
1125
1126 for (uint32_t i=1; i<midiIns; ++i)
1127 {
1128 const String portIndexNum(portIndex++);
1129 const String portIndexLabel(portIndex);
1130
1131 mainStream << " lv2:port [\n";
1132 mainStream << " a lv2:InputPort, atom:AtomPort ;\n";
1133 mainStream << " lv2:index " << portIndexNum << " ;\n";
1134 mainStream << " lv2:symbol \"clv2_midi_in_" << portIndexLabel << "\" ;\n";
1135 mainStream << " lv2:name \"MIDI Input " << portIndexLabel << "\" ;\n";
1136 mainStream << " ] ;\n";
1137 }
1138 }
1139 else
1140 {
1141 mainStream << " lv2:port [\n";
1142 mainStream << " a lv2:InputPort, atom:AtomPort ;\n";
1143 mainStream << " lv2:index 0 ;\n";
1144 mainStream << " lv2:symbol \"clv2_time_info\" ;\n";
1145 mainStream << " lv2:name \"Time Info\" ;\n";
1146 mainStream << " atom:bufferType atom:Sequence ;\n";
1147 mainStream << " atom:supports <http://lv2plug.in/ns/ext/time#Position> ;\n";
1148 mainStream << " ] ;\n";
1149 ++portIndex;
1150 }
1151
1152 for (uint32_t i=0; i<midiOuts; ++i)
1153 {
1154 const String portIndexNum(portIndex++);
1155 const String portIndexLabel(portIndex);
1156
1157 mainStream << " lv2:port [\n";
1158 mainStream << " a lv2:InputPort, atom:AtomPort ;\n";
1159 mainStream << " lv2:index " << portIndexNum << " ;\n";
1160 mainStream << " lv2:symbol \"clv2_midi_out_" << portIndexLabel << "\" ;\n";
1161 mainStream << " lv2:name \"MIDI Output " << portIndexLabel << "\" ;\n";
1162 mainStream << " atom:bufferType atom:Sequence ;\n";
1163 mainStream << " atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> ;\n";
1164 mainStream << " ] ;\n";
1165 }
1166
1167 mainStream << " lv2:port [\n";
1168 mainStream << " a lv2:InputPort, lv2:ControlPort ;\n";
1169 mainStream << " lv2:index " << String(portIndex++) << " ;\n";
1170 mainStream << " lv2:name \"freewheel\" ;\n";
1171 mainStream << " lv2:symbol \"clv2_freewheel\" ;\n";
1172 mainStream << " lv2:default 0 ;\n";
1173 mainStream << " lv2:minimum 0 ;\n";
1174 mainStream << " lv2:maximum 1 ;\n";
1175 mainStream << " lv2:designation lv2:freeWheeling ;\n";
1176 mainStream << " lv2:portProperty lv2:toggled , lv2:integer ;\n";
1177 mainStream << " lv2:portProperty <http://lv2plug.in/ns/ext/port-props#notOnGUI> ;\n";
1178 mainStream << " ] ;\n";
1179
1180 for (uint32_t i=0; i<pData->audioIn.count; ++i)
1181 {
1182 const String portIndexNum(portIndex++);
1183 const String portIndexLabel(i+1);
1184
1185 mainStream << " lv2:port [\n";
1186 mainStream << " a lv2:InputPort, lv2:AudioPort ;\n";
1187 mainStream << " lv2:index " << portIndexNum << " ;\n";
1188 mainStream << " lv2:symbol \"clv2_audio_in_" << portIndexLabel << "\" ;\n";
1189 mainStream << " lv2:name \"Audio Input " << portIndexLabel << "\" ;\n";
1190 mainStream << " ] ;\n";
1191 }
1192
1193 for (uint32_t i=0; i<pData->audioOut.count; ++i)
1194 {
1195 const String portIndexNum(portIndex++);
1196 const String portIndexLabel(i+1);
1197
1198 mainStream << " lv2:port [\n";
1199 mainStream << " a lv2:OutputPort, lv2:AudioPort ;\n";
1200 mainStream << " lv2:index " << portIndexNum << " ;\n";
1201 mainStream << " lv2:symbol \"clv2_audio_out_" << portIndexLabel << "\" ;\n";
1202 mainStream << " lv2:name \"Audio Output " << portIndexLabel << "\" ;\n";
1203 mainStream << " ] ;\n";
1204 }
1205
1206 CarlaStringList uniqueSymbolNames;
1207
1208 char strBufName[STR_MAX+1];
1209 char strBufSymbol[STR_MAX+1];
1210 carla_zeroChars(strBufName, STR_MAX+1);
1211 carla_zeroChars(strBufSymbol, STR_MAX+1);
1212
1213 for (uint32_t i=0; i<pData->param.count; ++i)
1214 {
1215 const ParameterData& paramData(pData->param.data[i]);
1216 const ParameterRanges& paramRanges(pData->param.ranges[i]);
1217
1218 const String portIndexNum(portIndex++);
1219
1220 mainStream << " lv2:port [\n";
1221
1222 if (paramData.type == PARAMETER_INPUT)
1223 mainStream << " a lv2:InputPort, lv2:ControlPort ;\n";
1224 else
1225 mainStream << " a lv2:OutputPort, lv2:ControlPort ;\n";
1226
1227 if (paramData.hints & PARAMETER_IS_BOOLEAN)
1228 mainStream << " lv2:portProperty lv2:toggled ;\n";
1229
1230 if (paramData.hints & PARAMETER_IS_INTEGER)
1231 mainStream << " lv2:portProperty lv2:integer ;\n";
1232
1233 // TODO logarithmic, enabled (not on gui), automable, samplerate, scalepoints
1234
1235 if (! getParameterName(i, strBufName))
1236 strBufName[0] = '\0';
1237 if (! getParameterSymbol(i, strBufSymbol))
1238 strBufSymbol[0] = '\0';
1239
1240 if (strBufSymbol[0] == '\0')
1241 {
1242 CarlaString s(strBufName);
1243 s.toBasic();
1244 std::memcpy(strBufSymbol, s.buffer(), s.length()+1);
1245
1246 if (strBufSymbol[0] >= '0' && strBufSymbol[0] <= '9')
1247 {
1248 const size_t len(std::strlen(strBufSymbol));
1249 std::memmove(strBufSymbol+1, strBufSymbol, len);
1250 strBufSymbol[0] = '_';
1251 strBufSymbol[len+1] = '\0';
1252 }
1253 }
1254
1255 if (uniqueSymbolNames.contains(strBufSymbol))
1256 std::snprintf(strBufSymbol, STR_MAX, "clv2_param_%d", i+1);
1257
1258 mainStream << " lv2:index " << portIndexNum << " ;\n";
1259 mainStream << " lv2:symbol \"" << strBufSymbol << "\" ;\n";
1260 mainStream << " lv2:name \"\"\"" << strBufName << "\"\"\" ;\n";
1261 mainStream << " lv2:default " << String(paramRanges.def) << " ;\n";
1262 mainStream << " lv2:minimum " << String(paramRanges.min) << " ;\n";
1263 mainStream << " lv2:maximum " << String(paramRanges.max) << " ;\n";
1264
1265 // TODO midiCC, midiChannel
1266
1267 mainStream << " ] ;\n";
1268 }
1269
1270 char strBuf[STR_MAX+1];
1271 carla_zeroChars(strBuf, STR_MAX+1);
1272
1273 if (! getMaker(strBuf))
1274 strBuf[0] = '\0';
1275
1276 mainStream << " rdfs:comment \"Plugin generated using Carla LV2 export.\" ;\n";
1277 mainStream << " doap:name \"\"\"" << getName() << "\"\"\" ;\n";
1278 mainStream << " doap:maintainer [ foaf:name \"\"\"" << strBuf << "\"\"\" ] .\n";
1279 mainStream << "\n";
1280
1281 const CarlaString mainFilename(bundlepath + CARLA_OS_SEP_STR + symbol + ".ttl");
1282 const File mainFile(mainFilename.buffer());
1283
1284 if (! mainFile.replaceWithData(mainStream.getData(), mainStream.getDataSize()))
1285 {
1286 pData->engine->setLastError("Failed to write main plugin ttl file");
1287 return false;
1288 }
1289 }
1290
1291 const CarlaString binaryFilename(bundlepath + CARLA_OS_SEP_STR + symbol + CARLA_LIB_EXT);
1292
1293 const File binaryFileSource(File::getSpecialLocation(File::currentExecutableFile).getSiblingFile("carla-bridge-lv2" CARLA_LIB_EXT));
1294 const File binaryFileTarget(binaryFilename.buffer());
1295
1296 const EngineOptions& opts(pData->engine->getOptions());
1297
1298 const CarlaString binFolderTarget(bundlepath + CARLA_OS_SEP_STR + "bin");
1299 const CarlaString resFolderTarget(bundlepath + CARLA_OS_SEP_STR + "res");
1300
1301 if (! binaryFileSource.copyFileTo(binaryFileTarget))
1302 {
1303 pData->engine->setLastError("Failed to copy plugin binary");
1304 return false;
1305 }
1306
1307 #ifdef CARLA_OS_WIN
1308 File(opts.resourceDir).copyDirectoryTo(File(resFolderTarget.buffer()));
1309
1310 // Copying all the binaries is pointless, just go through the expected needed bits
1311 const File binFolder1(opts.binaryDir);
1312 const File binFolder2(binFolderTarget.buffer());
1313 binFolder2.createDirectory();
1314
1315 static const char* files[] = {
1316 "carla-bridge-native.exe",
1317 "carla-bridge-win32.exe",
1318 "carla-discovery-win32.exe",
1319 "carla-discovery-win64.exe",
1320 "libcarla_utils.dll"
1321 };
1322
1323 for (int i=0; i<5; ++i)
1324 binFolder1.getChildFile(files[i]).copyFileTo(binFolder2.getChildFile(files[i]));;
1325 #else
1326 File(opts.binaryDir).createSymbolicLink(File(binFolderTarget.buffer()), true);
1327 File(opts.resourceDir).createSymbolicLink(File(resFolderTarget.buffer()), true);
1328 #endif
1329
1330 return true;
1331 }
1332
1333 // -------------------------------------------------------------------
1334 // Set data (internal stuff)
1335
setId(const uint newId)1336 void CarlaPlugin::setId(const uint newId) noexcept
1337 {
1338 pData->id = newId;
1339 }
1340
setName(const char * const newName)1341 void CarlaPlugin::setName(const char* const newName)
1342 {
1343 CARLA_SAFE_ASSERT_RETURN(newName != nullptr && newName[0] != '\0',);
1344
1345 if (pData->name != nullptr)
1346 delete[] pData->name;
1347
1348 pData->name = carla_strdup(newName);
1349 }
1350
setOption(const uint option,const bool yesNo,const bool sendCallback)1351 void CarlaPlugin::setOption(const uint option, const bool yesNo, const bool sendCallback)
1352 {
1353 CARLA_SAFE_ASSERT_UINT2_RETURN(getOptionsAvailable() & option, getOptionsAvailable(), option,);
1354
1355 if (yesNo)
1356 pData->options |= option;
1357 else
1358 pData->options &= ~option;
1359
1360 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1361 if (sendCallback)
1362 pData->engine->callback(true, true,
1363 ENGINE_CALLBACK_OPTION_CHANGED,
1364 pData->id,
1365 static_cast<int>(option),
1366 yesNo ? 1 : 0,
1367 0, 0.0f, nullptr);
1368 #else
1369 // unused
1370 return; (void)sendCallback;
1371 #endif
1372 }
1373
setEnabled(const bool yesNo)1374 void CarlaPlugin::setEnabled(const bool yesNo) noexcept
1375 {
1376 if (pData->enabled == yesNo)
1377 return;
1378
1379 pData->masterMutex.lock();
1380 pData->enabled = yesNo;
1381
1382 if (yesNo && ! pData->client->isActive())
1383 pData->client->activate();
1384
1385 pData->masterMutex.unlock();
1386 }
1387
setActive(const bool active,const bool sendOsc,const bool sendCallback)1388 void CarlaPlugin::setActive(const bool active, const bool sendOsc, const bool sendCallback) noexcept
1389 {
1390 if (pData->engineBridged) {
1391 CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
1392 } else if (pData->enginePlugin) {
1393 // nothing here
1394 } else {
1395 CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
1396 }
1397
1398 if (pData->active == active)
1399 return;
1400
1401 {
1402 const ScopedSingleProcessLocker spl(this, true);
1403
1404 if (active)
1405 activate();
1406 else
1407 deactivate();
1408 }
1409
1410 pData->active = active;
1411
1412 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1413 const float value = active ? 1.0f : 0.0f;
1414
1415 pData->engine->callback(sendCallback, sendOsc,
1416 ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED,
1417 pData->id,
1418 PARAMETER_ACTIVE,
1419 0, 0,
1420 value,
1421 nullptr);
1422 #endif
1423 }
1424
1425 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
setDryWet(const float value,const bool sendOsc,const bool sendCallback)1426 void CarlaPlugin::setDryWet(const float value, const bool sendOsc, const bool sendCallback) noexcept
1427 {
1428 if (pData->engineBridged) {
1429 CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
1430 } else {
1431 CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
1432 }
1433 CARLA_SAFE_ASSERT(value >= 0.0f && value <= 1.0f);
1434
1435 const float fixedValue(carla_fixedValue<float>(0.0f, 1.0f, value));
1436
1437 if (carla_isEqual(pData->postProc.dryWet, fixedValue))
1438 return;
1439
1440 pData->postProc.dryWet = fixedValue;
1441
1442 pData->engine->callback(sendCallback, sendOsc,
1443 ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED,
1444 pData->id,
1445 PARAMETER_DRYWET,
1446 0, 0,
1447 fixedValue,
1448 nullptr);
1449 }
1450
setVolume(const float value,const bool sendOsc,const bool sendCallback)1451 void CarlaPlugin::setVolume(const float value, const bool sendOsc, const bool sendCallback) noexcept
1452 {
1453 if (pData->engineBridged) {
1454 CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
1455 } else {
1456 CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
1457 }
1458 CARLA_SAFE_ASSERT(value >= 0.0f && value <= 1.27f);
1459
1460 const float fixedValue(carla_fixedValue<float>(0.0f, 1.27f, value));
1461
1462 if (carla_isEqual(pData->postProc.volume, fixedValue))
1463 return;
1464
1465 pData->postProc.volume = fixedValue;
1466
1467 pData->engine->callback(sendCallback, sendOsc,
1468 ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED,
1469 pData->id,
1470 PARAMETER_VOLUME,
1471 0, 0,
1472 fixedValue,
1473 nullptr);
1474 }
1475
setBalanceLeft(const float value,const bool sendOsc,const bool sendCallback)1476 void CarlaPlugin::setBalanceLeft(const float value, const bool sendOsc, const bool sendCallback) noexcept
1477 {
1478 if (pData->engineBridged) {
1479 CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
1480 } else {
1481 CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
1482 }
1483 CARLA_SAFE_ASSERT(value >= -1.0f && value <= 1.0f);
1484
1485 const float fixedValue(carla_fixedValue<float>(-1.0f, 1.0f, value));
1486
1487 if (carla_isEqual(pData->postProc.balanceLeft, fixedValue))
1488 return;
1489
1490 pData->postProc.balanceLeft = fixedValue;
1491
1492 pData->engine->callback(sendCallback, sendOsc,
1493 ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED,
1494 pData->id,
1495 PARAMETER_BALANCE_LEFT,
1496 0, 0,
1497 fixedValue,
1498 nullptr);
1499 }
1500
setBalanceRight(const float value,const bool sendOsc,const bool sendCallback)1501 void CarlaPlugin::setBalanceRight(const float value, const bool sendOsc, const bool sendCallback) noexcept
1502 {
1503 if (pData->engineBridged) {
1504 CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
1505 } else {
1506 CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
1507 }
1508 CARLA_SAFE_ASSERT(value >= -1.0f && value <= 1.0f);
1509
1510 const float fixedValue(carla_fixedValue<float>(-1.0f, 1.0f, value));
1511
1512 if (carla_isEqual(pData->postProc.balanceRight, fixedValue))
1513 return;
1514
1515 pData->postProc.balanceRight = fixedValue;
1516
1517 pData->engine->callback(sendCallback, sendOsc,
1518 ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED,
1519 pData->id,
1520 PARAMETER_BALANCE_RIGHT,
1521 0, 0,
1522 fixedValue,
1523 nullptr);
1524 }
1525
setPanning(const float value,const bool sendOsc,const bool sendCallback)1526 void CarlaPlugin::setPanning(const float value, const bool sendOsc, const bool sendCallback) noexcept
1527 {
1528 if (pData->engineBridged) {
1529 CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
1530 } else {
1531 CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
1532 }
1533 CARLA_SAFE_ASSERT(value >= -1.0f && value <= 1.0f);
1534
1535 const float fixedValue(carla_fixedValue<float>(-1.0f, 1.0f, value));
1536
1537 if (carla_isEqual(pData->postProc.panning, fixedValue))
1538 return;
1539
1540 pData->postProc.panning = fixedValue;
1541
1542 pData->engine->callback(sendCallback, sendOsc,
1543 ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED,
1544 pData->id,
1545 PARAMETER_PANNING,
1546 0, 0,
1547 fixedValue,
1548 nullptr);
1549 }
1550
setDryWetRT(const float value,const bool sendCallbackLater)1551 void CarlaPlugin::setDryWetRT(const float value, const bool sendCallbackLater) noexcept
1552 {
1553 CARLA_SAFE_ASSERT(value >= 0.0f && value <= 1.0f);
1554
1555 const float fixedValue(carla_fixedValue<float>(0.0f, 1.0f, value));
1556
1557 if (carla_isEqual(pData->postProc.dryWet, fixedValue))
1558 return;
1559
1560 pData->postProc.dryWet = fixedValue;
1561 pData->postponeParameterChangeRtEvent(sendCallbackLater, PARAMETER_DRYWET, fixedValue);
1562 }
1563
setVolumeRT(const float value,const bool sendCallbackLater)1564 void CarlaPlugin::setVolumeRT(const float value, const bool sendCallbackLater) noexcept
1565 {
1566 CARLA_SAFE_ASSERT(value >= 0.0f && value <= 1.27f);
1567
1568 const float fixedValue(carla_fixedValue<float>(0.0f, 1.27f, value));
1569
1570 if (carla_isEqual(pData->postProc.volume, fixedValue))
1571 return;
1572
1573 pData->postProc.volume = fixedValue;
1574 pData->postponeParameterChangeRtEvent(sendCallbackLater, PARAMETER_VOLUME, fixedValue);
1575 }
1576
setBalanceLeftRT(const float value,const bool sendCallbackLater)1577 void CarlaPlugin::setBalanceLeftRT(const float value, const bool sendCallbackLater) noexcept
1578 {
1579 CARLA_SAFE_ASSERT(value >= -1.0f && value <= 1.0f);
1580
1581 const float fixedValue(carla_fixedValue<float>(-1.0f, 1.0f, value));
1582
1583 if (carla_isEqual(pData->postProc.balanceLeft, fixedValue))
1584 return;
1585
1586 pData->postProc.balanceLeft = fixedValue;
1587 pData->postponeParameterChangeRtEvent(sendCallbackLater, PARAMETER_BALANCE_LEFT, fixedValue);
1588 }
1589
setBalanceRightRT(const float value,const bool sendCallbackLater)1590 void CarlaPlugin::setBalanceRightRT(const float value, const bool sendCallbackLater) noexcept
1591 {
1592 CARLA_SAFE_ASSERT(value >= -1.0f && value <= 1.0f);
1593
1594 const float fixedValue(carla_fixedValue<float>(-1.0f, 1.0f, value));
1595
1596 if (carla_isEqual(pData->postProc.balanceRight, fixedValue))
1597 return;
1598
1599 pData->postProc.balanceRight = fixedValue;
1600 pData->postponeParameterChangeRtEvent(sendCallbackLater, PARAMETER_BALANCE_RIGHT, fixedValue);
1601 }
1602
setPanningRT(const float value,const bool sendCallbackLater)1603 void CarlaPlugin::setPanningRT(const float value, const bool sendCallbackLater) noexcept
1604 {
1605 CARLA_SAFE_ASSERT(value >= -1.0f && value <= 1.0f);
1606
1607 const float fixedValue(carla_fixedValue<float>(-1.0f, 1.0f, value));
1608
1609 if (carla_isEqual(pData->postProc.panning, fixedValue))
1610 return;
1611
1612 pData->postProc.panning = fixedValue;
1613 pData->postponeParameterChangeRtEvent(sendCallbackLater, PARAMETER_PANNING, fixedValue);
1614 }
1615 #endif // ! BUILD_BRIDGE_ALTERNATIVE_ARCH
1616
setCtrlChannel(const int8_t channel,const bool sendOsc,const bool sendCallback)1617 void CarlaPlugin::setCtrlChannel(const int8_t channel, const bool sendOsc, const bool sendCallback) noexcept
1618 {
1619 if (pData->engineBridged) {
1620 CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
1621 } else {
1622 CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
1623 }
1624 CARLA_SAFE_ASSERT_RETURN(channel >= -1 && channel < MAX_MIDI_CHANNELS,);
1625
1626 if (pData->ctrlChannel == channel)
1627 return;
1628
1629 pData->ctrlChannel = channel;
1630
1631 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1632 const float channelf = static_cast<float>(channel);
1633
1634 pData->engine->callback(sendCallback, sendOsc,
1635 ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED,
1636 pData->id,
1637 PARAMETER_CTRL_CHANNEL,
1638 0, 0,
1639 channelf, nullptr);
1640 #endif
1641 }
1642
1643 // -------------------------------------------------------------------
1644 // Set data (plugin-specific stuff)
1645
setParameterValue(const uint32_t parameterId,const float value,const bool sendGui,const bool sendOsc,const bool sendCallback)1646 void CarlaPlugin::setParameterValue(const uint32_t parameterId, const float value, const bool sendGui, const bool sendOsc, const bool sendCallback) noexcept
1647 {
1648 if (pData->engineBridged) {
1649 // NOTE: some LV2 plugins feedback messages to UI on purpose
1650 CARLA_SAFE_ASSERT_RETURN(getType() == PLUGIN_LV2 || !sendGui,);
1651 } else if (pData->enginePlugin) {
1652 // nothing here
1653 } else {
1654 CARLA_SAFE_ASSERT_RETURN(sendGui || sendOsc || sendCallback,); // never call this from RT
1655 }
1656 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);
1657
1658 if (sendGui && (pData->hints & PLUGIN_HAS_CUSTOM_UI) != 0)
1659 uiParameterChange(parameterId, value);
1660
1661 pData->engine->callback(sendCallback, sendOsc,
1662 ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED,
1663 pData->id,
1664 static_cast<int>(parameterId),
1665 0, 0,
1666 value,
1667 nullptr);
1668 }
1669
setParameterValueRT(const uint32_t parameterId,const float value,const bool sendCallbackLater)1670 void CarlaPlugin::setParameterValueRT(const uint32_t parameterId, const float value, const bool sendCallbackLater) noexcept
1671 {
1672 pData->postponeParameterChangeRtEvent(sendCallbackLater, static_cast<int32_t>(parameterId), value);
1673 }
1674
setParameterValueByRealIndex(const int32_t rindex,const float value,const bool sendGui,const bool sendOsc,const bool sendCallback)1675 void CarlaPlugin::setParameterValueByRealIndex(const int32_t rindex, const float value, const bool sendGui, const bool sendOsc, const bool sendCallback) noexcept
1676 {
1677 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1678 CARLA_SAFE_ASSERT_RETURN(rindex > PARAMETER_MAX && rindex != PARAMETER_NULL,);
1679
1680 switch (rindex)
1681 {
1682 case PARAMETER_ACTIVE:
1683 return setActive((value > 0.0f), sendOsc, sendCallback);
1684 case PARAMETER_CTRL_CHANNEL:
1685 return setCtrlChannel(int8_t(value), sendOsc, sendCallback);
1686 case PARAMETER_DRYWET:
1687 return setDryWet(value, sendOsc, sendCallback);
1688 case PARAMETER_VOLUME:
1689 return setVolume(value, sendOsc, sendCallback);
1690 case PARAMETER_BALANCE_LEFT:
1691 return setBalanceLeft(value, sendOsc, sendCallback);
1692 case PARAMETER_BALANCE_RIGHT:
1693 return setBalanceRight(value, sendOsc, sendCallback);
1694 case PARAMETER_PANNING:
1695 return setPanning(value, sendOsc, sendCallback);
1696 }
1697 #endif
1698 CARLA_SAFE_ASSERT_RETURN(rindex >= 0,);
1699
1700 for (uint32_t i=0; i < pData->param.count; ++i)
1701 {
1702 if (pData->param.data[i].rindex == rindex)
1703 {
1704 //if (carla_isNotEqual(getParameterValue(i), value))
1705 setParameterValue(i, value, sendGui, sendOsc, sendCallback);
1706 break;
1707 }
1708 }
1709 }
1710
setParameterMidiChannel(const uint32_t parameterId,const uint8_t channel,const bool sendOsc,const bool sendCallback)1711 void CarlaPlugin::setParameterMidiChannel(const uint32_t parameterId, const uint8_t channel, const bool sendOsc, const bool sendCallback) noexcept
1712 {
1713 if (pData->engineBridged) {
1714 CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
1715 } else {
1716 CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
1717 }
1718 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);
1719 CARLA_SAFE_ASSERT_RETURN(channel < MAX_MIDI_CHANNELS,);
1720
1721 if (pData->param.data[parameterId].midiChannel == channel)
1722 return;
1723
1724 pData->param.data[parameterId].midiChannel = channel;
1725
1726 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1727 pData->engine->callback(sendCallback, sendOsc,
1728 ENGINE_CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED,
1729 pData->id,
1730 static_cast<int>(parameterId),
1731 channel,
1732 0, 0.0f, nullptr);
1733 #endif
1734 }
1735
setParameterMappedControlIndex(const uint32_t parameterId,const int16_t index,const bool sendOsc,const bool sendCallback,const bool reconfigureNow)1736 void CarlaPlugin::setParameterMappedControlIndex(const uint32_t parameterId, const int16_t index,
1737 const bool sendOsc, const bool sendCallback,
1738 const bool reconfigureNow) noexcept
1739 {
1740 if (pData->engineBridged) {
1741 CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
1742 } else {
1743 CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
1744 }
1745 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);
1746 CARLA_SAFE_ASSERT_RETURN(index >= CONTROL_INDEX_NONE && index <= CONTROL_INDEX_MAX_ALLOWED,);
1747
1748 ParameterData& paramData(pData->param.data[parameterId]);
1749
1750 if (paramData.mappedControlIndex == index)
1751 return;
1752
1753 const ParameterRanges& paramRanges(pData->param.ranges[parameterId]);
1754
1755 if ((paramData.hints & PARAMETER_MAPPED_RANGES_SET) == 0x0)
1756 setParameterMappedRange(parameterId, paramRanges.min, paramRanges.max, true, true);
1757
1758 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1759 char strBuf[STR_MAX+1];
1760 carla_zeroChars(strBuf, STR_MAX+1);
1761 if (! getParameterName(parameterId, strBuf))
1762 std::snprintf(strBuf, STR_MAX, "Param %u", parameterId);
1763
1764 const uint portNameSize = pData->engine->getMaxPortNameSize();
1765 if (portNameSize < STR_MAX)
1766 strBuf[portNameSize] = '\0';
1767
1768 // was learning something else before, stop that first
1769 if (pData->midiLearnParameterIndex >= 0 && pData->midiLearnParameterIndex != static_cast<int32_t>(parameterId))
1770 {
1771 const int32_t oldParameterId = pData->midiLearnParameterIndex;
1772 pData->midiLearnParameterIndex = -1;
1773
1774 CARLA_SAFE_ASSERT_RETURN(oldParameterId < static_cast<int32_t>(pData->param.count),);
1775
1776 pData->param.data[oldParameterId].mappedControlIndex = CONTROL_INDEX_NONE;
1777 pData->engine->callback(true, true,
1778 ENGINE_CALLBACK_PARAMETER_MAPPED_CONTROL_INDEX_CHANGED,
1779 pData->id,
1780 oldParameterId,
1781 CONTROL_INDEX_NONE,
1782 0, 0.0f, nullptr);
1783 }
1784
1785 // mapping new parameter to CV
1786 if (index == CONTROL_INDEX_CV)
1787 {
1788 CARLA_SAFE_ASSERT_RETURN(pData->event.cvSourcePorts != nullptr,);
1789 CARLA_SAFE_ASSERT_RETURN(paramData.type == PARAMETER_INPUT,);
1790 CARLA_SAFE_ASSERT_RETURN(paramData.hints & PARAMETER_CAN_BE_CV_CONTROLLED,);
1791
1792 CarlaEngineCVPort* const cvPort =
1793 (CarlaEngineCVPort*)pData->client->addPort(kEnginePortTypeCV, strBuf, true, parameterId);
1794 cvPort->setRange(paramData.mappedMinimum, paramData.mappedMaximum);
1795 pData->event.cvSourcePorts->addCVSource(cvPort, parameterId, reconfigureNow);
1796 }
1797 // unmapping from CV
1798 else if (paramData.mappedControlIndex == CONTROL_INDEX_CV)
1799 {
1800 CARLA_SAFE_ASSERT_RETURN(pData->event.cvSourcePorts != nullptr,);
1801
1802 CARLA_SAFE_ASSERT(pData->client->removePort(kEnginePortTypeCV, strBuf, true));
1803 CARLA_SAFE_ASSERT(pData->event.cvSourcePorts->removeCVSource(parameterId));
1804 }
1805 // mapping to something new
1806 else if (paramData.mappedControlIndex == CONTROL_INDEX_NONE)
1807 {
1808 // when doing MIDI CC mapping, ensure ranges are within bounds
1809 if (paramData.mappedMinimum < paramRanges.min || paramData.mappedMaximum > paramRanges.max)
1810 setParameterMappedRange(parameterId,
1811 std::max(paramData.mappedMinimum, paramRanges.min),
1812 std::min(paramData.mappedMaximum, paramRanges.max),
1813 true, true);
1814 }
1815 #endif
1816
1817 paramData.mappedControlIndex = index;
1818
1819 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1820 if (index == CONTROL_INDEX_MIDI_LEARN)
1821 pData->midiLearnParameterIndex = static_cast<int32_t>(parameterId);
1822 else
1823 pData->midiLearnParameterIndex = -1;
1824
1825 pData->engine->callback(sendCallback, sendOsc,
1826 ENGINE_CALLBACK_PARAMETER_MAPPED_CONTROL_INDEX_CHANGED,
1827 pData->id,
1828 static_cast<int>(parameterId),
1829 index,
1830 0, 0.0f, nullptr);
1831 #else
1832 return;
1833 // unused
1834 (void)reconfigureNow;
1835 #endif
1836 }
1837
setParameterMappedRange(const uint32_t parameterId,const float minimum,const float maximum,const bool sendOsc,const bool sendCallback)1838 void CarlaPlugin::setParameterMappedRange(const uint32_t parameterId, const float minimum, const float maximum,
1839 const bool sendOsc, const bool sendCallback) noexcept
1840 {
1841 if (pData->engineBridged) {
1842 CARLA_SAFE_ASSERT_RETURN(!sendOsc && !sendCallback,);
1843 } else {
1844 CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
1845 }
1846 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);
1847
1848 ParameterData& paramData(pData->param.data[parameterId]);
1849
1850 if (carla_isEqual(paramData.mappedMinimum, minimum) &&
1851 carla_isEqual(paramData.mappedMaximum, maximum) &&
1852 (paramData.hints & PARAMETER_MAPPED_RANGES_SET) != 0x0)
1853 return;
1854
1855 if (paramData.mappedControlIndex != CONTROL_INDEX_NONE && paramData.mappedControlIndex != CONTROL_INDEX_CV)
1856 {
1857 const ParameterRanges& paramRanges(pData->param.ranges[parameterId]);
1858 CARLA_SAFE_ASSERT_RETURN(minimum >= paramRanges.min,);
1859 CARLA_SAFE_ASSERT_RETURN(maximum <= paramRanges.max,);
1860 }
1861
1862 paramData.hints |= PARAMETER_MAPPED_RANGES_SET;
1863 paramData.mappedMinimum = minimum;
1864 paramData.mappedMaximum = maximum;
1865
1866 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1867 if (pData->event.cvSourcePorts != nullptr && paramData.mappedControlIndex == CONTROL_INDEX_CV)
1868 pData->event.cvSourcePorts->setCVSourceRange(parameterId, minimum, maximum);
1869
1870 char strBuf[STR_MAX+1];
1871 carla_zeroChars(strBuf, STR_MAX+1);
1872 std::snprintf(strBuf, STR_MAX, "%.12g:%.12g", static_cast<double>(minimum), static_cast<double>(maximum));
1873
1874 pData->engine->callback(sendCallback, sendOsc,
1875 ENGINE_CALLBACK_PARAMETER_MAPPED_RANGE_CHANGED,
1876 pData->id,
1877 static_cast<int>(parameterId),
1878 0, 0, 0.0f,
1879 strBuf);
1880 #endif
1881 }
1882
setCustomData(const char * const type,const char * const key,const char * const value,const bool)1883 void CarlaPlugin::setCustomData(const char* const type, const char* const key, const char* const value, const bool)
1884 {
1885 CARLA_SAFE_ASSERT_RETURN(type != nullptr && type[0] != '\0',);
1886 CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
1887 CARLA_SAFE_ASSERT_RETURN(value != nullptr,);
1888
1889 // Ignore some keys
1890 if (std::strcmp(type, CUSTOM_DATA_TYPE_STRING) == 0)
1891 {
1892 const PluginType ptype = getType();
1893
1894 if ((ptype == PLUGIN_INTERNAL && std::strncmp(key, "CarlaAlternateFile", 18) == 0) ||
1895 (ptype == PLUGIN_DSSI && std::strcmp (key, "guiVisible") == 0) ||
1896 (ptype == PLUGIN_LV2 && std::strncmp(key, "OSC:", 4) == 0))
1897 return;
1898 }
1899
1900 // Check if we already have this key
1901 for (LinkedList<CustomData>::Itenerator it = pData->custom.begin2(); it.valid(); it.next())
1902 {
1903 CustomData& customData(it.getValue(kCustomDataFallbackNC));
1904 CARLA_SAFE_ASSERT_CONTINUE(customData.isValid());
1905
1906 if (std::strcmp(customData.key, key) == 0)
1907 {
1908 if (customData.value != nullptr)
1909 delete[] customData.value;
1910
1911 customData.value = carla_strdup(value);
1912 return;
1913 }
1914 }
1915
1916 // Otherwise store it
1917 CustomData customData;
1918 customData.type = carla_strdup(type);
1919 customData.key = carla_strdup(key);
1920 customData.value = carla_strdup(value);
1921 pData->custom.append(customData);
1922 }
1923
setChunkData(const void * const data,const std::size_t dataSize)1924 void CarlaPlugin::setChunkData(const void* const data, const std::size_t dataSize)
1925 {
1926 CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
1927 CARLA_SAFE_ASSERT_RETURN(dataSize > 0,);
1928 CARLA_SAFE_ASSERT(false); // this should never happen
1929 }
1930
setProgram(const int32_t index,const bool sendGui,const bool sendOsc,const bool sendCallback,const bool)1931 void CarlaPlugin::setProgram(const int32_t index,
1932 const bool sendGui, const bool sendOsc, const bool sendCallback, const bool) noexcept
1933 {
1934 CARLA_SAFE_ASSERT_RETURN(index >= -1 && index < static_cast<int32_t>(pData->prog.count),);
1935
1936 pData->prog.current = index;
1937
1938 pData->engine->callback(sendCallback, sendOsc,
1939 ENGINE_CALLBACK_PROGRAM_CHANGED,
1940 pData->id,
1941 index,
1942 0, 0, 0.0f, nullptr);
1943
1944 // Change default parameter values
1945 if (index >= 0)
1946 {
1947 if (sendGui && (pData->hints & PLUGIN_HAS_CUSTOM_UI) != 0)
1948 uiProgramChange(static_cast<uint32_t>(index));
1949
1950 switch (getType())
1951 {
1952 case PLUGIN_SF2:
1953 case PLUGIN_SFZ:
1954 break;
1955
1956 default:
1957 pData->updateParameterValues(this, sendCallback, sendOsc, true);
1958 break;
1959 }
1960 }
1961 }
1962
setMidiProgram(const int32_t index,const bool sendGui,const bool sendOsc,const bool sendCallback,const bool)1963 void CarlaPlugin::setMidiProgram(const int32_t index,
1964 const bool sendGui, const bool sendOsc, const bool sendCallback, const bool) noexcept
1965 {
1966 CARLA_SAFE_ASSERT_RETURN(index >= -1 && index < static_cast<int32_t>(pData->midiprog.count),);
1967
1968 pData->midiprog.current = index;
1969
1970 pData->engine->callback(sendCallback, sendOsc,
1971 ENGINE_CALLBACK_MIDI_PROGRAM_CHANGED,
1972 pData->id,
1973 index,
1974 0, 0, 0.0f, nullptr);
1975
1976 // Change default parameter values
1977 if (index >= 0)
1978 {
1979 if (sendGui && (pData->hints & PLUGIN_HAS_CUSTOM_UI) != 0)
1980 uiMidiProgramChange(static_cast<uint32_t>(index));
1981
1982 switch (getType())
1983 {
1984 case PLUGIN_SF2:
1985 case PLUGIN_SFZ:
1986 break;
1987
1988 default:
1989 pData->updateParameterValues(this, sendCallback, sendOsc, true);
1990 break;
1991 }
1992 }
1993 }
1994
setMidiProgramById(const uint32_t bank,const uint32_t program,const bool sendGui,const bool sendOsc,const bool sendCallback)1995 void CarlaPlugin::setMidiProgramById(const uint32_t bank, const uint32_t program, const bool sendGui, const bool sendOsc, const bool sendCallback) noexcept
1996 {
1997 for (uint32_t i=0; i < pData->midiprog.count; ++i)
1998 {
1999 if (pData->midiprog.data[i].bank == bank && pData->midiprog.data[i].program == program)
2000 return setMidiProgram(static_cast<int32_t>(i), sendGui, sendOsc, sendCallback);
2001 }
2002 }
2003
setProgramRT(const uint32_t uindex,const bool sendCallbackLater)2004 void CarlaPlugin::setProgramRT(const uint32_t uindex, const bool sendCallbackLater) noexcept
2005 {
2006 CARLA_SAFE_ASSERT_RETURN(uindex < pData->prog.count,);
2007
2008 const int32_t index = static_cast<int32_t>(uindex);
2009 pData->prog.current = index;
2010
2011 // Change default parameter values
2012 switch (getType())
2013 {
2014 case PLUGIN_SF2:
2015 case PLUGIN_SFZ:
2016 break;
2017
2018 default:
2019 pData->updateDefaultParameterValues(this);
2020 break;
2021 }
2022
2023 pData->postponeProgramChangeRtEvent(sendCallbackLater, uindex);
2024 }
2025
setMidiProgramRT(const uint32_t uindex,const bool sendCallbackLater)2026 void CarlaPlugin::setMidiProgramRT(const uint32_t uindex, const bool sendCallbackLater) noexcept
2027 {
2028 CARLA_SAFE_ASSERT_RETURN(uindex < pData->midiprog.count,);
2029
2030 const int32_t index = static_cast<int32_t>(uindex);
2031 pData->midiprog.current = index;
2032
2033 // Change default parameter values
2034 switch (getType())
2035 {
2036 case PLUGIN_SF2:
2037 case PLUGIN_SFZ:
2038 break;
2039
2040 default:
2041 pData->updateDefaultParameterValues(this);
2042 break;
2043 }
2044
2045 pData->postponeMidiProgramChangeRtEvent(sendCallbackLater, uindex);
2046 }
2047
2048 // -------------------------------------------------------------------
2049 // Plugin state
2050
reloadPrograms(const bool)2051 void CarlaPlugin::reloadPrograms(const bool)
2052 {
2053 }
2054
2055 // -------------------------------------------------------------------
2056 // Plugin processing
2057
activate()2058 void CarlaPlugin::activate() noexcept
2059 {
2060 CARLA_SAFE_ASSERT(! pData->active);
2061 }
2062
deactivate()2063 void CarlaPlugin::deactivate() noexcept
2064 {
2065 CARLA_SAFE_ASSERT(pData->active);
2066 }
2067
bufferSizeChanged(const uint32_t)2068 void CarlaPlugin::bufferSizeChanged(const uint32_t)
2069 {
2070 }
2071
sampleRateChanged(const double)2072 void CarlaPlugin::sampleRateChanged(const double)
2073 {
2074 }
2075
offlineModeChanged(const bool)2076 void CarlaPlugin::offlineModeChanged(const bool)
2077 {
2078 }
2079
2080 // -------------------------------------------------------------------
2081 // Misc
2082
idle()2083 void CarlaPlugin::idle()
2084 {
2085 if (! pData->enabled)
2086 return;
2087
2088 const bool hasUI(pData->hints & PLUGIN_HAS_CUSTOM_UI);
2089 const bool needsUiMainThread(pData->hints & PLUGIN_NEEDS_UI_MAIN_THREAD);
2090 const uint32_t latency(getLatencyInFrames());
2091
2092 if (pData->latency.frames != latency)
2093 {
2094 carla_stdout("latency changed to %i samples", latency);
2095
2096 const ScopedSingleProcessLocker sspl(this, true);
2097
2098 pData->client->setLatency(latency);
2099 #ifndef BUILD_BRIDGE
2100 pData->latency.recreateBuffers(pData->latency.channels, latency);
2101 #else
2102 pData->latency.frames = latency;
2103 #endif
2104 }
2105
2106 ProtectedData::PostRtEvents::Access rtEvents(pData->postRtEvents);
2107
2108 if (rtEvents.isEmpty())
2109 return;
2110
2111 for (RtLinkedList<PluginPostRtEvent>::Itenerator it = rtEvents.getDataIterator(); it.valid(); it.next())
2112 {
2113 const PluginPostRtEvent& event(it.getValue(kPluginPostRtEventFallback));
2114 CARLA_SAFE_ASSERT_CONTINUE(event.type != kPluginPostRtEventNull);
2115
2116 switch (event.type)
2117 {
2118 case kPluginPostRtEventNull: {
2119 } break;
2120
2121 case kPluginPostRtEventParameterChange: {
2122 // Update UI
2123 if (event.parameter.index >= 0 && hasUI)
2124 {
2125 if (needsUiMainThread)
2126 pData->postUiEvents.append(event);
2127 else
2128 uiParameterChange(static_cast<uint32_t>(event.parameter.index), event.parameter.value);
2129 }
2130
2131 if (event.sendCallback)
2132 {
2133 // Update Host
2134 pData->engine->callback(true, true,
2135 ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED,
2136 pData->id,
2137 event.parameter.index,
2138 0, 0,
2139 event.parameter.value,
2140 nullptr);
2141 }
2142 } break;
2143
2144 case kPluginPostRtEventProgramChange: {
2145 // Update UI
2146 if (hasUI)
2147 {
2148 if (needsUiMainThread)
2149 pData->postUiEvents.append(event);
2150 else
2151 uiProgramChange(event.program.index);
2152 }
2153
2154 // Update param values
2155 for (uint32_t j=0; j < pData->param.count; ++j)
2156 {
2157 const float paramDefault(pData->param.ranges[j].def);
2158 const float paramValue(getParameterValue(j));
2159
2160 pData->engine->callback(true, true,
2161 ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED,
2162 pData->id,
2163 static_cast<int>(j),
2164 0, 0,
2165 paramValue,
2166 nullptr);
2167 pData->engine->callback(true, true,
2168 ENGINE_CALLBACK_PARAMETER_DEFAULT_CHANGED,
2169 pData->id,
2170 static_cast<int>(j),
2171 0, 0,
2172 paramDefault,
2173 nullptr);
2174 }
2175
2176 if (event.sendCallback)
2177 {
2178 // Update Host
2179 pData->engine->callback(true, true,
2180 ENGINE_CALLBACK_PROGRAM_CHANGED,
2181 pData->id,
2182 static_cast<int>(event.program.index),
2183 0, 0, 0.0f, nullptr);
2184 }
2185 } break;
2186
2187 case kPluginPostRtEventMidiProgramChange: {
2188 // Update UI
2189 if (hasUI)
2190 {
2191 if (needsUiMainThread)
2192 pData->postUiEvents.append(event);
2193 else
2194 uiMidiProgramChange(event.program.index);
2195 }
2196
2197 // Update param values
2198 for (uint32_t j=0; j < pData->param.count; ++j)
2199 {
2200 const float paramDefault(pData->param.ranges[j].def);
2201 const float paramValue(getParameterValue(j));
2202
2203 pData->engine->callback(true, true,
2204 ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED,
2205 pData->id,
2206 static_cast<int>(j),
2207 0, 0,
2208 paramValue,
2209 nullptr);
2210 pData->engine->callback(true, true,
2211 ENGINE_CALLBACK_PARAMETER_DEFAULT_CHANGED,
2212 pData->id,
2213 static_cast<int>(j),
2214 0, 0,
2215 paramDefault,
2216 nullptr);
2217 }
2218
2219 if (event.sendCallback)
2220 {
2221 // Update Host
2222 pData->engine->callback(true, true,
2223 ENGINE_CALLBACK_MIDI_PROGRAM_CHANGED,
2224 pData->id,
2225 static_cast<int>(event.program.index),
2226 0, 0, 0.0f, nullptr);
2227 }
2228 } break;
2229
2230 case kPluginPostRtEventNoteOn: {
2231 CARLA_SAFE_ASSERT_BREAK(event.note.channel < MAX_MIDI_CHANNELS);
2232 CARLA_SAFE_ASSERT_BREAK(event.note.note < MAX_MIDI_NOTE);
2233 CARLA_SAFE_ASSERT_BREAK(event.note.velocity < MAX_MIDI_VALUE);
2234
2235 // Update UI
2236 if (hasUI)
2237 {
2238 if (needsUiMainThread)
2239 pData->postUiEvents.append(event);
2240 else
2241 uiNoteOn(event.note.channel, event.note.note, event.note.velocity);
2242 }
2243
2244 if (event.sendCallback)
2245 {
2246 // Update Host
2247 pData->engine->callback(true, true,
2248 ENGINE_CALLBACK_NOTE_ON,
2249 pData->id,
2250 static_cast<int>(event.note.channel),
2251 static_cast<int>(event.note.note),
2252 static_cast<int>(event.note.velocity),
2253 0.0f, nullptr);
2254 }
2255 } break;
2256
2257 case kPluginPostRtEventNoteOff: {
2258 CARLA_SAFE_ASSERT_BREAK(event.note.channel < MAX_MIDI_CHANNELS);
2259 CARLA_SAFE_ASSERT_BREAK(event.note.note < MAX_MIDI_NOTE);
2260
2261 // Update UI
2262 if (hasUI)
2263 {
2264 if (needsUiMainThread)
2265 pData->postUiEvents.append(event);
2266 else
2267 uiNoteOff(event.note.channel, event.note.note);
2268 }
2269
2270 if (event.sendCallback)
2271 {
2272 // Update Host
2273 pData->engine->callback(true, true,
2274 ENGINE_CALLBACK_NOTE_OFF,
2275 pData->id,
2276 static_cast<int>(event.note.channel),
2277 static_cast<int>(event.note.note),
2278 0, 0.0f, nullptr);
2279 }
2280 } break;
2281
2282 case kPluginPostRtEventMidiLearn: {
2283 CARLA_SAFE_ASSERT_BREAK(event.midiLearn.cc < MAX_MIDI_VALUE);
2284 CARLA_SAFE_ASSERT_BREAK(event.midiLearn.channel < MAX_MIDI_CHANNELS);
2285
2286 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2287 if (event.sendCallback)
2288 {
2289 pData->engine->callback(true, true,
2290 ENGINE_CALLBACK_PARAMETER_MAPPED_CONTROL_INDEX_CHANGED,
2291 pData->id,
2292 static_cast<int>(event.midiLearn.parameter),
2293 static_cast<int>(event.midiLearn.cc),
2294 0, 0.0f, nullptr);
2295
2296 pData->engine->callback(true, true,
2297 ENGINE_CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED,
2298 pData->id,
2299 static_cast<int>(event.midiLearn.parameter),
2300 static_cast<int>(event.midiLearn.channel),
2301 0, 0.0f, nullptr);
2302 }
2303 #endif
2304 } break;
2305 }
2306 }
2307 }
2308
tryLock(const bool forcedOffline)2309 bool CarlaPlugin::tryLock(const bool forcedOffline) noexcept
2310 {
2311 if (forcedOffline)
2312 {
2313 #ifndef STOAT_TEST_BUILD
2314 pData->masterMutex.lock();
2315 return true;
2316 #endif
2317 }
2318
2319 return pData->masterMutex.tryLock();
2320 }
2321
unlock()2322 void CarlaPlugin::unlock() noexcept
2323 {
2324 pData->masterMutex.unlock();
2325 }
2326
2327 // -------------------------------------------------------------------
2328 // Plugin buffers
2329
initBuffers() const2330 void CarlaPlugin::initBuffers() const noexcept
2331 {
2332 pData->audioIn.initBuffers();
2333 pData->audioOut.initBuffers();
2334 pData->cvIn.initBuffers();
2335 pData->cvOut.initBuffers();
2336 pData->event.initBuffers();
2337 }
2338
clearBuffers()2339 void CarlaPlugin::clearBuffers() noexcept
2340 {
2341 pData->clearBuffers();
2342 }
2343
2344 // -------------------------------------------------------------------
2345 // OSC stuff
2346
2347 // FIXME
handleOscMessage(const char * const,const int,const void * const,const char * const,const lo_message)2348 void CarlaPlugin::handleOscMessage(const char* const, const int, const void* const, const char* const, const lo_message)
2349 {
2350 // do nothing
2351 }
2352
2353 // -------------------------------------------------------------------
2354 // MIDI events
2355
sendMidiSingleNote(const uint8_t channel,const uint8_t note,const uint8_t velo,const bool sendGui,const bool sendOsc,const bool sendCallback)2356 void CarlaPlugin::sendMidiSingleNote(const uint8_t channel, const uint8_t note, const uint8_t velo, const bool sendGui, const bool sendOsc, const bool sendCallback)
2357 {
2358 CARLA_SAFE_ASSERT_RETURN(channel < MAX_MIDI_CHANNELS,);
2359 CARLA_SAFE_ASSERT_RETURN(note < MAX_MIDI_NOTE,);
2360 CARLA_SAFE_ASSERT_RETURN(velo < MAX_MIDI_VALUE,);
2361
2362 if (! pData->active)
2363 return;
2364
2365 ExternalMidiNote extNote;
2366 extNote.channel = static_cast<int8_t>(channel);
2367 extNote.note = note;
2368 extNote.velo = velo;
2369
2370 pData->extNotes.appendNonRT(extNote);
2371
2372 if (sendGui && (pData->hints & PLUGIN_HAS_CUSTOM_UI) != 0)
2373 {
2374 if (velo > 0)
2375 uiNoteOn(channel, note, velo);
2376 else
2377 uiNoteOff(channel, note);
2378 }
2379
2380 pData->engine->callback(sendCallback, sendOsc,
2381 (velo > 0) ? ENGINE_CALLBACK_NOTE_ON : ENGINE_CALLBACK_NOTE_OFF,
2382 pData->id,
2383 channel,
2384 note,
2385 velo,
2386 0.0f, nullptr);
2387 }
2388
2389 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
postponeRtAllNotesOff()2390 void CarlaPlugin::postponeRtAllNotesOff()
2391 {
2392 if (pData->ctrlChannel < 0 || pData->ctrlChannel >= MAX_MIDI_CHANNELS)
2393 return;
2394
2395 PluginPostRtEvent postEvent = { kPluginPostRtEventNoteOff, true, {} };
2396 postEvent.note.channel = static_cast<uint8_t>(pData->ctrlChannel);
2397
2398 for (uint8_t i=0; i < MAX_MIDI_NOTE; ++i)
2399 {
2400 postEvent.note.note = i;
2401 pData->postRtEvents.appendRT(postEvent);
2402 }
2403 }
2404 #endif
2405
2406 // -------------------------------------------------------------------
2407 // UI Stuff
2408
setCustomUITitle(const char * const title)2409 void CarlaPlugin::setCustomUITitle(const char* const title) noexcept
2410 {
2411 pData->uiTitle = title;
2412 }
2413
showCustomUI(const bool yesNo)2414 void CarlaPlugin::showCustomUI(const bool yesNo)
2415 {
2416 if (yesNo) {
2417 CARLA_SAFE_ASSERT(false);
2418 }
2419 }
2420
embedCustomUI(void *)2421 void* CarlaPlugin::embedCustomUI(void*)
2422 {
2423 return nullptr;
2424 }
2425
uiIdle()2426 void CarlaPlugin::uiIdle()
2427 {
2428 if (pData->hints & PLUGIN_NEEDS_UI_MAIN_THREAD)
2429 {
2430 // Update parameter outputs
2431 for (uint32_t i=0; i < pData->param.count; ++i)
2432 {
2433 if (pData->param.data[i].type == PARAMETER_OUTPUT)
2434 uiParameterChange(i, getParameterValue(i));
2435 }
2436
2437 const CarlaMutexLocker sl(pData->postUiEvents.mutex);
2438
2439 for (LinkedList<PluginPostRtEvent>::Itenerator it = pData->postUiEvents.data.begin2(); it.valid(); it.next())
2440 {
2441 const PluginPostRtEvent& event(it.getValue(kPluginPostRtEventFallback));
2442 CARLA_SAFE_ASSERT_CONTINUE(event.type != kPluginPostRtEventNull);
2443
2444 switch (event.type)
2445 {
2446 case kPluginPostRtEventNull:
2447 case kPluginPostRtEventMidiLearn:
2448 break;
2449
2450 case kPluginPostRtEventParameterChange:
2451 uiParameterChange(static_cast<uint32_t>(event.parameter.index), event.parameter.value);
2452 break;
2453
2454 case kPluginPostRtEventProgramChange:
2455 uiProgramChange(event.program.index);
2456 break;
2457
2458 case kPluginPostRtEventMidiProgramChange:
2459 uiMidiProgramChange(event.program.index);
2460 break;
2461
2462 case kPluginPostRtEventNoteOn:
2463 uiNoteOn(event.note.channel, event.note.note, event.note.velocity);
2464 break;
2465
2466 case kPluginPostRtEventNoteOff:
2467 uiNoteOff(event.note.channel, event.note.note);
2468 break;
2469 }
2470 }
2471
2472 pData->postUiEvents.data.clear();
2473 }
2474
2475 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2476 if (pData->transientTryCounter == 0)
2477 return;
2478 if (++pData->transientTryCounter % 10 != 0)
2479 return;
2480 if (pData->transientTryCounter >= 200)
2481 return;
2482
2483 carla_stdout("Trying to get window...");
2484
2485 CarlaString uiTitle;
2486
2487 if (pData->uiTitle.isNotEmpty())
2488 {
2489 uiTitle = pData->uiTitle;
2490 }
2491 else
2492 {
2493 uiTitle = pData->name;
2494 uiTitle += " (GUI)";
2495 }
2496
2497 if (CarlaPluginUI::tryTransientWinIdMatch(getUiBridgeProcessId(), uiTitle,
2498 pData->engine->getOptions().frontendWinId, pData->transientFirstTry))
2499 {
2500 pData->transientTryCounter = 0;
2501 pData->transientFirstTry = false;
2502 }
2503 #endif
2504 }
2505
uiParameterChange(const uint32_t index,const float value)2506 void CarlaPlugin::uiParameterChange(const uint32_t index, const float value) noexcept
2507 {
2508 CARLA_SAFE_ASSERT_RETURN(index < getParameterCount(),);
2509 return;
2510
2511 // unused
2512 (void)value;
2513 }
2514
uiProgramChange(const uint32_t index)2515 void CarlaPlugin::uiProgramChange(const uint32_t index) noexcept
2516 {
2517 CARLA_SAFE_ASSERT_RETURN(index < getProgramCount(),);
2518 }
2519
uiMidiProgramChange(const uint32_t index)2520 void CarlaPlugin::uiMidiProgramChange(const uint32_t index) noexcept
2521 {
2522 CARLA_SAFE_ASSERT_RETURN(index < getMidiProgramCount(),);
2523 }
2524
uiNoteOn(const uint8_t channel,const uint8_t note,const uint8_t velo)2525 void CarlaPlugin::uiNoteOn(const uint8_t channel, const uint8_t note, const uint8_t velo) noexcept
2526 {
2527 CARLA_SAFE_ASSERT_RETURN(channel < MAX_MIDI_CHANNELS,);
2528 CARLA_SAFE_ASSERT_RETURN(note < MAX_MIDI_NOTE,);
2529 CARLA_SAFE_ASSERT_RETURN(velo > 0 && velo < MAX_MIDI_VALUE,);
2530 }
2531
uiNoteOff(const uint8_t channel,const uint8_t note)2532 void CarlaPlugin::uiNoteOff(const uint8_t channel, const uint8_t note) noexcept
2533 {
2534 CARLA_SAFE_ASSERT_RETURN(channel < MAX_MIDI_CHANNELS,);
2535 CARLA_SAFE_ASSERT_RETURN(note < MAX_MIDI_NOTE,);
2536 }
2537
getEngine() const2538 CarlaEngine* CarlaPlugin::getEngine() const noexcept
2539 {
2540 return pData->engine;
2541 }
2542
getEngineClient() const2543 CarlaEngineClient* CarlaPlugin::getEngineClient() const noexcept
2544 {
2545 return pData->client;
2546 }
2547
getAudioInPort(const uint32_t index) const2548 CarlaEngineAudioPort* CarlaPlugin::getAudioInPort(const uint32_t index) const noexcept
2549 {
2550 return pData->audioIn.ports[index].port;
2551 }
2552
getAudioOutPort(const uint32_t index) const2553 CarlaEngineAudioPort* CarlaPlugin::getAudioOutPort(const uint32_t index) const noexcept
2554 {
2555 return pData->audioOut.ports[index].port;
2556 }
2557
getCVInPort(const uint32_t index) const2558 CarlaEngineCVPort* CarlaPlugin::getCVInPort(const uint32_t index) const noexcept
2559 {
2560 return pData->cvIn.ports[index].port;
2561 }
2562
getCVOutPort(const uint32_t index) const2563 CarlaEngineCVPort* CarlaPlugin::getCVOutPort(const uint32_t index) const noexcept
2564 {
2565 return pData->cvOut.ports[index].port;
2566 }
2567
getDefaultEventInPort() const2568 CarlaEngineEventPort* CarlaPlugin::getDefaultEventInPort() const noexcept
2569 {
2570 return pData->event.portIn;
2571 }
2572
getDefaultEventOutPort() const2573 CarlaEngineEventPort* CarlaPlugin::getDefaultEventOutPort() const noexcept
2574 {
2575 return pData->event.portOut;
2576 }
2577
2578 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
checkForMidiLearn(EngineEvent & event)2579 void CarlaPlugin::checkForMidiLearn(EngineEvent& event) noexcept
2580 {
2581 if (pData->midiLearnParameterIndex < 0)
2582 return;
2583 if (event.ctrl.param == MIDI_CONTROL_BANK_SELECT || event.ctrl.param == MIDI_CONTROL_BANK_SELECT__LSB)
2584 return;
2585 if (event.ctrl.param >= MAX_MIDI_CONTROL)
2586 return;
2587
2588 const uint32_t parameterId = static_cast<uint32_t>(pData->midiLearnParameterIndex);
2589 CARLA_SAFE_ASSERT_UINT2_RETURN(parameterId < pData->param.count, parameterId, pData->param.count,);
2590
2591 ParameterData& paramData(pData->param.data[parameterId]);
2592 CARLA_SAFE_ASSERT_INT_RETURN(paramData.mappedControlIndex == CONTROL_INDEX_MIDI_LEARN,
2593 paramData.mappedControlIndex,);
2594
2595 event.ctrl.handled = true;
2596 paramData.mappedControlIndex = static_cast<int16_t>(event.ctrl.param);
2597 paramData.midiChannel = event.channel;
2598
2599 pData->postponeMidiLearnRtEvent(true, parameterId, static_cast<uint8_t>(event.ctrl.param), event.channel);
2600 pData->midiLearnParameterIndex = -1;
2601 }
2602 #endif
2603
getNativeHandle() const2604 void* CarlaPlugin::getNativeHandle() const noexcept
2605 {
2606 return nullptr;
2607 }
2608
getNativeDescriptor() const2609 const void* CarlaPlugin::getNativeDescriptor() const noexcept
2610 {
2611 return nullptr;
2612 }
2613
getUiBridgeProcessId() const2614 uintptr_t CarlaPlugin::getUiBridgeProcessId() const noexcept
2615 {
2616 return 0;
2617 }
2618
2619 // -------------------------------------------------------------------
2620
getPatchbayNodeId() const2621 uint32_t CarlaPlugin::getPatchbayNodeId() const noexcept
2622 {
2623 return pData->nodeId;
2624 }
2625
setPatchbayNodeId(const uint32_t nodeId)2626 void CarlaPlugin::setPatchbayNodeId(const uint32_t nodeId) noexcept
2627 {
2628 pData->nodeId = nodeId;
2629 }
2630
2631 // -------------------------------------------------------------------
2632
cloneLV2Files(const CarlaPlugin &)2633 void CarlaPlugin::cloneLV2Files(const CarlaPlugin&)
2634 {
2635 carla_stderr2("Warning: cloneLV2Files() called for non-implemented type");
2636 }
2637
restoreLV2State(const bool temporary)2638 void CarlaPlugin::restoreLV2State(const bool temporary) noexcept
2639 {
2640 carla_stderr2("Warning: restoreLV2State(%s) called for non-implemented type", bool2str(temporary));
2641 }
2642
prepareForDeletion()2643 void CarlaPlugin::prepareForDeletion() noexcept
2644 {
2645 carla_debug("CarlaPlugin::prepareForDeletion");
2646
2647 const CarlaMutexLocker cml(pData->masterMutex);
2648
2649 pData->client->deactivate(true);
2650 }
2651
waitForBridgeSaveSignal()2652 void CarlaPlugin::waitForBridgeSaveSignal() noexcept
2653 {
2654 }
2655
2656 // -------------------------------------------------------------------
2657 // Scoped Disabler
2658
ScopedDisabler(CarlaPlugin * const plugin)2659 CarlaPlugin::ScopedDisabler::ScopedDisabler(CarlaPlugin* const plugin) noexcept
2660 : fPlugin(plugin),
2661 fWasEnabled(false)
2662 {
2663 CARLA_SAFE_ASSERT_RETURN(plugin != nullptr,);
2664 CARLA_SAFE_ASSERT_RETURN(plugin->pData != nullptr,);
2665 CARLA_SAFE_ASSERT_RETURN(plugin->pData->client != nullptr,);
2666 carla_debug("CarlaPlugin::ScopedDisabler(%p)", plugin);
2667
2668 plugin->pData->masterMutex.lock();
2669
2670 if (plugin->pData->enabled)
2671 {
2672 fWasEnabled = true;
2673 plugin->pData->enabled = false;
2674
2675 if (plugin->pData->client->isActive())
2676 plugin->pData->client->deactivate(false);
2677 }
2678 }
2679
~ScopedDisabler()2680 CarlaPlugin::ScopedDisabler::~ScopedDisabler() noexcept
2681 {
2682 CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
2683 CARLA_SAFE_ASSERT_RETURN(fPlugin->pData != nullptr,);
2684 CARLA_SAFE_ASSERT_RETURN(fPlugin->pData->client != nullptr,);
2685 carla_debug("CarlaPlugin::~ScopedDisabler()");
2686
2687 if (fWasEnabled)
2688 {
2689 fPlugin->pData->enabled = true;
2690 fPlugin->pData->client->activate();
2691 }
2692
2693 fPlugin->pData->masterMutex.unlock();
2694 }
2695
2696 // -------------------------------------------------------------------
2697 // Scoped Process Locker
2698
ScopedSingleProcessLocker(CarlaPlugin * const plugin,const bool block)2699 CarlaPlugin::ScopedSingleProcessLocker::ScopedSingleProcessLocker(CarlaPlugin* const plugin, const bool block) noexcept
2700 : fPlugin(plugin),
2701 fBlock(block)
2702 {
2703 CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
2704 CARLA_SAFE_ASSERT_RETURN(fPlugin->pData != nullptr,);
2705 carla_debug("CarlaPlugin::ScopedSingleProcessLocker(%p, %s)", plugin, bool2str(block));
2706
2707 if (! fBlock)
2708 return;
2709
2710 plugin->pData->singleMutex.lock();
2711 }
2712
~ScopedSingleProcessLocker()2713 CarlaPlugin::ScopedSingleProcessLocker::~ScopedSingleProcessLocker() noexcept
2714 {
2715 CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
2716 CARLA_SAFE_ASSERT_RETURN(fPlugin->pData != nullptr,);
2717 carla_debug("CarlaPlugin::~ScopedSingleProcessLocker()");
2718
2719 if (! fBlock)
2720 return;
2721
2722 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2723 if (fPlugin->pData->singleMutex.wasTryLockCalled())
2724 fPlugin->pData->needsReset = true;
2725 #endif
2726
2727 fPlugin->pData->singleMutex.unlock();
2728 }
2729
2730 // -------------------------------------------------------------------
2731
2732 CARLA_BACKEND_END_NAMESPACE
2733