1 /*
2  * Carla Plugin Host
3  * Copyright (C) 2011-2021 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 /* TODO:
19  * - complete processRack(): carefully add to input, sorted events
20  * - implement processPatchbay()
21  * - implement oscSend_control_switch_plugins()
22  * - something about the peaks?
23  */
24 
25 #include "CarlaEngineClient.hpp"
26 #include "CarlaEngineInit.hpp"
27 #include "CarlaEngineInternal.hpp"
28 #include "CarlaPlugin.hpp"
29 
30 #include "CarlaBackendUtils.hpp"
31 #include "CarlaBinaryUtils.hpp"
32 #include "CarlaEngineUtils.hpp"
33 #include "CarlaMathUtils.hpp"
34 #include "CarlaPipeUtils.hpp"
35 #include "CarlaProcessUtils.hpp"
36 #include "CarlaScopeUtils.hpp"
37 #include "CarlaStateUtils.hpp"
38 #include "CarlaMIDI.h"
39 
40 #include "jackbridge/JackBridge.hpp"
41 
42 #include "water/files/File.h"
43 #include "water/streams/MemoryOutputStream.h"
44 #include "water/xml/XmlDocument.h"
45 #include "water/xml/XmlElement.h"
46 
47 #ifdef CARLA_OS_MAC
48 # include "CarlaMacUtils.hpp"
49 # if defined(CARLA_OS_64BIT) && defined(HAVE_LIBMAGIC) && ! defined(BUILD_BRIDGE_ALTERNATIVE_ARCH)
50 #  define ADAPT_FOR_APPLE_SILLICON
51 # endif
52 #endif
53 
54 #include <map>
55 
56 // FIXME Remove on 2.1 release
57 #include "lv2/atom.h"
58 
59 using water::Array;
60 using water::CharPointer_UTF8;
61 using water::File;
62 using water::MemoryOutputStream;
63 using water::String;
64 using water::StringArray;
65 using water::XmlDocument;
66 using water::XmlElement;
67 
68 // #define SFZ_FILES_USING_SFIZZ
69 
70 CARLA_BACKEND_START_NAMESPACE
71 
72 // -----------------------------------------------------------------------
73 // Carla Engine
74 
CarlaEngine()75 CarlaEngine::CarlaEngine()
76     : pData(new ProtectedData(this))
77 {
78     carla_debug("CarlaEngine::CarlaEngine()");
79 }
80 
~CarlaEngine()81 CarlaEngine::~CarlaEngine()
82 {
83     carla_debug("CarlaEngine::~CarlaEngine()");
84 
85     delete pData;
86 }
87 
88 // -----------------------------------------------------------------------
89 // Static calls
90 
getDriverCount()91 uint CarlaEngine::getDriverCount()
92 {
93     carla_debug("CarlaEngine::getDriverCount()");
94     using namespace EngineInit;
95 
96     uint count = 0;
97 
98 #ifndef STATIC_PLUGIN_TARGET
99     if (jackbridge_is_ok())
100         count += 1;
101 #endif
102 
103 #ifndef BUILD_BRIDGE
104 # ifdef USING_JUCE_AUDIO_DEVICES
105     count += getJuceApiCount();
106 # endif
107 # ifdef USING_RTAUDIO
108     count += getRtAudioApiCount();
109 # endif
110 #endif
111 
112     return count;
113 }
114 
getDriverName(const uint index2)115 const char* CarlaEngine::getDriverName(const uint index2)
116 {
117     carla_debug("CarlaEngine::getDriverName(%i)", index2);
118     using namespace EngineInit;
119 
120 #ifndef STATIC_PLUGIN_TARGET
121     uint index = index2;
122 
123     if (jackbridge_is_ok() && index-- == 0)
124         return "JACK";
125 #endif
126 
127 #ifndef BUILD_BRIDGE
128 # ifdef USING_JUCE_AUDIO_DEVICES
129     if (const uint count = getJuceApiCount())
130     {
131         if (index < count)
132             return getJuceApiName(index);
133         index -= count;
134     }
135 # endif
136 # ifdef USING_RTAUDIO
137     if (const uint count = getRtAudioApiCount())
138     {
139         if (index < count)
140             return getRtAudioApiName(index);
141     }
142 # endif
143 #endif
144 
145     carla_stderr("CarlaEngine::getDriverName(%i) - invalid index", index2);
146     return nullptr;
147 }
148 
getDriverDeviceNames(const uint index2)149 const char* const* CarlaEngine::getDriverDeviceNames(const uint index2)
150 {
151     carla_debug("CarlaEngine::getDriverDeviceNames(%i)", index2);
152     using namespace EngineInit;
153 
154 #ifndef STATIC_PLUGIN_TARGET
155     uint index = index2;
156 
157     if (jackbridge_is_ok() && index-- == 0)
158     {
159         static const char* ret[3] = { "Auto-Connect ON", "Auto-Connect OFF", nullptr };
160         return ret;
161     }
162 #endif
163 
164 #ifndef BUILD_BRIDGE
165 # ifdef USING_JUCE_AUDIO_DEVICES
166     if (const uint count = getJuceApiCount())
167     {
168         if (index < count)
169             return getJuceApiDeviceNames(index);
170         index -= count;
171     }
172 # endif
173 # ifdef USING_RTAUDIO
174     if (const uint count = getRtAudioApiCount())
175     {
176         if (index < count)
177             return getRtAudioApiDeviceNames(index);
178     }
179 # endif
180 #endif
181 
182     carla_stderr("CarlaEngine::getDriverDeviceNames(%i) - invalid index", index2);
183     return nullptr;
184 }
185 
getDriverDeviceInfo(const uint index2,const char * const deviceName)186 const EngineDriverDeviceInfo* CarlaEngine::getDriverDeviceInfo(const uint index2, const char* const deviceName)
187 {
188     carla_debug("CarlaEngine::getDriverDeviceInfo(%i, \"%s\")", index2, deviceName);
189     using namespace EngineInit;
190 
191 #ifndef STATIC_PLUGIN_TARGET
192     uint index = index2;
193 
194     if (jackbridge_is_ok() && index-- == 0)
195     {
196         static EngineDriverDeviceInfo devInfo;
197         devInfo.hints       = ENGINE_DRIVER_DEVICE_VARIABLE_BUFFER_SIZE;
198         devInfo.bufferSizes = nullptr;
199         devInfo.sampleRates = nullptr;
200         return &devInfo;
201     }
202 #endif
203 
204 #ifndef BUILD_BRIDGE
205 # ifdef USING_JUCE_AUDIO_DEVICES
206     if (const uint count = getJuceApiCount())
207     {
208         if (index < count)
209             return getJuceDeviceInfo(index, deviceName);
210         index -= count;
211     }
212 # endif
213 # ifdef USING_RTAUDIO
214     if (const uint count = getRtAudioApiCount())
215     {
216         if (index < count)
217             return getRtAudioDeviceInfo(index, deviceName);
218     }
219 # endif
220 #endif
221 
222     carla_stderr("CarlaEngine::getDriverDeviceNames(%i, \"%s\") - invalid index", index2, deviceName);
223     return nullptr;
224 }
225 
showDriverDeviceControlPanel(const uint index2,const char * const deviceName)226 bool CarlaEngine::showDriverDeviceControlPanel(const uint index2, const char* const deviceName)
227 {
228     carla_debug("CarlaEngine::showDriverDeviceControlPanel(%i, \"%s\")", index2, deviceName);
229     using namespace EngineInit;
230 
231 #ifndef STATIC_PLUGIN_TARGET
232     uint index = index2;
233 
234     if (jackbridge_is_ok() && index-- == 0)
235     {
236         return false;
237     }
238 #endif
239 
240 #ifndef BUILD_BRIDGE
241 # ifdef USING_JUCE_AUDIO_DEVICES
242     if (const uint count = getJuceApiCount())
243     {
244         if (index < count)
245             return showJuceDeviceControlPanel(index, deviceName);
246         index -= count;
247     }
248 # endif
249 # ifdef USING_RTAUDIO
250     if (const uint count = getRtAudioApiCount())
251     {
252         if (index < count)
253             return false;
254     }
255 # endif
256 #endif
257 
258     carla_stderr("CarlaEngine::showDriverDeviceControlPanel(%i, \"%s\") - invalid index", index2, deviceName);
259     return false;
260 }
261 
newDriverByName(const char * const driverName)262 CarlaEngine* CarlaEngine::newDriverByName(const char* const driverName)
263 {
264     CARLA_SAFE_ASSERT_RETURN(driverName != nullptr && driverName[0] != '\0', nullptr);
265     carla_debug("CarlaEngine::newDriverByName(\"%s\")", driverName);
266     using namespace EngineInit;
267 
268     if (std::strcmp(driverName, "JACK") == 0)
269         return newJack();
270 
271 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
272     if (std::strcmp(driverName, "Dummy") == 0)
273         return newDummy();
274 #endif
275 
276 #ifndef BUILD_BRIDGE
277 # ifdef USING_JUCE_AUDIO_DEVICES
278     // -------------------------------------------------------------------
279     // linux
280 
281     if (std::strcmp(driverName, "ALSA") == 0)
282         return newJuce(AUDIO_API_ALSA);
283 
284     // -------------------------------------------------------------------
285     // macos
286 
287     if (std::strcmp(driverName, "CoreAudio") == 0)
288         return newJuce(AUDIO_API_COREAUDIO);
289 
290     // -------------------------------------------------------------------
291     // windows
292 
293     if (std::strcmp(driverName, "ASIO") == 0)
294         return newJuce(AUDIO_API_ASIO);
295     if (std::strcmp(driverName, "DirectSound") == 0)
296         return newJuce(AUDIO_API_DIRECTSOUND);
297     if (std::strcmp(driverName, "WASAPI") == 0 || std::strcmp(driverName, "Windows Audio") == 0)
298         return newJuce(AUDIO_API_WASAPI);
299 # endif
300 # ifdef USING_RTAUDIO
301     // -------------------------------------------------------------------
302     // common
303 
304     if (std::strncmp(driverName, "JACK ", 5) == 0)
305         return newRtAudio(AUDIO_API_JACK);
306     if (std::strcmp(driverName, "OSS") == 0)
307         return newRtAudio(AUDIO_API_OSS);
308 
309     // -------------------------------------------------------------------
310     // linux
311 
312     if (std::strcmp(driverName, "ALSA") == 0)
313         return newRtAudio(AUDIO_API_ALSA);
314     if (std::strcmp(driverName, "PulseAudio") == 0)
315         return newRtAudio(AUDIO_API_PULSEAUDIO);
316 
317     // -------------------------------------------------------------------
318     // macos
319 
320     if (std::strcmp(driverName, "CoreAudio") == 0)
321         return newRtAudio(AUDIO_API_COREAUDIO);
322 
323     // -------------------------------------------------------------------
324     // windows
325 
326     if (std::strcmp(driverName, "ASIO") == 0)
327         return newRtAudio(AUDIO_API_ASIO);
328     if (std::strcmp(driverName, "DirectSound") == 0)
329         return newRtAudio(AUDIO_API_DIRECTSOUND);
330     if (std::strcmp(driverName, "WASAPI") == 0)
331         return newRtAudio(AUDIO_API_WASAPI);
332 # endif
333 #endif
334 
335     carla_stderr("CarlaEngine::newDriverByName(\"%s\") - invalid driver name", driverName);
336     return nullptr;
337 }
338 
339 // -----------------------------------------------------------------------
340 // Constant values
341 
getMaxClientNameSize() const342 uint CarlaEngine::getMaxClientNameSize() const noexcept
343 {
344     return STR_MAX/2;
345 }
346 
getMaxPortNameSize() const347 uint CarlaEngine::getMaxPortNameSize() const noexcept
348 {
349     return STR_MAX;
350 }
351 
getCurrentPluginCount() const352 uint CarlaEngine::getCurrentPluginCount() const noexcept
353 {
354     return pData->curPluginCount;
355 }
356 
getMaxPluginNumber() const357 uint CarlaEngine::getMaxPluginNumber() const noexcept
358 {
359     return pData->maxPluginNumber;
360 }
361 
362 // -----------------------------------------------------------------------
363 // Virtual, per-engine type calls
364 
close()365 bool CarlaEngine::close()
366 {
367     carla_debug("CarlaEngine::close()");
368 
369     if (pData->curPluginCount != 0)
370     {
371         pData->aboutToClose = true;
372         removeAllPlugins();
373     }
374 
375     pData->close();
376 
377     callback(true, true, ENGINE_CALLBACK_ENGINE_STOPPED, 0, 0, 0, 0, 0.0f, nullptr);
378     return true;
379 }
380 
usesConstantBufferSize() const381 bool CarlaEngine::usesConstantBufferSize() const noexcept
382 {
383     return true;
384 }
385 
idle()386 void CarlaEngine::idle() noexcept
387 {
388     CARLA_SAFE_ASSERT_RETURN(pData->nextAction.opcode == kEnginePostActionNull,);
389     CARLA_SAFE_ASSERT_RETURN(pData->nextPluginId == pData->maxPluginNumber,);
390     CARLA_SAFE_ASSERT_RETURN(getType() != kEngineTypePlugin,);
391 
392     const bool engineNotRunning = !isRunning();
393 
394     for (uint i=0; i < pData->curPluginCount; ++i)
395     {
396         if (const CarlaPluginPtr plugin = pData->plugins[i].plugin)
397         {
398             if (plugin->isEnabled())
399             {
400                 const uint hints = plugin->getHints();
401 
402                 if (engineNotRunning)
403                 {
404                     try {
405                         plugin->idle();
406                     } CARLA_SAFE_EXCEPTION_CONTINUE("Plugin idle");
407 
408                     if (hints & PLUGIN_HAS_CUSTOM_UI)
409                     {
410                         try {
411                             plugin->uiIdle();
412                         } CARLA_SAFE_EXCEPTION_CONTINUE("Plugin uiIdle");
413                     }
414                 }
415                 else if ((hints & PLUGIN_HAS_CUSTOM_UI) != 0 && (hints & PLUGIN_NEEDS_UI_MAIN_THREAD) != 0)
416                 {
417                     try {
418                         plugin->uiIdle();
419                     } CARLA_SAFE_EXCEPTION_CONTINUE("Plugin uiIdle");
420                 }
421             }
422         }
423     }
424 
425 #if defined(HAVE_LIBLO) && !defined(BUILD_BRIDGE)
426     pData->osc.idle();
427 #endif
428 
429     pData->deletePluginsAsNeeded();
430 }
431 
addClient(CarlaPluginPtr plugin)432 CarlaEngineClient* CarlaEngine::addClient(CarlaPluginPtr plugin)
433 {
434 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
435     return new CarlaEngineClientForStandalone(*this, pData->graph, plugin);
436 #else
437     return new CarlaEngineClientForBridge(*this);
438 
439     // unused
440     (void)plugin;
441 #endif
442 }
443 
getDSPLoad() const444 float CarlaEngine::getDSPLoad() const noexcept
445 {
446 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
447     return pData->dspLoad;
448 #else
449     return 0.0f;
450 #endif
451 }
452 
getTotalXruns() const453 uint32_t CarlaEngine::getTotalXruns() const noexcept
454 {
455 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
456     return pData->xruns;
457 #else
458     return 0;
459 #endif
460 }
461 
clearXruns() const462 void CarlaEngine::clearXruns() const noexcept
463 {
464 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
465     pData->xruns = 0;
466 #endif
467 }
468 
showDeviceControlPanel() const469 bool CarlaEngine::showDeviceControlPanel() const noexcept
470 {
471     return false;
472 }
473 
setBufferSizeAndSampleRate(const uint,const double)474 bool CarlaEngine::setBufferSizeAndSampleRate(const uint, const double)
475 {
476     return false;
477 }
478 
479 // -----------------------------------------------------------------------
480 // Plugin management
481 
addPlugin(const BinaryType btype,const PluginType ptype,const char * const filename,const char * const name,const char * const label,const int64_t uniqueId,const void * const extra,const uint options)482 bool CarlaEngine::addPlugin(const BinaryType btype,
483                             const PluginType ptype,
484                             const char* const filename,
485                             const char* const name,
486                             const char* const label,
487                             const int64_t uniqueId,
488                             const void* const extra,
489                             const uint options)
490 {
491     CARLA_SAFE_ASSERT_RETURN_ERR(pData->isIdling == 0, "An operation is still being processed, please wait for it to finish");
492 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
493     CARLA_SAFE_ASSERT_RETURN_ERR(pData->plugins != nullptr, "Invalid engine internal data");
494     CARLA_SAFE_ASSERT_RETURN_ERR(pData->nextPluginId <= pData->maxPluginNumber, "Invalid engine internal data");
495 #endif
496     CARLA_SAFE_ASSERT_RETURN_ERR(pData->nextAction.opcode == kEnginePostActionNull, "Invalid engine internal data");
497     CARLA_SAFE_ASSERT_RETURN_ERR(btype != BINARY_NONE, "Invalid plugin binary mode");
498     CARLA_SAFE_ASSERT_RETURN_ERR(ptype != PLUGIN_NONE, "Invalid plugin type");
499     CARLA_SAFE_ASSERT_RETURN_ERR((filename != nullptr && filename[0] != '\0') || (label != nullptr && label[0] != '\0'), "Invalid plugin filename and label");
500     carla_debug("CarlaEngine::addPlugin(%i:%s, %i:%s, \"%s\", \"%s\", \"%s\", " P_INT64 ", %p, %u)",
501                 btype, BinaryType2Str(btype), ptype, PluginType2Str(ptype), filename, name, label, uniqueId, extra, options);
502 
503 #ifndef CARLA_OS_WIN
504     if (ptype != PLUGIN_JACK && ptype != PLUGIN_LV2 && filename != nullptr && filename[0] != '\0') {
505         CARLA_SAFE_ASSERT_RETURN_ERR(filename[0] == CARLA_OS_SEP || filename[0] == '.' || filename[0] == '~', "Invalid plugin filename");
506     }
507 #endif
508 
509     uint id;
510 
511 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
512     CarlaPluginPtr oldPlugin;
513 
514     if (pData->nextPluginId < pData->curPluginCount)
515     {
516         id = pData->nextPluginId;
517         pData->nextPluginId = pData->maxPluginNumber;
518 
519         oldPlugin = pData->plugins[id].plugin;
520 
521         CARLA_SAFE_ASSERT_RETURN_ERR(oldPlugin.get() != nullptr, "Invalid replace plugin Id");
522     }
523     else
524 #endif
525     {
526         id = pData->curPluginCount;
527 
528         if (id == pData->maxPluginNumber)
529         {
530             setLastError("Maximum number of plugins reached");
531             return false;
532         }
533 
534 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
535         CARLA_SAFE_ASSERT_RETURN_ERR(pData->plugins[id].plugin.get() == nullptr, "Invalid engine internal data");
536 #endif
537     }
538 
539     CarlaPlugin::Initializer initializer = {
540         this,
541         id,
542         filename,
543         name,
544         label,
545         uniqueId,
546         options
547     };
548 
549     CarlaPluginPtr plugin;
550     CarlaString bridgeBinary(pData->options.binaryDir);
551 
552     if (bridgeBinary.isNotEmpty())
553     {
554 #ifndef CARLA_OS_WIN
555         if (btype == BINARY_NATIVE)
556         {
557             bridgeBinary += CARLA_OS_SEP_STR "carla-bridge-native";
558         }
559         else
560 #endif
561         {
562             switch (btype)
563             {
564             case BINARY_POSIX32:
565                 bridgeBinary += CARLA_OS_SEP_STR "carla-bridge-posix32";
566                 break;
567             case BINARY_POSIX64:
568                 bridgeBinary += CARLA_OS_SEP_STR "carla-bridge-posix64";
569                 break;
570             case BINARY_WIN32:
571 #if defined(CARLA_OS_WIN) && !defined(CARLA_OS_64BIT)
572                 bridgeBinary += CARLA_OS_SEP_STR "carla-bridge-native.exe";
573 #else
574                 bridgeBinary += CARLA_OS_SEP_STR "carla-bridge-win32.exe";
575 #endif
576                 break;
577             case BINARY_WIN64:
578 #if defined(CARLA_OS_WIN) && defined(CARLA_OS_64BIT)
579                 bridgeBinary += CARLA_OS_SEP_STR "carla-bridge-native.exe";
580 #else
581                 bridgeBinary += CARLA_OS_SEP_STR "carla-bridge-win64.exe";
582 #endif
583                 break;
584             default:
585                 bridgeBinary.clear();
586                 break;
587             }
588         }
589 
590         if (! File(bridgeBinary.buffer()).existsAsFile())
591             bridgeBinary.clear();
592     }
593 
594     const bool canBeBridged = ptype != PLUGIN_INTERNAL
595                            && ptype != PLUGIN_DLS
596                            && ptype != PLUGIN_GIG
597                            && ptype != PLUGIN_SF2
598                            && ptype != PLUGIN_SFZ
599                            && ptype != PLUGIN_JACK;
600 
601     // Prefer bridges for some specific plugins
602     bool preferBridges = pData->options.preferPluginBridges;
603     const char* needsArchBridge = nullptr;
604 
605 #ifdef CARLA_OS_MAC
606     // Plugin might be in quarentine due to Apple stupid notarization rules, let's remove that if possible
607     if (canBeBridged && ptype != PLUGIN_LV2 && ptype != PLUGIN_AU)
608         removeFileFromQuarantine(filename);
609 #endif
610 
611 #ifndef BUILD_BRIDGE
612     if (canBeBridged && ! preferBridges)
613     {
614 # if 0
615         if (ptype == PLUGIN_LV2 && label != nullptr)
616         {
617             if (std::strncmp(label, "http://calf.sourceforge.net/plugins/", 36) == 0 ||
618                 std::strcmp(label, "http://factorial.hu/plugins/lv2/ir") == 0 ||
619                 std::strstr(label, "v1.sourceforge.net/lv2") != nullptr)
620             {
621                 preferBridges = true;
622             }
623         }
624 # endif
625 # ifdef ADAPT_FOR_APPLE_SILLICON
626         // see if this binary needs bridging
627         if (ptype == PLUGIN_VST2 || ptype == PLUGIN_VST3)
628         {
629             if (const char* const vst2Binary = findBinaryInBundle(filename))
630             {
631                 const CarlaMagic magic;
632                 if (const char* const output = magic.getFileDescription(vst2Binary))
633                 {
634                     carla_stdout("VST binary magic output is '%s'", output);
635 #  ifdef __aarch64__
636                     if (std::strstr(output, "arm64") == nullptr && std::strstr(output, "x86_64") != nullptr)
637                         needsArchBridge = "x86_64";
638 #  else
639                     if (std::strstr(output, "x86_64") == nullptr && std::strstr(output, "arm64") != nullptr)
640                         needsArchBridge = "arm64";
641 #  endif
642                 }
643                 else
644                 {
645                     carla_stdout("VST binary magic output is null");
646                 }
647             }
648             else
649             {
650                 carla_stdout("Search for binary in VST bundle failed");
651             }
652         }
653 # endif
654     }
655 #endif // ! BUILD_BRIDGE
656 
657     if (canBeBridged && (needsArchBridge || btype != BINARY_NATIVE || (preferBridges && bridgeBinary.isNotEmpty())))
658     {
659         if (bridgeBinary.isNotEmpty())
660         {
661             plugin = CarlaPlugin::newBridge(initializer, btype, ptype, needsArchBridge, bridgeBinary);
662         }
663         else
664         {
665             setLastError("This Carla build cannot handle this binary");
666             return false;
667         }
668     }
669     else
670     {
671 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
672         bool use16Outs;
673 #endif
674         setLastError("Invalid or unsupported plugin type");
675 
676         // Some stupid plugins mess up with global signals, err!!
677         const CarlaSignalRestorer csr;
678 
679         switch (ptype)
680         {
681         case PLUGIN_NONE:
682             break;
683 
684         case PLUGIN_LADSPA:
685             plugin = CarlaPlugin::newLADSPA(initializer, (const LADSPA_RDF_Descriptor*)extra);
686             break;
687 
688         case PLUGIN_DSSI:
689             plugin = CarlaPlugin::newDSSI(initializer);
690             break;
691 
692         case PLUGIN_LV2:
693             plugin = CarlaPlugin::newLV2(initializer);
694             break;
695 
696         case PLUGIN_VST2:
697             plugin = CarlaPlugin::newVST2(initializer);
698             break;
699 
700         case PLUGIN_VST3:
701             plugin = CarlaPlugin::newVST3(initializer);
702             break;
703 
704         case PLUGIN_AU:
705             plugin = CarlaPlugin::newAU(initializer);
706             break;
707 
708 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
709         case PLUGIN_INTERNAL:
710             plugin = CarlaPlugin::newNative(initializer);
711             break;
712 
713         case PLUGIN_DLS:
714         case PLUGIN_GIG:
715         case PLUGIN_SF2:
716             use16Outs = (extra != nullptr && std::strcmp((const char*)extra, "true") == 0);
717             plugin = CarlaPlugin::newFluidSynth(initializer, ptype, use16Outs);
718             break;
719 
720         case PLUGIN_SFZ:
721 # ifdef SFZ_FILES_USING_SFIZZ
722             {
723                 CarlaPlugin::Initializer sfizzInitializer = {
724                     this,
725                     id,
726                     name,
727                     "",
728                     "http://sfztools.github.io/sfizz",
729                     0,
730                     options
731                 };
732 
733                 plugin = CarlaPlugin::newLV2(sfizzInitializer);
734             }
735 # else
736             plugin = CarlaPlugin::newSFZero(initializer);
737 # endif
738             break;
739 
740         case PLUGIN_JACK:
741             plugin = CarlaPlugin::newJackApp(initializer);
742             break;
743 #else
744         case PLUGIN_INTERNAL:
745         case PLUGIN_DLS:
746         case PLUGIN_GIG:
747         case PLUGIN_SF2:
748         case PLUGIN_SFZ:
749         case PLUGIN_JACK:
750             setLastError("Plugin bridges cannot handle this binary");
751             break;
752 #endif
753         }
754     }
755 
756     if (plugin.get() == nullptr)
757         return false;
758 
759     plugin->reload();
760 
761 #ifdef SFZ_FILES_USING_SFIZZ
762     if (ptype == PLUGIN_SFZ && plugin->getType() == PLUGIN_LV2)
763     {
764         plugin->setCustomData(LV2_ATOM__Path,
765                               "http://sfztools.github.io/sfizz:sfzfile",
766                               filename,
767                               false);
768 
769         plugin->restoreLV2State(true);
770     }
771 #endif
772 
773     bool canRun = true;
774 
775     if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
776     {
777         /**/ if (plugin->getMidiInCount() > 1 || plugin->getMidiOutCount() > 1)
778         {
779             setLastError("Carla's patchbay mode cannot work with plugins that have multiple MIDI ports, sorry!");
780             canRun = false;
781         }
782     }
783 
784     if (! canRun)
785     {
786         return false;
787     }
788 
789     EnginePluginData& pluginData(pData->plugins[id]);
790     pluginData.plugin = plugin;
791     carla_zeroFloats(pluginData.peaks, 4);
792 
793 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
794     if (oldPlugin.get() != nullptr)
795     {
796         CARLA_SAFE_ASSERT(! pData->loadingProject);
797 
798         const ScopedThreadStopper sts(this);
799 
800         if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
801             pData->graph.replacePlugin(oldPlugin, plugin);
802 
803         const bool  wasActive = oldPlugin->getInternalParameterValue(PARAMETER_ACTIVE) >= 0.5f;
804         const float oldDryWet = oldPlugin->getInternalParameterValue(PARAMETER_DRYWET);
805         const float oldVolume = oldPlugin->getInternalParameterValue(PARAMETER_VOLUME);
806 
807         oldPlugin->prepareForDeletion();
808         pData->pluginsToDelete.push_back(oldPlugin);
809 
810         if (plugin->getHints() & PLUGIN_CAN_DRYWET)
811             plugin->setDryWet(oldDryWet, true, true);
812 
813         if (plugin->getHints() & PLUGIN_CAN_VOLUME)
814             plugin->setVolume(oldVolume, true, true);
815 
816         plugin->setActive(wasActive, true, true);
817         plugin->setEnabled(true);
818 
819         callback(true, true, ENGINE_CALLBACK_RELOAD_ALL, id, 0, 0, 0, 0.0f, nullptr);
820     }
821     else if (! pData->loadingProject)
822 #endif
823     {
824         plugin->setEnabled(true);
825 
826         ++pData->curPluginCount;
827         callback(true, true, ENGINE_CALLBACK_PLUGIN_ADDED, id, 0, 0, 0, 0.0f, plugin->getName());
828 
829         if (getType() != kEngineTypeBridge)
830             plugin->setActive(true, true, true);
831 
832 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
833         if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
834             pData->graph.addPlugin(plugin);
835 #endif
836     }
837 
838     return true;
839 }
840 
addPlugin(const PluginType ptype,const char * const filename,const char * const name,const char * const label,const int64_t uniqueId,const void * const extra)841 bool CarlaEngine::addPlugin(const PluginType ptype,
842                             const char* const filename,
843                             const char* const name,
844                             const char* const label,
845                             const int64_t uniqueId,
846                             const void* const extra)
847 {
848     return addPlugin(BINARY_NATIVE, ptype, filename, name, label, uniqueId, extra, PLUGIN_OPTIONS_NULL);
849 }
850 
removePlugin(const uint id)851 bool CarlaEngine::removePlugin(const uint id)
852 {
853     CARLA_SAFE_ASSERT_RETURN_ERR(pData->isIdling == 0, "An operation is still being processed, please wait for it to finish");
854 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
855     CARLA_SAFE_ASSERT_RETURN_ERR(pData->plugins != nullptr, "Invalid engine internal data");
856     CARLA_SAFE_ASSERT_RETURN_ERR(pData->curPluginCount != 0, "Invalid engine internal data");
857 #else
858     CARLA_SAFE_ASSERT_RETURN_ERR(id == 0, "Invalid engine internal data");
859 #endif
860     CARLA_SAFE_ASSERT_RETURN_ERR(pData->nextAction.opcode == kEnginePostActionNull, "Invalid engine internal data");
861     CARLA_SAFE_ASSERT_RETURN_ERR(id < pData->curPluginCount, "Invalid plugin Id");
862     carla_debug("CarlaEngine::removePlugin(%i)", id);
863 
864     const CarlaPluginPtr plugin = pData->plugins[id].plugin;
865 
866     CARLA_SAFE_ASSERT_RETURN_ERR(plugin.get() != nullptr, "Could not find plugin to remove");
867     CARLA_SAFE_ASSERT_RETURN_ERR(plugin->getId() == id, "Invalid engine internal data");
868 
869     const ScopedThreadStopper sts(this);
870 
871 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
872     if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
873         pData->graph.removePlugin(plugin);
874 
875     const ScopedActionLock sal(this, kEnginePostActionRemovePlugin, id, 0);
876 
877     /*
878     for (uint i=id; i < pData->curPluginCount; ++i)
879     {
880         CarlaPlugin* const plugin2(pData->plugins[i].plugin);
881         CARLA_SAFE_ASSERT_BREAK(plugin2 != nullptr);
882         plugin2->updateOscURL();
883     }
884     */
885 #else
886     pData->curPluginCount = 0;
887     pData->plugins[0].plugin = nullptr;
888     carla_zeroStruct(pData->plugins[0].peaks);
889 #endif
890 
891     plugin->prepareForDeletion();
892     pData->pluginsToDelete.push_back(plugin);
893 
894     callback(true, true, ENGINE_CALLBACK_PLUGIN_REMOVED, id, 0, 0, 0, 0.0f, nullptr);
895     return true;
896 }
897 
removeAllPlugins()898 bool CarlaEngine::removeAllPlugins()
899 {
900     CARLA_SAFE_ASSERT_RETURN_ERR(pData->isIdling == 0, "An operation is still being processed, please wait for it to finish");
901 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
902     CARLA_SAFE_ASSERT_RETURN_ERR(pData->plugins != nullptr, "Invalid engine internal data");
903     CARLA_SAFE_ASSERT_RETURN_ERR(pData->nextPluginId == pData->maxPluginNumber, "Invalid engine internal data");
904 #endif
905     CARLA_SAFE_ASSERT_RETURN_ERR(pData->nextAction.opcode == kEnginePostActionNull, "Invalid engine internal data");
906     carla_debug("CarlaEngine::removeAllPlugins()");
907 
908     if (pData->curPluginCount == 0)
909         return true;
910 
911     const ScopedThreadStopper sts(this);
912 
913     const uint curPluginCount = pData->curPluginCount;
914 
915 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
916     if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
917         pData->graph.removeAllPlugins();
918 #endif
919 
920     const ScopedActionLock sal(this, kEnginePostActionZeroCount, 0, 0);
921 
922     callback(true, false, ENGINE_CALLBACK_IDLE, 0, 0, 0, 0, 0.0f, nullptr);
923 
924     for (uint i=0; i < curPluginCount; ++i)
925     {
926         const uint id = curPluginCount - i - 1;
927         EnginePluginData& pluginData(pData->plugins[id]);
928 
929         pluginData.plugin->prepareForDeletion();
930         pData->pluginsToDelete.push_back(pluginData.plugin);
931 
932         pluginData.plugin.reset();
933         carla_zeroStruct(pluginData.peaks);
934 
935         callback(true, true, ENGINE_CALLBACK_PLUGIN_REMOVED, id, 0, 0, 0, 0.0f, nullptr);
936         callback(true, false, ENGINE_CALLBACK_IDLE, 0, 0, 0, 0, 0.0f, nullptr);
937     }
938 
939     return true;
940 }
941 
942 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
renamePlugin(const uint id,const char * const newName)943 bool CarlaEngine::renamePlugin(const uint id, const char* const newName)
944 {
945     CARLA_SAFE_ASSERT_RETURN_ERR(pData->isIdling == 0, "An operation is still being processed, please wait for it to finish");
946     CARLA_SAFE_ASSERT_RETURN_ERR(pData->plugins != nullptr, "Invalid engine internal data");
947     CARLA_SAFE_ASSERT_RETURN_ERR(pData->curPluginCount != 0, "Invalid engine internal data");
948     CARLA_SAFE_ASSERT_RETURN_ERR(pData->nextAction.opcode == kEnginePostActionNull, "Invalid engine internal data");
949     CARLA_SAFE_ASSERT_RETURN_ERR(id < pData->curPluginCount, "Invalid plugin Id");
950     CARLA_SAFE_ASSERT_RETURN_ERR(newName != nullptr && newName[0] != '\0', "Invalid plugin name");
951     carla_debug("CarlaEngine::renamePlugin(%i, \"%s\")", id, newName);
952 
953     const CarlaPluginPtr plugin = pData->plugins[id].plugin;
954     CARLA_SAFE_ASSERT_RETURN_ERR(plugin.get() != nullptr, "Could not find plugin to rename");
955     CARLA_SAFE_ASSERT_RETURN_ERR(plugin->getId() == id, "Invalid engine internal data");
956 
957     const char* const uniqueName(getUniquePluginName(newName));
958     CARLA_SAFE_ASSERT_RETURN_ERR(uniqueName != nullptr, "Unable to get new unique plugin name");
959 
960     plugin->setName(uniqueName);
961 
962     if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
963         pData->graph.renamePlugin(plugin, uniqueName);
964 
965     callback(true, true, ENGINE_CALLBACK_PLUGIN_RENAMED, id, 0, 0, 0, 0.0f, uniqueName);
966 
967     delete[] uniqueName;
968     return true;
969 }
970 
clonePlugin(const uint id)971 bool CarlaEngine::clonePlugin(const uint id)
972 {
973     CARLA_SAFE_ASSERT_RETURN_ERR(pData->isIdling == 0, "An operation is still being processed, please wait for it to finish");
974     CARLA_SAFE_ASSERT_RETURN_ERR(pData->plugins != nullptr, "Invalid engine internal data");
975     CARLA_SAFE_ASSERT_RETURN_ERR(pData->curPluginCount != 0, "Invalid engine internal data");
976     CARLA_SAFE_ASSERT_RETURN_ERR(pData->nextAction.opcode == kEnginePostActionNull, "Invalid engine internal data");
977     CARLA_SAFE_ASSERT_RETURN_ERR(id < pData->curPluginCount, "Invalid plugin Id");
978     carla_debug("CarlaEngine::clonePlugin(%i)", id);
979 
980     const CarlaPluginPtr plugin = pData->plugins[id].plugin;
981 
982     CARLA_SAFE_ASSERT_RETURN_ERR(plugin.get() != nullptr, "Could not find plugin to clone");
983     CARLA_SAFE_ASSERT_RETURN_ERR(plugin->getId() == id, "Invalid engine internal data");
984 
985     char label[STR_MAX+1];
986     carla_zeroChars(label, STR_MAX+1);
987 
988     if (! plugin->getLabel(label))
989         label[0] = '\0';
990 
991     const uint pluginCountBefore(pData->curPluginCount);
992 
993     if (! addPlugin(plugin->getBinaryType(), plugin->getType(),
994                     plugin->getFilename(), plugin->getName(), label, plugin->getUniqueId(),
995                     plugin->getExtraStuff(), plugin->getOptionsEnabled()))
996         return false;
997 
998     CARLA_SAFE_ASSERT_RETURN_ERR(pluginCountBefore+1 == pData->curPluginCount, "No new plugin found");
999 
1000     if (const CarlaPluginPtr newPlugin = pData->plugins[pluginCountBefore].plugin)
1001     {
1002         if (newPlugin->getType() == PLUGIN_LV2)
1003             newPlugin->cloneLV2Files(*plugin);
1004         newPlugin->loadStateSave(plugin->getStateSave(true));
1005     }
1006 
1007     return true;
1008 }
1009 
replacePlugin(const uint id)1010 bool CarlaEngine::replacePlugin(const uint id) noexcept
1011 {
1012     CARLA_SAFE_ASSERT_RETURN_ERR(pData->isIdling == 0, "An operation is still being processed, please wait for it to finish");
1013     CARLA_SAFE_ASSERT_RETURN_ERR(pData->plugins != nullptr, "Invalid engine internal data");
1014     CARLA_SAFE_ASSERT_RETURN_ERR(pData->curPluginCount != 0, "Invalid engine internal data");
1015     CARLA_SAFE_ASSERT_RETURN_ERR(pData->nextAction.opcode == kEnginePostActionNull, "Invalid engine internal data");
1016     carla_debug("CarlaEngine::replacePlugin(%i)", id);
1017 
1018     // might use this to reset
1019     if (id == pData->maxPluginNumber)
1020     {
1021         pData->nextPluginId = pData->maxPluginNumber;
1022         return true;
1023     }
1024 
1025     CARLA_SAFE_ASSERT_RETURN_ERR(id < pData->curPluginCount, "Invalid plugin Id");
1026 
1027     const CarlaPluginPtr plugin = pData->plugins[id].plugin;
1028 
1029     CARLA_SAFE_ASSERT_RETURN_ERR(plugin.get() != nullptr, "Could not find plugin to replace");
1030     CARLA_SAFE_ASSERT_RETURN_ERR(plugin->getId() == id, "Invalid engine internal data");
1031 
1032     pData->nextPluginId = id;
1033 
1034     return true;
1035 }
1036 
switchPlugins(const uint idA,const uint idB)1037 bool CarlaEngine::switchPlugins(const uint idA, const uint idB) noexcept
1038 {
1039     CARLA_SAFE_ASSERT_RETURN_ERR(pData->isIdling == 0, "An operation is still being processed, please wait for it to finish");
1040     CARLA_SAFE_ASSERT_RETURN_ERR(pData->plugins != nullptr, "Invalid engine internal data");
1041     CARLA_SAFE_ASSERT_RETURN_ERR(pData->curPluginCount >= 2, "Invalid engine internal data");
1042     CARLA_SAFE_ASSERT_RETURN_ERR(pData->nextAction.opcode == kEnginePostActionNull, "Invalid engine internal data");
1043     CARLA_SAFE_ASSERT_RETURN_ERR(idA != idB, "Invalid operation, cannot switch plugin with itself");
1044     CARLA_SAFE_ASSERT_RETURN_ERR(idA < pData->curPluginCount, "Invalid plugin Id");
1045     CARLA_SAFE_ASSERT_RETURN_ERR(idB < pData->curPluginCount, "Invalid plugin Id");
1046     carla_debug("CarlaEngine::switchPlugins(%i)", idA, idB);
1047 
1048     const CarlaPluginPtr pluginA = pData->plugins[idA].plugin;
1049     const CarlaPluginPtr pluginB = pData->plugins[idB].plugin;
1050 
1051     CARLA_SAFE_ASSERT_RETURN_ERR(pluginA.get() != nullptr, "Could not find plugin to switch");
1052     CARLA_SAFE_ASSERT_RETURN_ERR(pluginB.get() != nullptr, "Could not find plugin to switch");
1053     CARLA_SAFE_ASSERT_RETURN_ERR(pluginA->getId() == idA, "Invalid engine internal data");
1054     CARLA_SAFE_ASSERT_RETURN_ERR(pluginB->getId() == idB, "Invalid engine internal data");
1055 
1056     const ScopedThreadStopper sts(this);
1057 
1058     if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
1059         pData->graph.switchPlugins(pluginA, pluginB);
1060 
1061     const ScopedActionLock sal(this, kEnginePostActionSwitchPlugins, idA, idB);
1062 
1063     // TODO
1064     /*
1065     pluginA->updateOscURL();
1066     pluginB->updateOscURL();
1067 
1068     if (isOscControlRegistered())
1069         oscSend_control_switch_plugins(idA, idB);
1070     */
1071 
1072     return true;
1073 }
1074 #endif
1075 
touchPluginParameter(const uint,const uint32_t,const bool)1076 void CarlaEngine::touchPluginParameter(const uint, const uint32_t, const bool) noexcept
1077 {
1078 }
1079 
getPlugin(const uint id) const1080 CarlaPluginPtr CarlaEngine::getPlugin(const uint id) const noexcept
1081 {
1082 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1083     CARLA_SAFE_ASSERT_RETURN_ERRN(pData->plugins != nullptr, "Invalid engine internal data");
1084     CARLA_SAFE_ASSERT_RETURN_ERRN(pData->curPluginCount != 0, "Invalid engine internal data");
1085 #endif
1086     CARLA_SAFE_ASSERT_RETURN_ERRN(pData->nextAction.opcode == kEnginePostActionNull, "Invalid engine internal data");
1087     CARLA_SAFE_ASSERT_RETURN_ERRN(id < pData->curPluginCount, "Invalid plugin Id");
1088 
1089     return pData->plugins[id].plugin;
1090 }
1091 
getPluginUnchecked(const uint id) const1092 CarlaPluginPtr CarlaEngine::getPluginUnchecked(const uint id) const noexcept
1093 {
1094     return pData->plugins[id].plugin;
1095 }
1096 
getUniquePluginName(const char * const name) const1097 const char* CarlaEngine::getUniquePluginName(const char* const name) const
1098 {
1099     CARLA_SAFE_ASSERT_RETURN(pData->nextAction.opcode == kEnginePostActionNull, nullptr);
1100     CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', nullptr);
1101     carla_debug("CarlaEngine::getUniquePluginName(\"%s\")", name);
1102 
1103     CarlaString sname;
1104     sname = name;
1105 
1106     if (sname.isEmpty())
1107     {
1108         sname = "(No name)";
1109         return sname.dup();
1110     }
1111 
1112     const std::size_t maxNameSize(carla_minConstrained<uint>(getMaxClientNameSize(), 0xff, 6U) - 6); // 6 = strlen(" (10)") + 1
1113 
1114     if (maxNameSize == 0 || ! isRunning())
1115         return sname.dup();
1116 
1117     sname.truncate(maxNameSize);
1118     sname.replace(':', '.'); // ':' is used in JACK1 to split client/port names
1119     sname.replace('/', '.'); // '/' is used by us for client name prefix
1120 
1121     for (uint i=0; i < pData->curPluginCount; ++i)
1122     {
1123         const CarlaPluginPtr plugin = pData->plugins[i].plugin;
1124         CARLA_SAFE_ASSERT_BREAK(plugin.use_count() > 0);
1125 
1126         // Check if unique name doesn't exist
1127         if (const char* const pluginName = plugin->getName())
1128         {
1129             if (sname != pluginName)
1130                 continue;
1131         }
1132 
1133         // Check if string has already been modified
1134         {
1135             const std::size_t len(sname.length());
1136 
1137             // 1 digit, ex: " (2)"
1138             if (len > 4 && sname[len-4] == ' ' && sname[len-3] == '(' && sname.isDigit(len-2) && sname[len-1] == ')')
1139             {
1140                 const int number = sname[len-2] - '0';
1141 
1142                 if (number == 9)
1143                 {
1144                     // next number is 10, 2 digits
1145                     sname.truncate(len-4);
1146                     sname += " (10)";
1147                     //sname.replace(" (9)", " (10)");
1148                 }
1149                 else
1150                     sname[len-2] = char('0' + number + 1);
1151 
1152                 continue;
1153             }
1154 
1155             // 2 digits, ex: " (11)"
1156             if (len > 5 && sname[len-5] == ' ' && sname[len-4] == '(' && sname.isDigit(len-3) && sname.isDigit(len-2) && sname[len-1] == ')')
1157             {
1158                 char n2 = sname[len-2];
1159                 char n3 = sname[len-3];
1160 
1161                 if (n2 == '9')
1162                 {
1163                     n2 = '0';
1164                     n3 = static_cast<char>(n3 + 1);
1165                 }
1166                 else
1167                     n2 = static_cast<char>(n2 + 1);
1168 
1169                 sname[len-2] = n2;
1170                 sname[len-3] = n3;
1171 
1172                 continue;
1173             }
1174         }
1175 
1176         // Modify string if not
1177         sname += " (2)";
1178     }
1179 
1180     return sname.dup();
1181 }
1182 
1183 // -----------------------------------------------------------------------
1184 // Project management
1185 
loadFile(const char * const filename)1186 bool CarlaEngine::loadFile(const char* const filename)
1187 {
1188     CARLA_SAFE_ASSERT_RETURN_ERR(pData->isIdling == 0, "An operation is still being processed, please wait for it to finish");
1189     CARLA_SAFE_ASSERT_RETURN_ERR(filename != nullptr && filename[0] != '\0', "Invalid filename");
1190     carla_debug("CarlaEngine::loadFile(\"%s\")", filename);
1191 
1192     const String jfilename = String(CharPointer_UTF8(filename));
1193     File file(jfilename);
1194     CARLA_SAFE_ASSERT_RETURN_ERR(file.exists(), "Requested file does not exist or is not a readable");
1195 
1196     CarlaString baseName(file.getFileNameWithoutExtension().toRawUTF8());
1197     CarlaString extension(file.getFileExtension().replace(".","").toLowerCase().toRawUTF8());
1198 
1199     const uint curPluginId(pData->nextPluginId < pData->curPluginCount ? pData->nextPluginId : pData->curPluginCount);
1200 
1201     // -------------------------------------------------------------------
1202     // NOTE: please keep in sync with carla_get_supported_file_extensions!!
1203 
1204     if (extension == "carxp" || extension == "carxs")
1205         return loadProject(filename, false);
1206 
1207     // -------------------------------------------------------------------
1208 
1209     if (extension == "dls")
1210         return addPlugin(PLUGIN_DLS, filename, baseName, baseName, 0, nullptr);
1211 
1212     if (extension == "gig")
1213         return addPlugin(PLUGIN_GIG, filename, baseName, baseName, 0, nullptr);
1214 
1215     if (extension == "sf2" || extension == "sf3")
1216         return addPlugin(PLUGIN_SF2, filename, baseName, baseName, 0, nullptr);
1217 
1218     if (extension == "sfz")
1219         return addPlugin(PLUGIN_SFZ, filename, baseName, baseName, 0, nullptr);
1220 
1221     // -------------------------------------------------------------------
1222 
1223     if (
1224         extension == "mp3"  ||
1225 #ifdef HAVE_SNDFILE
1226         extension == "aif"  ||
1227         extension == "aifc" ||
1228         extension == "aiff" ||
1229         extension == "au"   ||
1230         extension == "bwf"  ||
1231         extension == "flac" ||
1232         extension == "htk"  ||
1233         extension == "iff"  ||
1234         extension == "mat4" ||
1235         extension == "mat5" ||
1236         extension == "oga"  ||
1237         extension == "ogg"  ||
1238         extension == "opus" ||
1239         extension == "paf"  ||
1240         extension == "pvf"  ||
1241         extension == "pvf5" ||
1242         extension == "sd2"  ||
1243         extension == "sf"   ||
1244         extension == "snd"  ||
1245         extension == "svx"  ||
1246         extension == "vcc"  ||
1247         extension == "w64"  ||
1248         extension == "wav"  ||
1249         extension == "xi"   ||
1250 #endif
1251 #ifdef HAVE_FFMPEG
1252         extension == "3g2" ||
1253         extension == "3gp" ||
1254         extension == "aac" ||
1255         extension == "ac3" ||
1256         extension == "amr" ||
1257         extension == "ape" ||
1258         extension == "mp2" ||
1259         extension == "mpc" ||
1260         extension == "wma" ||
1261 # ifndef HAVE_SNDFILE
1262         // FFmpeg without sndfile
1263         extension == "flac" ||
1264         extension == "oga"  ||
1265         extension == "ogg"  ||
1266         extension == "w64"  ||
1267         extension == "wav"  ||
1268 # endif
1269 #endif
1270         false
1271        )
1272     {
1273         if (addPlugin(PLUGIN_INTERNAL, nullptr, baseName, "audiofile", 0, nullptr))
1274         {
1275             if (const CarlaPluginPtr plugin = getPlugin(curPluginId))
1276                 plugin->setCustomData(CUSTOM_DATA_TYPE_STRING, "file", filename, true);
1277             return true;
1278         }
1279         return false;
1280     }
1281 
1282     // -------------------------------------------------------------------
1283 
1284     if (extension == "mid" || extension == "midi")
1285     {
1286         if (addPlugin(PLUGIN_INTERNAL, nullptr, baseName, "midifile", 0, nullptr))
1287         {
1288             if (const CarlaPluginPtr plugin = getPlugin(curPluginId))
1289                 plugin->setCustomData(CUSTOM_DATA_TYPE_STRING, "file", filename, true);
1290             return true;
1291         }
1292         return false;
1293     }
1294 
1295     // -------------------------------------------------------------------
1296     // ZynAddSubFX
1297 
1298     if (extension == "xmz" || extension == "xiz")
1299     {
1300 #ifdef HAVE_ZYN_DEPS
1301         CarlaString nicerName("Zyn - ");
1302 
1303         const std::size_t sep(baseName.find('-')+1);
1304 
1305         if (sep < baseName.length())
1306             nicerName += baseName.buffer()+sep;
1307         else
1308             nicerName += baseName;
1309 
1310         if (addPlugin(PLUGIN_INTERNAL, nullptr, nicerName, "zynaddsubfx", 0, nullptr))
1311         {
1312             callback(true, true, ENGINE_CALLBACK_UI_STATE_CHANGED, curPluginId, 0, 0, 0, 0.0f, nullptr);
1313 
1314             if (const CarlaPluginPtr plugin = getPlugin(curPluginId))
1315             {
1316                 const char* const ext = (extension == "xmz") ? "CarlaAlternateFile1" : "CarlaAlternateFile2";
1317                 plugin->setCustomData(CUSTOM_DATA_TYPE_STRING, ext, filename, true);
1318             }
1319 
1320             return true;
1321         }
1322         return false;
1323 #else
1324         setLastError("This Carla build does not have ZynAddSubFX support");
1325         return false;
1326 #endif
1327     }
1328 
1329     // -------------------------------------------------------------------
1330     // Direct plugin binaries
1331 
1332 #ifdef CARLA_OS_MAC
1333     if (extension == "vst")
1334         return addPlugin(PLUGIN_VST2, filename, nullptr, nullptr, 0, nullptr);
1335 #else
1336     if (extension == "dll" || extension == "so")
1337         return addPlugin(getBinaryTypeFromFile(filename), PLUGIN_VST2, filename, nullptr, nullptr, 0, nullptr);
1338 #endif
1339 
1340 #ifdef USING_JUCE
1341     if (extension == "vst3")
1342         return addPlugin(getBinaryTypeFromFile(filename), PLUGIN_VST3, filename, nullptr, nullptr, 0, nullptr);
1343 #endif
1344 
1345     // -------------------------------------------------------------------
1346 
1347     setLastError("Unknown file extension");
1348     return false;
1349 }
1350 
loadProject(const char * const filename,const bool setAsCurrentProject)1351 bool CarlaEngine::loadProject(const char* const filename, const bool setAsCurrentProject)
1352 {
1353     CARLA_SAFE_ASSERT_RETURN_ERR(pData->isIdling == 0, "An operation is still being processed, please wait for it to finish");
1354     CARLA_SAFE_ASSERT_RETURN_ERR(filename != nullptr && filename[0] != '\0', "Invalid filename");
1355     carla_debug("CarlaEngine::loadProject(\"%s\")", filename);
1356 
1357     const String jfilename = String(CharPointer_UTF8(filename));
1358     const File file(jfilename);
1359     CARLA_SAFE_ASSERT_RETURN_ERR(file.existsAsFile(), "Requested file does not exist or is not a readable file");
1360 
1361     if (setAsCurrentProject)
1362     {
1363 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1364         if (pData->currentProjectFilename != filename)
1365         {
1366             pData->currentProjectFilename = filename;
1367 
1368             bool found;
1369             const size_t r = pData->currentProjectFilename.rfind(CARLA_OS_SEP, &found);
1370 
1371             if (found)
1372             {
1373                 pData->currentProjectFolder = filename;
1374                 pData->currentProjectFolder[r] = '\0';
1375             }
1376             else
1377             {
1378                 pData->currentProjectFolder.clear();
1379             }
1380         }
1381 #endif
1382     }
1383 
1384     XmlDocument xml(file);
1385     return loadProjectInternal(xml, !setAsCurrentProject);
1386 }
1387 
saveProject(const char * const filename,const bool setAsCurrentProject)1388 bool CarlaEngine::saveProject(const char* const filename, const bool setAsCurrentProject)
1389 {
1390     CARLA_SAFE_ASSERT_RETURN_ERR(filename != nullptr && filename[0] != '\0', "Invalid filename");
1391     carla_debug("CarlaEngine::saveProject(\"%s\")", filename);
1392 
1393     if (setAsCurrentProject)
1394     {
1395 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1396         if (pData->currentProjectFilename != filename)
1397         {
1398             pData->currentProjectFilename = filename;
1399 
1400             bool found;
1401             const size_t r = pData->currentProjectFilename.rfind(CARLA_OS_SEP, &found);
1402 
1403             if (found)
1404             {
1405                 pData->currentProjectFolder = filename;
1406                 pData->currentProjectFolder[r] = '\0';
1407             }
1408             else
1409             {
1410                 pData->currentProjectFolder.clear();
1411             }
1412         }
1413 #endif
1414     }
1415 
1416     MemoryOutputStream out;
1417     saveProjectInternal(out);
1418 
1419     const String jfilename = String(CharPointer_UTF8(filename));
1420     File file(jfilename);
1421 
1422     if (file.replaceWithData(out.getData(), out.getDataSize()))
1423         return true;
1424 
1425     setLastError("Failed to write file");
1426     return false;
1427 }
1428 
1429 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
getCurrentProjectFolder() const1430 const char* CarlaEngine::getCurrentProjectFolder() const noexcept
1431 {
1432     return pData->currentProjectFolder.isNotEmpty() ? pData->currentProjectFolder.buffer()
1433                                                     : nullptr;
1434 }
1435 
getCurrentProjectFilename() const1436 const char* CarlaEngine::getCurrentProjectFilename() const noexcept
1437 {
1438     return pData->currentProjectFilename;
1439 }
1440 
clearCurrentProjectFilename()1441 void CarlaEngine::clearCurrentProjectFilename() noexcept
1442 {
1443     pData->currentProjectFilename.clear();
1444     pData->currentProjectFolder.clear();
1445 }
1446 #endif
1447 
1448 // -----------------------------------------------------------------------
1449 // Information (base)
1450 
getBufferSize() const1451 uint32_t CarlaEngine::getBufferSize() const noexcept
1452 {
1453     return pData->bufferSize;
1454 }
1455 
getSampleRate() const1456 double CarlaEngine::getSampleRate() const noexcept
1457 {
1458     return pData->sampleRate;
1459 }
1460 
getName() const1461 const char* CarlaEngine::getName() const noexcept
1462 {
1463     return pData->name;
1464 }
1465 
getProccessMode() const1466 EngineProcessMode CarlaEngine::getProccessMode() const noexcept
1467 {
1468     return pData->options.processMode;
1469 }
1470 
getOptions() const1471 const EngineOptions& CarlaEngine::getOptions() const noexcept
1472 {
1473     return pData->options;
1474 }
1475 
getTimeInfo() const1476 EngineTimeInfo CarlaEngine::getTimeInfo() const noexcept
1477 {
1478     return pData->timeInfo;
1479 }
1480 
1481 // -----------------------------------------------------------------------
1482 // Information (peaks)
1483 
getPeaks(const uint pluginId) const1484 const float* CarlaEngine::getPeaks(const uint pluginId) const noexcept
1485 {
1486     static const float kFallback[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
1487 
1488     if (pluginId == MAIN_CARLA_PLUGIN_ID)
1489     {
1490         // get peak from first plugin, if available
1491         if (const uint count = pData->curPluginCount)
1492         {
1493             pData->peaks[0] = pData->plugins[0].peaks[0];
1494             pData->peaks[1] = pData->plugins[0].peaks[1];
1495             pData->peaks[2] = pData->plugins[count-1].peaks[2];
1496             pData->peaks[3] = pData->plugins[count-1].peaks[3];
1497         }
1498         else
1499         {
1500             carla_zeroFloats(pData->peaks, 4);
1501         }
1502 
1503         return pData->peaks;
1504     }
1505 
1506     CARLA_SAFE_ASSERT_RETURN(pluginId < pData->curPluginCount, kFallback);
1507 
1508     return pData->plugins[pluginId].peaks;
1509 }
1510 
getInputPeak(const uint pluginId,const bool isLeft) const1511 float CarlaEngine::getInputPeak(const uint pluginId, const bool isLeft) const noexcept
1512 {
1513     if (pluginId == MAIN_CARLA_PLUGIN_ID)
1514     {
1515         // get peak from first plugin, if available
1516         if (pData->curPluginCount > 0)
1517             return pData->plugins[0].peaks[isLeft ? 0 : 1];
1518         return 0.0f;
1519     }
1520 
1521     CARLA_SAFE_ASSERT_RETURN(pluginId < pData->curPluginCount, 0.0f);
1522 
1523     return pData->plugins[pluginId].peaks[isLeft ? 0 : 1];
1524 }
1525 
getOutputPeak(const uint pluginId,const bool isLeft) const1526 float CarlaEngine::getOutputPeak(const uint pluginId, const bool isLeft) const noexcept
1527 {
1528     if (pluginId == MAIN_CARLA_PLUGIN_ID)
1529     {
1530         // get peak from last plugin, if available
1531         if (pData->curPluginCount > 0)
1532             return pData->plugins[pData->curPluginCount-1].peaks[isLeft ? 2 : 3];
1533         return 0.0f;
1534     }
1535 
1536     CARLA_SAFE_ASSERT_RETURN(pluginId < pData->curPluginCount, 0.0f);
1537 
1538     return pData->plugins[pluginId].peaks[isLeft ? 2 : 3];
1539 }
1540 
1541 // -----------------------------------------------------------------------
1542 // Callback
1543 
callback(const bool sendHost,const bool sendOSC,const EngineCallbackOpcode action,const uint pluginId,const int value1,const int value2,const int value3,const float valuef,const char * const valueStr)1544 void CarlaEngine::callback(const bool sendHost, const bool sendOSC,
1545                            const EngineCallbackOpcode action, const uint pluginId,
1546                            const int value1, const int value2, const int value3,
1547                            const float valuef, const char* const valueStr) noexcept
1548 {
1549 #ifdef DEBUG
1550     if (pData->isIdling)
1551         carla_stdout("CarlaEngine::callback [while idling] (%s, %s, %i:%s, %i, %i, %i, %i, %f, \"%s\")",
1552                      bool2str(sendHost), bool2str(sendOSC),
1553                      action, EngineCallbackOpcode2Str(action), pluginId, value1, value2, value3,
1554                      static_cast<double>(valuef), valueStr);
1555     else if (action != ENGINE_CALLBACK_IDLE && action != ENGINE_CALLBACK_NOTE_ON && action != ENGINE_CALLBACK_NOTE_OFF)
1556         carla_debug("CarlaEngine::callback(%s, %s, %i:%s, %i, %i, %i, %i, %f, \"%s\")",
1557                     bool2str(sendHost), bool2str(sendOSC),
1558                     action, EngineCallbackOpcode2Str(action), pluginId, value1, value2, value3,
1559                     static_cast<double>(valuef), valueStr);
1560 #endif
1561 
1562     if (sendHost && pData->callback != nullptr)
1563     {
1564         if (action == ENGINE_CALLBACK_IDLE)
1565             ++pData->isIdling;
1566 
1567         try {
1568             pData->callback(pData->callbackPtr, action, pluginId, value1, value2, value3, valuef, valueStr);
1569         } CARLA_SAFE_EXCEPTION("callback")
1570 
1571         if (action == ENGINE_CALLBACK_IDLE)
1572             --pData->isIdling;
1573     }
1574 
1575     if (sendOSC)
1576     {
1577 #if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE)
1578         if (pData->osc.isControlRegisteredForTCP())
1579         {
1580             switch (action)
1581             {
1582             case ENGINE_CALLBACK_RELOAD_INFO:
1583             {
1584                 CarlaPluginPtr plugin = pData->plugins[pluginId].plugin;
1585                 CARLA_SAFE_ASSERT_BREAK(plugin != nullptr);
1586 
1587                 pData->osc.sendPluginInfo(plugin);
1588                 break;
1589             }
1590 
1591             case ENGINE_CALLBACK_RELOAD_PARAMETERS:
1592             {
1593                 CarlaPluginPtr plugin = pData->plugins[pluginId].plugin;
1594                 CARLA_SAFE_ASSERT_BREAK(plugin != nullptr);
1595 
1596                 pData->osc.sendPluginPortCount(plugin);
1597 
1598                 if (const uint32_t count = plugin->getParameterCount())
1599                 {
1600                     for (uint32_t i=0; i<count; ++i)
1601                       pData->osc.sendPluginParameterInfo(plugin, i);
1602                 }
1603                 break;
1604             }
1605 
1606             case ENGINE_CALLBACK_RELOAD_PROGRAMS:
1607             {
1608                 CarlaPluginPtr plugin = pData->plugins[pluginId].plugin;
1609                 CARLA_SAFE_ASSERT_BREAK(plugin != nullptr);
1610 
1611                 pData->osc.sendPluginProgramCount(plugin);
1612 
1613                 if (const uint32_t count = plugin->getProgramCount())
1614                 {
1615                     for (uint32_t i=0; i<count; ++i)
1616                       pData->osc.sendPluginProgram(plugin, i);
1617                 }
1618 
1619                 if (const uint32_t count = plugin->getMidiProgramCount())
1620                 {
1621                     for (uint32_t i=0; i<count; ++i)
1622                       pData->osc.sendPluginMidiProgram(plugin, i);
1623                 }
1624                 break;
1625             }
1626 
1627             case ENGINE_CALLBACK_PLUGIN_ADDED:
1628             case ENGINE_CALLBACK_RELOAD_ALL:
1629             {
1630                 CarlaPluginPtr plugin = pData->plugins[pluginId].plugin;
1631                 CARLA_SAFE_ASSERT_BREAK(plugin != nullptr);
1632 
1633                 pData->osc.sendPluginInfo(plugin);
1634                 pData->osc.sendPluginPortCount(plugin);
1635                 pData->osc.sendPluginDataCount(plugin);
1636 
1637                 if (const uint32_t count = plugin->getParameterCount())
1638                 {
1639                     for (uint32_t i=0; i<count; ++i)
1640                       pData->osc.sendPluginParameterInfo(plugin, i);
1641                 }
1642 
1643                 if (const uint32_t count = plugin->getProgramCount())
1644                 {
1645                     for (uint32_t i=0; i<count; ++i)
1646                       pData->osc.sendPluginProgram(plugin, i);
1647                 }
1648 
1649                 if (const uint32_t count = plugin->getMidiProgramCount())
1650                 {
1651                     for (uint32_t i=0; i<count; ++i)
1652                       pData->osc.sendPluginMidiProgram(plugin, i);
1653                 }
1654 
1655                 if (const uint32_t count = plugin->getCustomDataCount())
1656                 {
1657                     for (uint32_t i=0; i<count; ++i)
1658                       pData->osc.sendPluginCustomData(plugin, i);
1659                 }
1660 
1661                 pData->osc.sendPluginInternalParameterValues(plugin);
1662                 break;
1663             }
1664 
1665             case ENGINE_CALLBACK_IDLE:
1666                 return;
1667 
1668             default:
1669                 break;
1670             }
1671 
1672             pData->osc.sendCallback(action, pluginId, value1, value2, value3, valuef, valueStr);
1673         }
1674 #endif
1675     }
1676 }
1677 
setCallback(const EngineCallbackFunc func,void * const ptr)1678 void CarlaEngine::setCallback(const EngineCallbackFunc func, void* const ptr) noexcept
1679 {
1680     carla_debug("CarlaEngine::setCallback(%p, %p)", func, ptr);
1681 
1682     pData->callback    = func;
1683     pData->callbackPtr = ptr;
1684 }
1685 
1686 // -----------------------------------------------------------------------
1687 // File Callback
1688 
runFileCallback(const FileCallbackOpcode action,const bool isDir,const char * const title,const char * const filter)1689 const char* CarlaEngine::runFileCallback(const FileCallbackOpcode action, const bool isDir, const char* const title, const char* const filter) noexcept
1690 {
1691     CARLA_SAFE_ASSERT_RETURN(title != nullptr && title[0] != '\0', nullptr);
1692     CARLA_SAFE_ASSERT_RETURN(filter != nullptr, nullptr);
1693     carla_debug("CarlaEngine::runFileCallback(%i:%s, %s, \"%s\", \"%s\")", action, FileCallbackOpcode2Str(action), bool2str(isDir), title, filter);
1694 
1695     const char* ret = nullptr;
1696 
1697     if (pData->fileCallback != nullptr)
1698     {
1699         try {
1700             ret = pData->fileCallback(pData->fileCallbackPtr, action, isDir, title, filter);
1701         } CARLA_SAFE_EXCEPTION("runFileCallback");
1702     }
1703 
1704     return ret;
1705 }
1706 
setFileCallback(const FileCallbackFunc func,void * const ptr)1707 void CarlaEngine::setFileCallback(const FileCallbackFunc func, void* const ptr) noexcept
1708 {
1709     carla_debug("CarlaEngine::setFileCallback(%p, %p)", func, ptr);
1710 
1711     pData->fileCallback    = func;
1712     pData->fileCallbackPtr = ptr;
1713 }
1714 
1715 // -----------------------------------------------------------------------
1716 // Transport
1717 
transportPlay()1718 void CarlaEngine::transportPlay() noexcept
1719 {
1720     pData->timeInfo.playing = true;
1721     pData->time.setNeedsReset();
1722 }
1723 
transportPause()1724 void CarlaEngine::transportPause() noexcept
1725 {
1726     if (pData->timeInfo.playing)
1727         pData->time.pause();
1728     else
1729         pData->time.setNeedsReset();
1730 }
1731 
transportBPM(const double bpm)1732 void CarlaEngine::transportBPM(const double bpm) noexcept
1733 {
1734     CARLA_SAFE_ASSERT_RETURN(bpm >= 20.0,)
1735 
1736     try {
1737         pData->time.setBPM(bpm);
1738     } CARLA_SAFE_EXCEPTION("CarlaEngine::transportBPM");
1739 }
1740 
transportRelocate(const uint64_t frame)1741 void CarlaEngine::transportRelocate(const uint64_t frame) noexcept
1742 {
1743     pData->time.relocate(frame);
1744 }
1745 
1746 // -----------------------------------------------------------------------
1747 // Error handling
1748 
getLastError() const1749 const char* CarlaEngine::getLastError() const noexcept
1750 {
1751     return pData->lastError;
1752 }
1753 
setLastError(const char * const error) const1754 void CarlaEngine::setLastError(const char* const error) const noexcept
1755 {
1756     pData->lastError = error;
1757 }
1758 
1759 // -----------------------------------------------------------------------
1760 // Misc
1761 
isAboutToClose() const1762 bool CarlaEngine::isAboutToClose() const noexcept
1763 {
1764     return pData->aboutToClose;
1765 }
1766 
setAboutToClose()1767 bool CarlaEngine::setAboutToClose() noexcept
1768 {
1769     carla_debug("CarlaEngine::setAboutToClose()");
1770 
1771     pData->aboutToClose = true;
1772 
1773     return (pData->isIdling == 0);
1774 }
1775 
1776 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
isLoadingProject() const1777 bool CarlaEngine::isLoadingProject() const noexcept
1778 {
1779     return pData->loadingProject;
1780 }
1781 #endif
1782 
setActionCanceled(const bool canceled)1783 void CarlaEngine::setActionCanceled(const bool canceled) noexcept
1784 {
1785     pData->actionCanceled = canceled;
1786 }
1787 
wasActionCanceled() const1788 bool CarlaEngine::wasActionCanceled() const noexcept
1789 {
1790     return pData->actionCanceled;
1791 }
1792 
1793 // -----------------------------------------------------------------------
1794 // Global options
1795 
setOption(const EngineOption option,const int value,const char * const valueStr)1796 void CarlaEngine::setOption(const EngineOption option, const int value, const char* const valueStr) noexcept
1797 {
1798     carla_debug("CarlaEngine::setOption(%i:%s, %i, \"%s\")", option, EngineOption2Str(option), value, valueStr);
1799 
1800     if (isRunning())
1801     {
1802         switch (option)
1803         {
1804         case ENGINE_OPTION_PROCESS_MODE:
1805         case ENGINE_OPTION_AUDIO_TRIPLE_BUFFER:
1806         case ENGINE_OPTION_AUDIO_DRIVER:
1807         case ENGINE_OPTION_AUDIO_DEVICE:
1808             return carla_stderr("CarlaEngine::setOption(%i:%s, %i, \"%s\") - Cannot set this option while engine is running!",
1809                                 option, EngineOption2Str(option), value, valueStr);
1810         default:
1811             break;
1812         }
1813     }
1814 
1815     // do not un-force stereo for rack mode
1816     if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK && option == ENGINE_OPTION_FORCE_STEREO && value != 0)
1817         return;
1818 
1819     switch (option)
1820     {
1821     case ENGINE_OPTION_DEBUG:
1822         break;
1823 
1824     case ENGINE_OPTION_PROCESS_MODE:
1825         CARLA_SAFE_ASSERT_RETURN(value >= ENGINE_PROCESS_MODE_SINGLE_CLIENT && value <= ENGINE_PROCESS_MODE_BRIDGE,);
1826         pData->options.processMode = static_cast<EngineProcessMode>(value);
1827         break;
1828 
1829     case ENGINE_OPTION_TRANSPORT_MODE:
1830         CARLA_SAFE_ASSERT_RETURN(value >= ENGINE_TRANSPORT_MODE_DISABLED && value <= ENGINE_TRANSPORT_MODE_BRIDGE,);
1831         CARLA_SAFE_ASSERT_RETURN(getType() == kEngineTypeJack || value != ENGINE_TRANSPORT_MODE_JACK,);
1832         pData->options.transportMode = static_cast<EngineTransportMode>(value);
1833         delete[] pData->options.transportExtra;
1834         if (value >= ENGINE_TRANSPORT_MODE_DISABLED && valueStr != nullptr)
1835             pData->options.transportExtra = carla_strdup_safe(valueStr);
1836         else
1837             pData->options.transportExtra = nullptr;
1838 
1839         pData->time.setNeedsReset();
1840 
1841 #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE)
1842         // enable link now if needed
1843         {
1844             const bool linkEnabled = pData->options.transportExtra != nullptr && std::strstr(pData->options.transportExtra, ":link:") != nullptr;
1845             pData->time.enableLink(linkEnabled);
1846         }
1847 #endif
1848         break;
1849 
1850     case ENGINE_OPTION_FORCE_STEREO:
1851         CARLA_SAFE_ASSERT_RETURN(value == 0 || value == 1,);
1852         pData->options.forceStereo = (value != 0);
1853         break;
1854 
1855     case ENGINE_OPTION_PREFER_PLUGIN_BRIDGES:
1856 #ifdef BUILD_BRIDGE_ALTERNATIVE_ARCH
1857         CARLA_SAFE_ASSERT_RETURN(value == 0,);
1858 #else
1859         CARLA_SAFE_ASSERT_RETURN(value == 0 || value == 1,);
1860 #endif
1861         pData->options.preferPluginBridges = (value != 0);
1862         break;
1863 
1864     case ENGINE_OPTION_PREFER_UI_BRIDGES:
1865         CARLA_SAFE_ASSERT_RETURN(value == 0 || value == 1,);
1866         pData->options.preferUiBridges = (value != 0);
1867         break;
1868 
1869     case ENGINE_OPTION_UIS_ALWAYS_ON_TOP:
1870         CARLA_SAFE_ASSERT_RETURN(value == 0 || value == 1,);
1871         pData->options.uisAlwaysOnTop = (value != 0);
1872         break;
1873 
1874     case ENGINE_OPTION_MAX_PARAMETERS:
1875         CARLA_SAFE_ASSERT_RETURN(value >= 0,);
1876         pData->options.maxParameters = static_cast<uint>(value);
1877         break;
1878 
1879     case ENGINE_OPTION_RESET_XRUNS:
1880         CARLA_SAFE_ASSERT_RETURN(value == 0 || value == 1,);
1881         pData->options.resetXruns = (value != 0);
1882         break;
1883 
1884     case ENGINE_OPTION_UI_BRIDGES_TIMEOUT:
1885         CARLA_SAFE_ASSERT_RETURN(value >= 0,);
1886         pData->options.uiBridgesTimeout = static_cast<uint>(value);
1887         break;
1888 
1889     case ENGINE_OPTION_AUDIO_BUFFER_SIZE:
1890         CARLA_SAFE_ASSERT_RETURN(value >= 8,);
1891         pData->options.audioBufferSize = static_cast<uint>(value);
1892         break;
1893 
1894     case ENGINE_OPTION_AUDIO_SAMPLE_RATE:
1895         CARLA_SAFE_ASSERT_RETURN(value >= 22050,);
1896         pData->options.audioSampleRate = static_cast<uint>(value);
1897         break;
1898 
1899     case ENGINE_OPTION_AUDIO_TRIPLE_BUFFER:
1900         CARLA_SAFE_ASSERT_RETURN(value == 0 || value == 1,);
1901         pData->options.audioTripleBuffer = (value != 0);
1902         break;
1903 
1904     case ENGINE_OPTION_AUDIO_DRIVER:
1905         CARLA_SAFE_ASSERT_RETURN(valueStr != nullptr,);
1906 
1907         if (pData->options.audioDriver != nullptr)
1908             delete[] pData->options.audioDriver;
1909 
1910         pData->options.audioDriver = carla_strdup_safe(valueStr);
1911         break;
1912 
1913     case ENGINE_OPTION_AUDIO_DEVICE:
1914         CARLA_SAFE_ASSERT_RETURN(valueStr != nullptr,);
1915 
1916         if (pData->options.audioDevice != nullptr)
1917             delete[] pData->options.audioDevice;
1918 
1919         pData->options.audioDevice = carla_strdup_safe(valueStr);
1920         break;
1921 
1922 #ifndef BUILD_BRIDGE
1923     case ENGINE_OPTION_OSC_ENABLED:
1924         CARLA_SAFE_ASSERT_RETURN(value == 0 || value == 1,);
1925         pData->options.oscEnabled = (value != 0);
1926         break;
1927 
1928     case ENGINE_OPTION_OSC_PORT_TCP:
1929         CARLA_SAFE_ASSERT_RETURN(value <= 0 || value >= 1024,);
1930         pData->options.oscPortTCP = value;
1931         break;
1932 
1933     case ENGINE_OPTION_OSC_PORT_UDP:
1934         CARLA_SAFE_ASSERT_RETURN(value <= 0 || value >= 1024,);
1935         pData->options.oscPortUDP = value;
1936         break;
1937 #endif
1938 
1939     case ENGINE_OPTION_FILE_PATH:
1940         CARLA_SAFE_ASSERT_RETURN(value > FILE_NONE,);
1941         CARLA_SAFE_ASSERT_RETURN(value <= FILE_MIDI,);
1942 
1943         switch (value)
1944         {
1945         case FILE_AUDIO:
1946             if (pData->options.pathAudio != nullptr)
1947                 delete[] pData->options.pathAudio;
1948             if (valueStr != nullptr)
1949                 pData->options.pathAudio = carla_strdup_safe(valueStr);
1950             else
1951                 pData->options.pathAudio = nullptr;
1952             break;
1953         case FILE_MIDI:
1954             if (pData->options.pathMIDI != nullptr)
1955                 delete[] pData->options.pathMIDI;
1956             if (valueStr != nullptr)
1957                 pData->options.pathMIDI = carla_strdup_safe(valueStr);
1958             else
1959                 pData->options.pathMIDI = nullptr;
1960             break;
1961         default:
1962             return carla_stderr("CarlaEngine::setOption(%i:%s, %i, \"%s\") - Invalid file type",
1963                                 option, EngineOption2Str(option), value, valueStr);
1964             break;
1965         }
1966         break;
1967     case ENGINE_OPTION_PLUGIN_PATH:
1968         CARLA_SAFE_ASSERT_RETURN(value > PLUGIN_NONE,);
1969         CARLA_SAFE_ASSERT_RETURN(value <= PLUGIN_SFZ,);
1970 
1971         switch (value)
1972         {
1973         case PLUGIN_LADSPA:
1974             if (pData->options.pathLADSPA != nullptr)
1975                 delete[] pData->options.pathLADSPA;
1976             if (valueStr != nullptr)
1977                 pData->options.pathLADSPA = carla_strdup_safe(valueStr);
1978             else
1979                 pData->options.pathLADSPA = nullptr;
1980             break;
1981         case PLUGIN_DSSI:
1982             if (pData->options.pathDSSI != nullptr)
1983                 delete[] pData->options.pathDSSI;
1984             if (valueStr != nullptr)
1985                 pData->options.pathDSSI = carla_strdup_safe(valueStr);
1986             else
1987                 pData->options.pathDSSI = nullptr;
1988             break;
1989         case PLUGIN_LV2:
1990             if (pData->options.pathLV2 != nullptr)
1991                 delete[] pData->options.pathLV2;
1992             if (valueStr != nullptr)
1993                 pData->options.pathLV2 = carla_strdup_safe(valueStr);
1994             else
1995                 pData->options.pathLV2 = nullptr;
1996             break;
1997         case PLUGIN_VST2:
1998             if (pData->options.pathVST2 != nullptr)
1999                 delete[] pData->options.pathVST2;
2000             if (valueStr != nullptr)
2001                 pData->options.pathVST2 = carla_strdup_safe(valueStr);
2002             else
2003                 pData->options.pathVST2 = nullptr;
2004             break;
2005         case PLUGIN_VST3:
2006             if (pData->options.pathVST3 != nullptr)
2007                 delete[] pData->options.pathVST3;
2008             if (valueStr != nullptr)
2009                 pData->options.pathVST3 = carla_strdup_safe(valueStr);
2010             else
2011                 pData->options.pathVST3 = nullptr;
2012             break;
2013         case PLUGIN_SF2:
2014             if (pData->options.pathSF2 != nullptr)
2015                 delete[] pData->options.pathSF2;
2016             if (valueStr != nullptr)
2017                 pData->options.pathSF2 = carla_strdup_safe(valueStr);
2018             else
2019                 pData->options.pathSF2 = nullptr;
2020             break;
2021         case PLUGIN_SFZ:
2022             if (pData->options.pathSFZ != nullptr)
2023                 delete[] pData->options.pathSFZ;
2024             if (valueStr != nullptr)
2025                 pData->options.pathSFZ = carla_strdup_safe(valueStr);
2026             else
2027                 pData->options.pathSFZ = nullptr;
2028             break;
2029         default:
2030             return carla_stderr("CarlaEngine::setOption(%i:%s, %i, \"%s\") - Invalid plugin type",
2031                                 option, EngineOption2Str(option), value, valueStr);
2032             break;
2033         }
2034         break;
2035 
2036     case ENGINE_OPTION_PATH_BINARIES:
2037         CARLA_SAFE_ASSERT_RETURN(valueStr != nullptr && valueStr[0] != '\0',);
2038 
2039         if (pData->options.binaryDir != nullptr)
2040             delete[] pData->options.binaryDir;
2041 
2042         pData->options.binaryDir = carla_strdup_safe(valueStr);
2043         break;
2044 
2045     case ENGINE_OPTION_PATH_RESOURCES:
2046         CARLA_SAFE_ASSERT_RETURN(valueStr != nullptr && valueStr[0] != '\0',);
2047 
2048         if (pData->options.resourceDir != nullptr)
2049             delete[] pData->options.resourceDir;
2050 
2051         pData->options.resourceDir = carla_strdup_safe(valueStr);
2052         break;
2053 
2054     case ENGINE_OPTION_PREVENT_BAD_BEHAVIOUR: {
2055         CARLA_SAFE_ASSERT_RETURN(pData->options.binaryDir != nullptr && pData->options.binaryDir[0] != '\0',);
2056 
2057 #ifdef CARLA_OS_LINUX
2058         const ScopedEngineEnvironmentLocker _seel(this);
2059 
2060         if (value != 0)
2061         {
2062             CarlaString interposerPath(CarlaString(pData->options.binaryDir) + "/libcarla_interposer-safe.so");
2063             ::setenv("LD_PRELOAD", interposerPath.buffer(), 1);
2064         }
2065         else
2066         {
2067             ::unsetenv("LD_PRELOAD");
2068         }
2069 #endif
2070     }   break;
2071 
2072     case ENGINE_OPTION_FRONTEND_BACKGROUND_COLOR:
2073         pData->options.bgColor = static_cast<uint>(value);
2074         break;
2075 
2076     case ENGINE_OPTION_FRONTEND_FOREGROUND_COLOR:
2077         pData->options.fgColor = static_cast<uint>(value);
2078         break;
2079 
2080     case ENGINE_OPTION_FRONTEND_UI_SCALE:
2081         CARLA_SAFE_ASSERT_RETURN(value > 0,);
2082         pData->options.uiScale = static_cast<float>(value) / 1000;
2083         break;
2084 
2085     case ENGINE_OPTION_FRONTEND_WIN_ID: {
2086         CARLA_SAFE_ASSERT_RETURN(valueStr != nullptr && valueStr[0] != '\0',);
2087         const long long winId(std::strtoll(valueStr, nullptr, 16));
2088         CARLA_SAFE_ASSERT_RETURN(winId >= 0,);
2089         pData->options.frontendWinId = static_cast<uintptr_t>(winId);
2090     }   break;
2091 
2092 #if !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH) && !defined(CARLA_OS_WIN)
2093     case ENGINE_OPTION_WINE_EXECUTABLE:
2094         CARLA_SAFE_ASSERT_RETURN(valueStr != nullptr && valueStr[0] != '\0',);
2095 
2096         if (pData->options.wine.executable != nullptr)
2097             delete[] pData->options.wine.executable;
2098 
2099         pData->options.wine.executable = carla_strdup_safe(valueStr);
2100         break;
2101 
2102     case ENGINE_OPTION_WINE_AUTO_PREFIX:
2103         CARLA_SAFE_ASSERT_RETURN(value == 0 || value == 1,);
2104         pData->options.wine.autoPrefix = (value != 0);
2105         break;
2106 
2107     case ENGINE_OPTION_WINE_FALLBACK_PREFIX:
2108         CARLA_SAFE_ASSERT_RETURN(valueStr != nullptr && valueStr[0] != '\0',);
2109 
2110         if (pData->options.wine.fallbackPrefix != nullptr)
2111             delete[] pData->options.wine.fallbackPrefix;
2112 
2113         pData->options.wine.fallbackPrefix = carla_strdup_safe(valueStr);
2114         break;
2115 
2116     case ENGINE_OPTION_WINE_RT_PRIO_ENABLED:
2117         CARLA_SAFE_ASSERT_RETURN(value == 0 || value == 1,);
2118         pData->options.wine.rtPrio = (value != 0);
2119         break;
2120 
2121     case ENGINE_OPTION_WINE_BASE_RT_PRIO:
2122         CARLA_SAFE_ASSERT_RETURN(value >= 1 && value <= 89,);
2123         pData->options.wine.baseRtPrio = value;
2124         break;
2125 
2126     case ENGINE_OPTION_WINE_SERVER_RT_PRIO:
2127         CARLA_SAFE_ASSERT_RETURN(value >= 1 && value <= 99,);
2128         pData->options.wine.serverRtPrio = value;
2129         break;
2130 #endif
2131 
2132 #ifndef BUILD_BRIDGE
2133     case ENGINE_OPTION_DEBUG_CONSOLE_OUTPUT:
2134         break;
2135 #endif
2136 
2137     case ENGINE_OPTION_CLIENT_NAME_PREFIX:
2138         if (pData->options.clientNamePrefix != nullptr)
2139             delete[] pData->options.clientNamePrefix;
2140 
2141         pData->options.clientNamePrefix = valueStr != nullptr && valueStr[0] != '\0'
2142                                         ? carla_strdup_safe(valueStr)
2143                                         : nullptr;
2144         break;
2145 
2146     case ENGINE_OPTION_PLUGINS_ARE_STANDALONE:
2147         CARLA_SAFE_ASSERT_RETURN(value == 0 || value == 1,);
2148         pData->options.pluginsAreStandalone = (value != 0);
2149         break;
2150     }
2151 }
2152 
2153 #ifndef BUILD_BRIDGE
2154 // -----------------------------------------------------------------------
2155 // OSC Stuff
2156 
isOscControlRegistered() const2157 bool CarlaEngine::isOscControlRegistered() const noexcept
2158 {
2159 # ifdef HAVE_LIBLO
2160     return pData->osc.isControlRegisteredForTCP();
2161 # else
2162     return false;
2163 # endif
2164 }
2165 
getOscServerPathTCP() const2166 const char* CarlaEngine::getOscServerPathTCP() const noexcept
2167 {
2168 # ifdef HAVE_LIBLO
2169     return pData->osc.getServerPathTCP();
2170 # else
2171     return nullptr;
2172 # endif
2173 }
2174 
getOscServerPathUDP() const2175 const char* CarlaEngine::getOscServerPathUDP() const noexcept
2176 {
2177 # ifdef HAVE_LIBLO
2178     return pData->osc.getServerPathUDP();
2179 # else
2180     return nullptr;
2181 # endif
2182 }
2183 #endif
2184 
2185 // -----------------------------------------------------------------------
2186 // Internal stuff
2187 
bufferSizeChanged(const uint32_t newBufferSize)2188 void CarlaEngine::bufferSizeChanged(const uint32_t newBufferSize)
2189 {
2190     carla_debug("CarlaEngine::bufferSizeChanged(%i)", newBufferSize);
2191 
2192 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2193     if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK ||
2194         pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
2195     {
2196         pData->graph.setBufferSize(newBufferSize);
2197     }
2198 #endif
2199 
2200     pData->time.updateAudioValues(newBufferSize, pData->sampleRate);
2201 
2202     for (uint i=0; i < pData->curPluginCount; ++i)
2203     {
2204         if (const CarlaPluginPtr plugin = pData->plugins[i].plugin)
2205         {
2206             if (plugin->isEnabled() && plugin->tryLock(true))
2207             {
2208                 plugin->bufferSizeChanged(newBufferSize);
2209                 plugin->unlock();
2210             }
2211         }
2212     }
2213 
2214     callback(true, true, ENGINE_CALLBACK_BUFFER_SIZE_CHANGED, 0, static_cast<int>(newBufferSize), 0, 0, 0.0f, nullptr);
2215 }
2216 
sampleRateChanged(const double newSampleRate)2217 void CarlaEngine::sampleRateChanged(const double newSampleRate)
2218 {
2219     carla_debug("CarlaEngine::sampleRateChanged(%g)", newSampleRate);
2220 
2221 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2222     if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK ||
2223         pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
2224     {
2225         pData->graph.setSampleRate(newSampleRate);
2226     }
2227 #endif
2228 
2229     pData->time.updateAudioValues(pData->bufferSize, newSampleRate);
2230 
2231     for (uint i=0; i < pData->curPluginCount; ++i)
2232     {
2233         if (const CarlaPluginPtr plugin = pData->plugins[i].plugin)
2234         {
2235             if (plugin->isEnabled() && plugin->tryLock(true))
2236             {
2237                 plugin->sampleRateChanged(newSampleRate);
2238                 plugin->unlock();
2239             }
2240         }
2241     }
2242 
2243     callback(true, true, ENGINE_CALLBACK_SAMPLE_RATE_CHANGED, 0, 0, 0, 0, static_cast<float>(newSampleRate), nullptr);
2244 }
2245 
offlineModeChanged(const bool isOfflineNow)2246 void CarlaEngine::offlineModeChanged(const bool isOfflineNow)
2247 {
2248     carla_debug("CarlaEngine::offlineModeChanged(%s)", bool2str(isOfflineNow));
2249 
2250 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2251     if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK ||
2252         pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
2253     {
2254         pData->graph.setOffline(isOfflineNow);
2255     }
2256 #endif
2257 
2258     for (uint i=0; i < pData->curPluginCount; ++i)
2259     {
2260         if (const CarlaPluginPtr plugin = pData->plugins[i].plugin)
2261             if (plugin->isEnabled())
2262                 plugin->offlineModeChanged(isOfflineNow);
2263     }
2264 }
2265 
setPluginPeaksRT(const uint pluginId,float const inPeaks[2],float const outPeaks[2])2266 void CarlaEngine::setPluginPeaksRT(const uint pluginId, float const inPeaks[2], float const outPeaks[2]) noexcept
2267 {
2268     EnginePluginData& pluginData(pData->plugins[pluginId]);
2269 
2270     pluginData.peaks[0]  = inPeaks[0];
2271     pluginData.peaks[1]  = inPeaks[1];
2272     pluginData.peaks[2] = outPeaks[0];
2273     pluginData.peaks[3] = outPeaks[1];
2274 }
2275 
saveProjectInternal(water::MemoryOutputStream & outStream) const2276 void CarlaEngine::saveProjectInternal(water::MemoryOutputStream& outStream) const
2277 {
2278     // send initial prepareForSave first, giving time for bridges to act
2279     for (uint i=0; i < pData->curPluginCount; ++i)
2280     {
2281         if (const CarlaPluginPtr plugin = pData->plugins[i].plugin)
2282         {
2283             if (plugin->isEnabled())
2284             {
2285 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2286                 // deactivate bridge client-side ping check, since some plugins block during save
2287                 if (plugin->getHints() & PLUGIN_IS_BRIDGE)
2288                     plugin->setCustomData(CUSTOM_DATA_TYPE_STRING, "__CarlaPingOnOff__", "false", false);
2289 #endif
2290                 plugin->prepareForSave(false);
2291             }
2292         }
2293     }
2294 
2295     outStream << "<?xml version='1.0' encoding='UTF-8'?>\n";
2296     outStream << "<!DOCTYPE CARLA-PROJECT>\n";
2297     outStream << "<CARLA-PROJECT VERSION='" CARLA_VERSION_STRMIN "'";
2298 
2299 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2300     if (pData->ignoreClientPrefix)
2301         outStream << " IgnoreClientPrefix='true'";
2302 #endif
2303 
2304     outStream << ">\n";
2305 
2306     const bool isPlugin(getType() == kEngineTypePlugin);
2307     const EngineOptions& options(pData->options);
2308 
2309     {
2310         MemoryOutputStream outSettings(1024);
2311 
2312         outSettings << " <EngineSettings>\n";
2313 
2314         outSettings << "  <ForceStereo>"         << bool2str(options.forceStereo)         << "</ForceStereo>\n";
2315         outSettings << "  <PreferPluginBridges>" << bool2str(options.preferPluginBridges) << "</PreferPluginBridges>\n";
2316         outSettings << "  <PreferUiBridges>"     << bool2str(options.preferUiBridges)     << "</PreferUiBridges>\n";
2317         outSettings << "  <UIsAlwaysOnTop>"      << bool2str(options.uisAlwaysOnTop)      << "</UIsAlwaysOnTop>\n";
2318 
2319         outSettings << "  <MaxParameters>"       << String(options.maxParameters)    << "</MaxParameters>\n";
2320         outSettings << "  <UIBridgesTimeout>"    << String(options.uiBridgesTimeout) << "</UIBridgesTimeout>\n";
2321 
2322         if (isPlugin)
2323         {
2324             outSettings << "  <LADSPA_PATH>" << xmlSafeString(options.pathLADSPA, true) << "</LADSPA_PATH>\n";
2325             outSettings << "  <DSSI_PATH>"   << xmlSafeString(options.pathDSSI,   true) << "</DSSI_PATH>\n";
2326             outSettings << "  <LV2_PATH>"    << xmlSafeString(options.pathLV2,    true) << "</LV2_PATH>\n";
2327             outSettings << "  <VST2_PATH>"   << xmlSafeString(options.pathVST2,   true) << "</VST2_PATH>\n";
2328             outSettings << "  <VST3_PATH>"   << xmlSafeString(options.pathVST3,   true) << "</VST3_PATH>\n";
2329             outSettings << "  <SF2_PATH>"    << xmlSafeString(options.pathSF2,    true) << "</SF2_PATH>\n";
2330             outSettings << "  <SFZ_PATH>"    << xmlSafeString(options.pathSFZ,    true) << "</SFZ_PATH>\n";
2331         }
2332 
2333         outSettings << " </EngineSettings>\n";
2334         outStream << outSettings;
2335     }
2336 
2337     if (pData->timeInfo.bbt.valid && ! isPlugin)
2338     {
2339         MemoryOutputStream outTransport(128);
2340 
2341         outTransport << "\n <Transport>\n";
2342         // outTransport << "  <BeatsPerBar>"    << pData->timeInfo.bbt.beatsPerBar    << "</BeatsPerBar>\n";
2343         outTransport << "  <BeatsPerMinute>" << pData->timeInfo.bbt.beatsPerMinute << "</BeatsPerMinute>\n";
2344         outTransport << " </Transport>\n";
2345         outStream << outTransport;
2346     }
2347 
2348     char strBuf[STR_MAX+1];
2349     carla_zeroChars(strBuf, STR_MAX+1);
2350 
2351     for (uint i=0; i < pData->curPluginCount; ++i)
2352     {
2353         if (const CarlaPluginPtr plugin = pData->plugins[i].plugin)
2354         {
2355             if (plugin->isEnabled())
2356             {
2357                 MemoryOutputStream outPlugin(4096), streamPlugin;
2358                 plugin->getStateSave(false).dumpToMemoryStream(streamPlugin);
2359 
2360                 outPlugin << "\n";
2361 
2362                 if (plugin->getRealName(strBuf))
2363                     outPlugin << " <!-- " << xmlSafeString(strBuf, true) << " -->\n";
2364 
2365                 outPlugin << " <Plugin>\n";
2366                 outPlugin << streamPlugin;
2367                 outPlugin << " </Plugin>\n";
2368                 outStream << outPlugin;
2369             }
2370         }
2371     }
2372 
2373 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2374     // tell bridges we're done saving
2375     for (uint i=0; i < pData->curPluginCount; ++i)
2376     {
2377         if (const CarlaPluginPtr plugin = pData->plugins[i].plugin)
2378             if (plugin->isEnabled() && (plugin->getHints() & PLUGIN_IS_BRIDGE) != 0)
2379                 plugin->setCustomData(CUSTOM_DATA_TYPE_STRING, "__CarlaPingOnOff__", "true", false);
2380     }
2381 
2382     // save internal connections
2383     if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
2384     {
2385         uint posCount = 0;
2386         const char* const* const patchbayConns = getPatchbayConnections(false);
2387         const PatchbayPosition* const patchbayPos = getPatchbayPositions(false, posCount);
2388 
2389         if (patchbayConns != nullptr || patchbayPos != nullptr)
2390         {
2391             MemoryOutputStream outPatchbay(2048);
2392 
2393             outPatchbay << "\n <Patchbay>\n";
2394 
2395             if (patchbayConns != nullptr)
2396             {
2397                 for (int i=0; patchbayConns[i] != nullptr && patchbayConns[i+1] != nullptr; ++i, ++i)
2398                 {
2399                     const char* const connSource(patchbayConns[i]);
2400                     const char* const connTarget(patchbayConns[i+1]);
2401 
2402                     CARLA_SAFE_ASSERT_CONTINUE(connSource != nullptr && connSource[0] != '\0');
2403                     CARLA_SAFE_ASSERT_CONTINUE(connTarget != nullptr && connTarget[0] != '\0');
2404 
2405                     outPatchbay << "  <Connection>\n";
2406                     outPatchbay << "   <Source>" << xmlSafeString(connSource, true) << "</Source>\n";
2407                     outPatchbay << "   <Target>" << xmlSafeString(connTarget, true) << "</Target>\n";
2408                     outPatchbay << "  </Connection>\n";
2409                 }
2410             }
2411 
2412             if (patchbayPos != nullptr && posCount != 0)
2413             {
2414                 outPatchbay << "  <Positions>\n";
2415 
2416                 for (uint i=0; i<posCount; ++i)
2417                 {
2418                     const PatchbayPosition& ppos(patchbayPos[i]);
2419 
2420                     CARLA_SAFE_ASSERT_CONTINUE(ppos.name != nullptr && ppos.name[0] != '\0');
2421 
2422                     outPatchbay << "   <Position x1=\"" << ppos.x1 << "\" y1=\"" << ppos.y1;
2423                     if (ppos.x2 != 0 || ppos.y2 != 0)
2424                         outPatchbay << "\" x2=\"" << ppos.x2 << "\" y2=\"" << ppos.y2;
2425                     if (ppos.pluginId >= 0)
2426                         outPatchbay << "\" pluginId=\"" << ppos.pluginId;
2427                     outPatchbay << "\">\n";
2428                     outPatchbay << "    <Name>" << xmlSafeString(ppos.name, true) << "</Name>\n";
2429                     outPatchbay << "   </Position>\n";
2430 
2431                     if (ppos.dealloc)
2432                         delete[] ppos.name;
2433                 }
2434 
2435                 outPatchbay << "  </Positions>\n";
2436             }
2437 
2438             outPatchbay << " </Patchbay>\n";
2439             outStream << outPatchbay;
2440 
2441             delete[] patchbayPos;
2442         }
2443 
2444     }
2445 
2446     // if we're running inside some session-manager (and using JACK), let them handle the connections
2447     bool saveExternalConnections, saveExternalPositions = true;
2448 
2449     /**/ if (isPlugin)
2450     {
2451         saveExternalConnections = false;
2452         saveExternalPositions = false;
2453     }
2454     else if (std::strcmp(getCurrentDriverName(), "JACK") != 0)
2455     {
2456         saveExternalConnections = true;
2457     }
2458     else if (std::getenv("CARLA_DONT_MANAGE_CONNECTIONS") != nullptr)
2459     {
2460         saveExternalConnections = false;
2461     }
2462     else
2463     {
2464         saveExternalConnections = true;
2465     }
2466 
2467     if (saveExternalConnections || saveExternalPositions)
2468     {
2469         uint posCount = 0;
2470         const char* const* const patchbayConns = saveExternalConnections
2471                                                ? getPatchbayConnections(true)
2472                                                : nullptr;
2473         const PatchbayPosition* const patchbayPos = saveExternalPositions
2474                                                   ? getPatchbayPositions(true, posCount)
2475                                                   : nullptr;
2476 
2477         if (patchbayConns != nullptr || patchbayPos != nullptr)
2478         {
2479             MemoryOutputStream outPatchbay(2048);
2480 
2481             outPatchbay << "\n <ExternalPatchbay>\n";
2482 
2483             if (patchbayConns != nullptr)
2484             {
2485                 for (int i=0; patchbayConns[i] != nullptr && patchbayConns[i+1] != nullptr; ++i, ++i )
2486                 {
2487                     const char* const connSource(patchbayConns[i]);
2488                     const char* const connTarget(patchbayConns[i+1]);
2489 
2490                     CARLA_SAFE_ASSERT_CONTINUE(connSource != nullptr && connSource[0] != '\0');
2491                     CARLA_SAFE_ASSERT_CONTINUE(connTarget != nullptr && connTarget[0] != '\0');
2492 
2493                     outPatchbay << "  <Connection>\n";
2494                     outPatchbay << "   <Source>" << xmlSafeString(connSource, true) << "</Source>\n";
2495                     outPatchbay << "   <Target>" << xmlSafeString(connTarget, true) << "</Target>\n";
2496                     outPatchbay << "  </Connection>\n";
2497                 }
2498             }
2499 
2500             if (patchbayPos != nullptr && posCount != 0)
2501             {
2502                 outPatchbay << "  <Positions>\n";
2503 
2504                 for (uint i=0; i<posCount; ++i)
2505                 {
2506                     const PatchbayPosition& ppos(patchbayPos[i]);
2507 
2508                     CARLA_SAFE_ASSERT_CONTINUE(ppos.name != nullptr && ppos.name[0] != '\0');
2509 
2510                     outPatchbay << "   <Position x1=\"" << ppos.x1 << "\" y1=\"" << ppos.y1;
2511                     if (ppos.x2 != 0 || ppos.y2 != 0)
2512                         outPatchbay << "\" x2=\"" << ppos.x2 << "\" y2=\"" << ppos.y2;
2513                     if (ppos.pluginId >= 0)
2514                         outPatchbay << "\" pluginId=\"" << ppos.pluginId;
2515                     outPatchbay << "\">\n";
2516                     outPatchbay << "    <Name>" << xmlSafeString(ppos.name, true) << "</Name>\n";
2517                     outPatchbay << "   </Position>\n";
2518 
2519                     if (ppos.dealloc)
2520                         delete[] ppos.name;
2521                 }
2522 
2523                 outPatchbay << "  </Positions>\n";
2524             }
2525 
2526             outPatchbay << " </ExternalPatchbay>\n";
2527             outStream << outPatchbay;
2528         }
2529     }
2530 #endif
2531 
2532     outStream << "</CARLA-PROJECT>\n";
2533 }
2534 
findBinaryInCustomPath(const char * const searchPath,const char * const binary)2535 static String findBinaryInCustomPath(const char* const searchPath, const char* const binary)
2536 {
2537     const StringArray searchPaths(StringArray::fromTokens(searchPath, CARLA_OS_SPLIT_STR, ""));
2538 
2539     // try direct filename first
2540     String jbinary(binary);
2541 
2542     // adjust for current platform
2543 #ifdef CARLA_OS_WIN
2544     if (jbinary[0] == '/')
2545         jbinary = "C:" + jbinary.replaceCharacter('/', '\\');
2546 #else
2547     if (jbinary[1] == ':' && (jbinary[2] == '\\' || jbinary[2] == '/'))
2548         jbinary = jbinary.substring(2).replaceCharacter('\\', '/');
2549 #endif
2550 
2551     String filename = File(jbinary).getFileName();
2552 
2553     int searchFlags = File::findFiles|File::ignoreHiddenFiles;
2554 #ifdef CARLA_OS_MAC
2555     if (filename.endsWithIgnoreCase(".vst") || filename.endsWithIgnoreCase(".vst3"))
2556         searchFlags |= File::findDirectories;
2557 #endif
2558 
2559     Array<File> results;
2560     for (const String *it=searchPaths.begin(), *end=searchPaths.end(); it != end; ++it)
2561     {
2562         const File path(*it);
2563 
2564         results.clear();
2565         path.findChildFiles(results, searchFlags, true, filename);
2566 
2567         if (results.size() > 0)
2568             return results.getFirst().getFullPathName();
2569     }
2570 
2571     // try changing extension
2572 #if defined(CARLA_OS_MAC)
2573     if (filename.endsWithIgnoreCase(".dll") || filename.endsWithIgnoreCase(".so"))
2574         filename = File(jbinary).getFileNameWithoutExtension() + ".dylib";
2575 #elif defined(CARLA_OS_WIN)
2576     if (filename.endsWithIgnoreCase(".dylib") || filename.endsWithIgnoreCase(".so"))
2577         filename = File(jbinary).getFileNameWithoutExtension() + ".dll";
2578 #else
2579     if (filename.endsWithIgnoreCase(".dll") || filename.endsWithIgnoreCase(".dylib"))
2580         filename = File(jbinary).getFileNameWithoutExtension() + ".so";
2581 #endif
2582     else
2583         return String();
2584 
2585     for (const String *it=searchPaths.begin(), *end=searchPaths.end(); it != end; ++it)
2586     {
2587         const File path(*it);
2588 
2589         results.clear();
2590         path.findChildFiles(results, searchFlags, true, filename);
2591 
2592         if (results.size() > 0)
2593             return results.getFirst().getFullPathName();
2594     }
2595 
2596     return String();
2597 }
2598 
loadProjectInternal(water::XmlDocument & xmlDoc,const bool alwaysLoadConnections)2599 bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc, const bool alwaysLoadConnections)
2600 {
2601     carla_debug("CarlaEngine::loadProjectInternal(%p, %s) - START", &xmlDoc, bool2str(alwaysLoadConnections));
2602 
2603     CarlaScopedPointer<XmlElement> xmlElement(xmlDoc.getDocumentElement(true));
2604     CARLA_SAFE_ASSERT_RETURN_ERR(xmlElement != nullptr, "Failed to parse project file");
2605 
2606     const String& xmlType(xmlElement->getTagName());
2607     const bool isPreset(xmlType.equalsIgnoreCase("carla-preset"));
2608 
2609     if (! (xmlType.equalsIgnoreCase("carla-project") || isPreset))
2610     {
2611         callback(true, true, ENGINE_CALLBACK_PROJECT_LOAD_FINISHED, 0, 0, 0, 0, 0.0f, nullptr);
2612         setLastError("Not a valid Carla project or preset file");
2613         return false;
2614     }
2615 
2616     pData->actionCanceled = false;
2617     callback(true, true, ENGINE_CALLBACK_CANCELABLE_ACTION, 0, 1, 0, 0, 0.0f, "Loading project");
2618 
2619 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2620     if (pData->options.clientNamePrefix != nullptr)
2621     {
2622         if (carla_isEqual(xmlElement->getDoubleAttribute("VERSION", 0.0), 2.0) ||
2623             xmlElement->getBoolAttribute("IgnoreClientPrefix", false))
2624         {
2625             carla_stdout("Loading project in compatibility mode, will ignore client name prefix");
2626             pData->ignoreClientPrefix = true;
2627             setOption(ENGINE_OPTION_CLIENT_NAME_PREFIX, 0, "");
2628         }
2629     }
2630 
2631     const CarlaScopedValueSetter<bool> csvs(pData->loadingProject, true, false);
2632 #endif
2633 
2634     // completely load file
2635     xmlElement = xmlDoc.getDocumentElement(false);
2636     CARLA_SAFE_ASSERT_RETURN_ERR(xmlElement != nullptr, "Failed to completely parse project file");
2637 
2638     if (pData->aboutToClose)
2639         return true;
2640 
2641     if (pData->actionCanceled)
2642     {
2643         setLastError("Project load canceled");
2644         return false;
2645     }
2646 
2647     callback(true, false, ENGINE_CALLBACK_IDLE, 0, 0, 0, 0, 0.0f, nullptr);
2648 
2649 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2650     const bool isMultiClient = pData->options.processMode == ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS;
2651     const bool isPatchbay    = pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY;
2652 #endif
2653     const bool isPlugin = getType() == kEngineTypePlugin;
2654 
2655     // load engine settings first of all
2656     if (XmlElement* const elem = isPreset ? nullptr : xmlElement->getChildByName("EngineSettings"))
2657     {
2658         for (XmlElement* settElem = elem->getFirstChildElement(); settElem != nullptr; settElem = settElem->getNextElement())
2659         {
2660             const String& tag(settElem->getTagName());
2661             const String  text(settElem->getAllSubText().trim());
2662 
2663             /** some settings might be incorrect or require extra work,
2664                 so we call setOption rather than modifying them direly */
2665 
2666             int option = -1;
2667             int value  = 0;
2668             const char* valueStr = nullptr;
2669 
2670             /**/ if (tag == "ForceStereo")
2671             {
2672                 option = ENGINE_OPTION_FORCE_STEREO;
2673                 value  = text == "true" ? 1 : 0;
2674             }
2675             else if (tag == "PreferPluginBridges")
2676             {
2677                 option = ENGINE_OPTION_PREFER_PLUGIN_BRIDGES;
2678                 value  = text == "true" ? 1 : 0;
2679             }
2680             else if (tag == "PreferUiBridges")
2681             {
2682                 option = ENGINE_OPTION_PREFER_UI_BRIDGES;
2683                 value  = text == "true" ? 1 : 0;
2684             }
2685             else if (tag == "UIsAlwaysOnTop")
2686             {
2687                 option = ENGINE_OPTION_UIS_ALWAYS_ON_TOP;
2688                 value  = text == "true" ? 1 : 0;
2689             }
2690             else if (tag == "MaxParameters")
2691             {
2692                 option = ENGINE_OPTION_MAX_PARAMETERS;
2693                 value  = text.getIntValue();
2694             }
2695             else if (tag == "UIBridgesTimeout")
2696             {
2697                 option = ENGINE_OPTION_UI_BRIDGES_TIMEOUT;
2698                 value  = text.getIntValue();
2699             }
2700             else if (isPlugin)
2701             {
2702                 /**/ if (tag == "LADSPA_PATH")
2703                 {
2704                     option   = ENGINE_OPTION_PLUGIN_PATH;
2705                     value    = PLUGIN_LADSPA;
2706                     valueStr = text.toRawUTF8();
2707                 }
2708                 else if (tag == "DSSI_PATH")
2709                 {
2710                     option   = ENGINE_OPTION_PLUGIN_PATH;
2711                     value    = PLUGIN_DSSI;
2712                     valueStr = text.toRawUTF8();
2713                 }
2714                 else if (tag == "LV2_PATH")
2715                 {
2716                     option   = ENGINE_OPTION_PLUGIN_PATH;
2717                     value    = PLUGIN_LV2;
2718                     valueStr = text.toRawUTF8();
2719                 }
2720                 else if (tag == "VST2_PATH")
2721                 {
2722                     option   = ENGINE_OPTION_PLUGIN_PATH;
2723                     value    = PLUGIN_VST2;
2724                     valueStr = text.toRawUTF8();
2725                 }
2726                 else if (tag.equalsIgnoreCase("VST3_PATH"))
2727                 {
2728                     option   = ENGINE_OPTION_PLUGIN_PATH;
2729                     value    = PLUGIN_VST3;
2730                     valueStr = text.toRawUTF8();
2731                 }
2732                 else if (tag == "SF2_PATH")
2733                 {
2734                     option   = ENGINE_OPTION_PLUGIN_PATH;
2735                     value    = PLUGIN_SF2;
2736                     valueStr = text.toRawUTF8();
2737                 }
2738                 else if (tag == "SFZ_PATH")
2739                 {
2740                     option   = ENGINE_OPTION_PLUGIN_PATH;
2741                     value    = PLUGIN_SFZ;
2742                     valueStr = text.toRawUTF8();
2743                 }
2744             }
2745 
2746             if (option == -1)
2747             {
2748                 // check old stuff, unhandled now
2749                 if (tag == "GIG_PATH")
2750                     continue;
2751                 // ignored tags
2752                 if (tag == "LADSPA_PATH" || tag == "DSSI_PATH" || tag == "LV2_PATH" || tag == "VST2_PATH")
2753                     continue;
2754                 if (tag == "VST3_PATH" || tag == "AU_PATH")
2755                     continue;
2756                 if (tag == "SF2_PATH" || tag == "SFZ_PATH")
2757                     continue;
2758 
2759                 // hmm something is wrong..
2760                 carla_stderr2("CarlaEngine::loadProjectInternal() - Unhandled option '%s'", tag.toRawUTF8());
2761                 continue;
2762             }
2763 
2764             setOption(static_cast<EngineOption>(option), value, valueStr);
2765         }
2766 
2767         if (pData->aboutToClose)
2768             return true;
2769 
2770         if (pData->actionCanceled)
2771         {
2772             setLastError("Project load canceled");
2773             return false;
2774         }
2775     }
2776 
2777     // now setup transport
2778     if (XmlElement* const elem = (isPreset || isPlugin) ? nullptr : xmlElement->getChildByName("Transport"))
2779     {
2780         if (XmlElement* const bpmElem = elem->getChildByName("BeatsPerMinute"))
2781         {
2782             const String bpmText(bpmElem->getAllSubText().trim());
2783             const double bpm = bpmText.getDoubleValue();
2784 
2785             // some sane limits
2786             if (bpm >= 20.0 && bpm < 400.0)
2787                 pData->time.setBPM(bpm);
2788 
2789             if (pData->aboutToClose)
2790                 return true;
2791 
2792             if (pData->actionCanceled)
2793             {
2794                 setLastError("Project load canceled");
2795                 return false;
2796             }
2797         }
2798     }
2799 
2800     // and we handle plugins
2801     for (XmlElement* elem = xmlElement->getFirstChildElement(); elem != nullptr; elem = elem->getNextElement())
2802     {
2803         const String& tagName(elem->getTagName());
2804 
2805         if (isPreset || tagName == "Plugin")
2806         {
2807             CarlaStateSave stateSave;
2808             stateSave.fillFromXmlElement(isPreset ? xmlElement.get() : elem);
2809 
2810             if (pData->aboutToClose)
2811                 return true;
2812 
2813             if (pData->actionCanceled)
2814             {
2815                 setLastError("Project load canceled");
2816                 return false;
2817             }
2818 
2819             CARLA_SAFE_ASSERT_CONTINUE(stateSave.type != nullptr);
2820 
2821 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
2822             // compatibility code to load projects with GIG files
2823             // FIXME Remove on 2.1 release
2824             if (std::strcmp(stateSave.type, "GIG") == 0)
2825             {
2826                 if (addPlugin(PLUGIN_LV2, "", stateSave.name, "http://linuxsampler.org/plugins/linuxsampler", 0, nullptr))
2827                 {
2828                     const uint pluginId = pData->curPluginCount;
2829 
2830                     if (const CarlaPluginPtr plugin = pData->plugins[pluginId].plugin)
2831                     {
2832                         if (pData->aboutToClose)
2833                             return true;
2834 
2835                         if (pData->actionCanceled)
2836                         {
2837                             setLastError("Project load canceled");
2838                             return false;
2839                         }
2840 
2841                         String lsState;
2842                         lsState << "0.35\n";
2843                         lsState << "18 0 Chromatic\n";
2844                         lsState << "18 1 Drum Kits\n";
2845                         lsState << "20 0\n";
2846                         lsState << "0 1 " << stateSave.binary << "\n";
2847                         lsState << "0 0 0 0 1 0 GIG\n";
2848 
2849                         plugin->setCustomData(LV2_ATOM__String, "http://linuxsampler.org/schema#state-string", lsState.toRawUTF8(), true);
2850                         plugin->restoreLV2State(true);
2851 
2852                         plugin->setDryWet(stateSave.dryWet, true, true);
2853                         plugin->setVolume(stateSave.volume, true, true);
2854                         plugin->setBalanceLeft(stateSave.balanceLeft, true, true);
2855                         plugin->setBalanceRight(stateSave.balanceRight, true, true);
2856                         plugin->setPanning(stateSave.panning, true, true);
2857                         plugin->setCtrlChannel(stateSave.ctrlChannel, true, true);
2858                         plugin->setActive(stateSave.active, true, true);
2859                         plugin->setEnabled(true);
2860 
2861                         ++pData->curPluginCount;
2862                         callback(true, true, ENGINE_CALLBACK_PLUGIN_ADDED, pluginId, 0, 0, 0, 0.0f, plugin->getName());
2863 
2864                         if (isPatchbay)
2865                             pData->graph.addPlugin(plugin);
2866                     }
2867                     else
2868                     {
2869                         carla_stderr2("Failed to get new plugin, state will not be restored correctly\n");
2870                     }
2871                 }
2872                 else
2873                 {
2874                     carla_stderr2("Failed to load a linuxsampler LV2 plugin, GIG file won't be loaded");
2875                 }
2876 
2877                 callback(true, true, ENGINE_CALLBACK_IDLE, 0, 0, 0, 0, 0.0f, nullptr);
2878                 continue;
2879             }
2880 # ifdef SFZ_FILES_USING_SFIZZ
2881             if (std::strcmp(stateSave.type, "SFZ") == 0)
2882             {
2883                 if (addPlugin(PLUGIN_LV2, "", stateSave.name, "http://sfztools.github.io/sfizz", 0, nullptr))
2884                 {
2885                     const uint pluginId = pData->curPluginCount;
2886 
2887                     if (const CarlaPluginPtr plugin = pData->plugins[pluginId].plugin)
2888                     {
2889                         if (pData->aboutToClose)
2890                             return true;
2891 
2892                         if (pData->actionCanceled)
2893                         {
2894                             setLastError("Project load canceled");
2895                             return false;
2896                         }
2897 
2898                         plugin->setCustomData(LV2_ATOM__Path,
2899                                               "http://sfztools.github.io/sfizz:sfzfile",
2900                                               stateSave.binary,
2901                                               false);
2902 
2903                         plugin->restoreLV2State(true);
2904 
2905                         plugin->setDryWet(stateSave.dryWet, true, true);
2906                         plugin->setVolume(stateSave.volume, true, true);
2907                         plugin->setBalanceLeft(stateSave.balanceLeft, true, true);
2908                         plugin->setBalanceRight(stateSave.balanceRight, true, true);
2909                         plugin->setPanning(stateSave.panning, true, true);
2910                         plugin->setCtrlChannel(stateSave.ctrlChannel, true, true);
2911                         plugin->setActive(stateSave.active, true, true);
2912                         plugin->setEnabled(true);
2913 
2914                         ++pData->curPluginCount;
2915                         callback(true, true, ENGINE_CALLBACK_PLUGIN_ADDED, pluginId, 0, 0, 0, 0.0f, plugin->getName());
2916 
2917                         if (isPatchbay)
2918                             pData->graph.addPlugin(plugin);
2919                     }
2920                     else
2921                     {
2922                         carla_stderr2("Failed to get new plugin, state will not be restored correctly\n");
2923                     }
2924                 }
2925                 else
2926                 {
2927                     carla_stderr2("Failed to load a sfizz LV2 plugin, SFZ file won't be loaded");
2928                 }
2929 
2930                 callback(true, true, ENGINE_CALLBACK_IDLE, 0, 0, 0, 0, 0.0f, nullptr);
2931                 continue;
2932             }
2933 # endif
2934 #endif
2935 
2936             const void* extraStuff    = nullptr;
2937             static const char kTrue[] = "true";
2938 
2939             const PluginType ptype(getPluginTypeFromString(stateSave.type));
2940 
2941             switch (ptype)
2942             {
2943             case PLUGIN_SF2:
2944                 if (CarlaString(stateSave.label).endsWith(" (16 outs)"))
2945                     extraStuff = kTrue;
2946                 // fall through
2947             case PLUGIN_LADSPA:
2948             case PLUGIN_DSSI:
2949             case PLUGIN_VST2:
2950             case PLUGIN_VST3:
2951             case PLUGIN_SFZ:
2952                 if (stateSave.binary != nullptr && stateSave.binary[0] != '\0' &&
2953                     ! (File::isAbsolutePath(stateSave.binary) && File(stateSave.binary).exists()))
2954                 {
2955                     const char* searchPath;
2956 
2957                     switch (ptype)
2958                     {
2959                     case PLUGIN_LADSPA: searchPath = pData->options.pathLADSPA; break;
2960                     case PLUGIN_DSSI:   searchPath = pData->options.pathDSSI;   break;
2961                     case PLUGIN_VST2:   searchPath = pData->options.pathVST2;   break;
2962                     case PLUGIN_VST3:   searchPath = pData->options.pathVST3;   break;
2963                     case PLUGIN_SF2:    searchPath = pData->options.pathSF2;    break;
2964                     case PLUGIN_SFZ:    searchPath = pData->options.pathSFZ;    break;
2965                     default:            searchPath = nullptr;                   break;
2966                     }
2967 
2968                     if (searchPath != nullptr && searchPath[0] != '\0')
2969                     {
2970                         carla_stderr("Plugin binary '%s' doesn't exist on this filesystem, let's look for it...",
2971                                      stateSave.binary);
2972 
2973                         String result = findBinaryInCustomPath(searchPath, stateSave.binary);
2974 
2975                         if (result.isEmpty())
2976                         {
2977                             switch (ptype)
2978                             {
2979                             case PLUGIN_LADSPA: searchPath = std::getenv("LADSPA_PATH"); break;
2980                             case PLUGIN_DSSI:   searchPath = std::getenv("DSSI_PATH");   break;
2981                             case PLUGIN_VST2:   searchPath = std::getenv("VST_PATH");    break;
2982                             case PLUGIN_VST3:   searchPath = std::getenv("VST3_PATH");   break;
2983                             case PLUGIN_SF2:    searchPath = std::getenv("SF2_PATH");    break;
2984                             case PLUGIN_SFZ:    searchPath = std::getenv("SFZ_PATH");    break;
2985                             default:            searchPath = nullptr;                    break;
2986                             }
2987 
2988                             if (searchPath != nullptr && searchPath[0] != '\0')
2989                                 result = findBinaryInCustomPath(searchPath, stateSave.binary);
2990                         }
2991 
2992                         if (result.isNotEmpty())
2993                         {
2994                             delete[] stateSave.binary;
2995                             stateSave.binary = carla_strdup(result.toRawUTF8());
2996                             carla_stderr("Found it! :)");
2997                         }
2998                         else
2999                         {
3000                             carla_stderr("Damn, we failed... :(");
3001                         }
3002 
3003                         callback(true, true, ENGINE_CALLBACK_IDLE, 0, 0, 0, 0, 0.0f, nullptr);
3004                     }
3005                 }
3006                 break;
3007             default:
3008                 break;
3009             }
3010 
3011             BinaryType btype;
3012 
3013             switch (ptype)
3014             {
3015             case PLUGIN_LADSPA:
3016             case PLUGIN_DSSI:
3017             case PLUGIN_LV2:
3018             case PLUGIN_VST2:
3019             case PLUGIN_VST3:
3020                 btype = getBinaryTypeFromFile(stateSave.binary);
3021                 break;
3022             default:
3023                 btype = BINARY_NATIVE;
3024                 break;
3025             }
3026 
3027             if (addPlugin(btype, ptype, stateSave.binary,
3028                           stateSave.name, stateSave.label, stateSave.uniqueId, extraStuff, stateSave.options))
3029             {
3030 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
3031                 const uint pluginId = pData->curPluginCount;
3032 #else
3033                 const uint pluginId = 0;
3034 #endif
3035 
3036                 if (const CarlaPluginPtr plugin = pData->plugins[pluginId].plugin)
3037                 {
3038                     if (pData->aboutToClose)
3039                         return true;
3040 
3041                     if (pData->actionCanceled)
3042                     {
3043                         setLastError("Project load canceled");
3044                         return false;
3045                     }
3046 
3047                     // deactivate bridge client-side ping check, since some plugins block during load
3048                     if ((plugin->getHints() & PLUGIN_IS_BRIDGE) != 0 && ! isPreset)
3049                         plugin->setCustomData(CUSTOM_DATA_TYPE_STRING, "__CarlaPingOnOff__", "false", false);
3050 
3051                     plugin->loadStateSave(stateSave);
3052 
3053                     /* NOTE: The following code is the same as the end of addPlugin().
3054                      *       When project is loading we do not enable the plugin right away,
3055                      *        as we want to load state first.
3056                      */
3057                     plugin->setEnabled(true);
3058 
3059                     ++pData->curPluginCount;
3060                     callback(true, true, ENGINE_CALLBACK_PLUGIN_ADDED, pluginId, 0, 0, 0, 0.0f, plugin->getName());
3061 
3062 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
3063                     if (isPatchbay)
3064                         pData->graph.addPlugin(plugin);
3065 #endif
3066                 }
3067                 else
3068                 {
3069                     carla_stderr2("Failed to get new plugin, state will not be restored correctly\n");
3070                 }
3071             }
3072             else
3073             {
3074                 carla_stderr2("Failed to load a plugin '%s', error was:\n%s", stateSave.name, getLastError());
3075             }
3076 
3077             if (! isPreset)
3078                 callback(true, true, ENGINE_CALLBACK_IDLE, 0, 0, 0, 0, 0.0f, nullptr);
3079         }
3080 
3081         if (isPreset)
3082         {
3083             callback(true, true, ENGINE_CALLBACK_PROJECT_LOAD_FINISHED, 0, 0, 0, 0, 0.0f, nullptr);
3084             callback(true, true, ENGINE_CALLBACK_CANCELABLE_ACTION, 0, 0, 0, 0, 0.0f, "Loading project");
3085             return true;
3086         }
3087     }
3088 
3089 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
3090     // tell bridges we're done loading
3091     for (uint i=0; i < pData->curPluginCount; ++i)
3092     {
3093         if (const CarlaPluginPtr plugin = pData->plugins[i].plugin)
3094             if (plugin->isEnabled() && (plugin->getHints() & PLUGIN_IS_BRIDGE) != 0)
3095                 plugin->setCustomData(CUSTOM_DATA_TYPE_STRING, "__CarlaPingOnOff__", "true", false);
3096     }
3097 
3098     if (pData->aboutToClose)
3099         return true;
3100 
3101     if (pData->actionCanceled)
3102     {
3103         setLastError("Project load canceled");
3104         return false;
3105     }
3106 
3107     // now we handle positions
3108     bool loadingAsExternal;
3109     std::map<water::String, water::String> mapGroupNamesInternal, mapGroupNamesExternal;
3110 
3111     bool hasInternalPositions = false;
3112 
3113     if (XmlElement* const elemPatchbay = xmlElement->getChildByName("Patchbay"))
3114     {
3115         hasInternalPositions = true;
3116 
3117         if (XmlElement* const elemPositions = elemPatchbay->getChildByName("Positions"))
3118         {
3119             String name;
3120             PatchbayPosition ppos = { nullptr, -1, 0, 0, 0, 0, false };
3121 
3122             for (XmlElement* patchElem = elemPositions->getFirstChildElement(); patchElem != nullptr; patchElem = patchElem->getNextElement())
3123             {
3124                 const String& patchTag(patchElem->getTagName());
3125 
3126                 if (patchTag != "Position")
3127                     continue;
3128 
3129                 XmlElement* const patchName = patchElem->getChildByName("Name");
3130                 CARLA_SAFE_ASSERT_CONTINUE(patchName != nullptr);
3131 
3132                 const String nameText(patchName->getAllSubText().trim());
3133                 name = xmlSafeString(nameText, false);
3134 
3135                 ppos.name = name.toRawUTF8();
3136                 ppos.x1 = patchElem->getIntAttribute("x1");
3137                 ppos.y1 = patchElem->getIntAttribute("y1");
3138                 ppos.x2 = patchElem->getIntAttribute("x2");
3139                 ppos.y2 = patchElem->getIntAttribute("y2");
3140                 ppos.pluginId = patchElem->getIntAttribute("pluginId", -1);
3141                 ppos.dealloc = false;
3142 
3143                 loadingAsExternal = ppos.pluginId >= 0 && isMultiClient;
3144 
3145                 if (name.isNotEmpty() && restorePatchbayGroupPosition(loadingAsExternal, ppos))
3146                 {
3147                     if (name != ppos.name)
3148                     {
3149                         carla_stdout("Converted client name '%s' to '%s' for this session",
3150                                      name.toRawUTF8(), ppos.name);
3151                         if (loadingAsExternal)
3152                             mapGroupNamesExternal[name] = ppos.name;
3153                         else
3154                             mapGroupNamesInternal[name] = ppos.name;
3155                     }
3156 
3157                     if (ppos.dealloc)
3158                         std::free(const_cast<char*>(ppos.name));
3159                 }
3160             }
3161 
3162             if (pData->aboutToClose)
3163                 return true;
3164 
3165             if (pData->actionCanceled)
3166             {
3167                 setLastError("Project load canceled");
3168                 return false;
3169             }
3170         }
3171     }
3172 
3173     if (XmlElement* const elemPatchbay = xmlElement->getChildByName("ExternalPatchbay"))
3174     {
3175         if (XmlElement* const elemPositions = elemPatchbay->getChildByName("Positions"))
3176         {
3177             String name;
3178             PatchbayPosition ppos = { nullptr, -1, 0, 0, 0, 0, false };
3179 
3180             for (XmlElement* patchElem = elemPositions->getFirstChildElement(); patchElem != nullptr; patchElem = patchElem->getNextElement())
3181             {
3182                 const String& patchTag(patchElem->getTagName());
3183 
3184                 if (patchTag != "Position")
3185                     continue;
3186 
3187                 XmlElement* const patchName = patchElem->getChildByName("Name");
3188                 CARLA_SAFE_ASSERT_CONTINUE(patchName != nullptr);
3189 
3190                 const String nameText(patchName->getAllSubText().trim());
3191                 name = xmlSafeString(nameText, false);
3192 
3193                 ppos.name = name.toRawUTF8();
3194                 ppos.x1 = patchElem->getIntAttribute("x1");
3195                 ppos.y1 = patchElem->getIntAttribute("y1");
3196                 ppos.x2 = patchElem->getIntAttribute("x2");
3197                 ppos.y2 = patchElem->getIntAttribute("y2");
3198                 ppos.pluginId = patchElem->getIntAttribute("pluginId", -1);
3199                 ppos.dealloc = false;
3200 
3201                 loadingAsExternal = ppos.pluginId < 0 || hasInternalPositions || !isPatchbay;
3202 
3203                 carla_debug("loadingAsExternal: %i because %i %i %i",
3204                             loadingAsExternal, ppos.pluginId < 0, hasInternalPositions, !isPatchbay);
3205 
3206                 if (name.isNotEmpty() && restorePatchbayGroupPosition(loadingAsExternal, ppos))
3207                 {
3208                     if (name != ppos.name)
3209                     {
3210                         carla_stdout("Converted client name '%s' to '%s' for this session",
3211                                      name.toRawUTF8(), ppos.name);
3212 
3213                         if (loadingAsExternal)
3214                             mapGroupNamesExternal[name] = ppos.name;
3215                         else
3216                             mapGroupNamesInternal[name] = ppos.name;
3217                     }
3218 
3219                     if (ppos.dealloc)
3220                         std::free(const_cast<char*>(ppos.name));
3221                 }
3222             }
3223 
3224             if (pData->aboutToClose)
3225                 return true;
3226 
3227             if (pData->actionCanceled)
3228             {
3229                 setLastError("Project load canceled");
3230                 return false;
3231             }
3232         }
3233     }
3234 
3235     bool hasInternalConnections = false;
3236 
3237     // and now we handle connections (internal)
3238     if (XmlElement* const elem = xmlElement->getChildByName("Patchbay"))
3239     {
3240         hasInternalConnections = true;
3241 
3242         if (isPatchbay)
3243         {
3244             water::String sourcePort, targetPort;
3245 
3246             for (XmlElement* patchElem = elem->getFirstChildElement(); patchElem != nullptr; patchElem = patchElem->getNextElement())
3247             {
3248                 const String& patchTag(patchElem->getTagName());
3249 
3250                 if (patchTag != "Connection")
3251                     continue;
3252 
3253                 sourcePort.clear();
3254                 targetPort.clear();
3255 
3256                 for (XmlElement* connElem = patchElem->getFirstChildElement(); connElem != nullptr; connElem = connElem->getNextElement())
3257                 {
3258                     const String& tag(connElem->getTagName());
3259                     const String  text(connElem->getAllSubText().trim());
3260 
3261                     /**/ if (tag == "Source")
3262                         sourcePort = xmlSafeString(text, false);
3263                     else if (tag == "Target")
3264                         targetPort = xmlSafeString(text, false);
3265                 }
3266 
3267                 if (sourcePort.isNotEmpty() && targetPort.isNotEmpty())
3268                 {
3269                     std::map<water::String, water::String>& map(mapGroupNamesInternal);
3270                     std::map<water::String, water::String>::iterator it;
3271 
3272                     if ((it = map.find(sourcePort.upToFirstOccurrenceOf(":", false, false))) != map.end())
3273                         sourcePort = it->second + sourcePort.fromFirstOccurrenceOf(":", true, false);
3274                     if ((it = map.find(targetPort.upToFirstOccurrenceOf(":", false, false))) != map.end())
3275                         targetPort = it->second + targetPort.fromFirstOccurrenceOf(":", true, false);
3276 
3277                     restorePatchbayConnection(false, sourcePort.toRawUTF8(), targetPort.toRawUTF8());
3278                 }
3279             }
3280 
3281             if (pData->aboutToClose)
3282                 return true;
3283 
3284             if (pData->actionCanceled)
3285             {
3286                 setLastError("Project load canceled");
3287                 return false;
3288             }
3289         }
3290     }
3291 
3292     // if we're running inside some session-manager (and using JACK), let them handle the external connections
3293     bool loadExternalConnections;
3294 
3295     if (alwaysLoadConnections)
3296     {
3297         loadExternalConnections = true;
3298     }
3299     else
3300     {
3301         /**/ if (std::strcmp(getCurrentDriverName(), "JACK") != 0)
3302             loadExternalConnections = true;
3303         else if (std::getenv("CARLA_DONT_MANAGE_CONNECTIONS") != nullptr)
3304             loadExternalConnections = false;
3305         else if (std::getenv("LADISH_APP_NAME") != nullptr)
3306             loadExternalConnections = false;
3307         else if (std::getenv("NSM_URL") != nullptr)
3308             loadExternalConnections = false;
3309         else
3310             loadExternalConnections = true;
3311     }
3312 
3313     // plus external connections too
3314     if (loadExternalConnections)
3315     {
3316         bool isExternal;
3317         loadingAsExternal = hasInternalConnections &&
3318             (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK || isPatchbay);
3319 
3320         for (XmlElement* elem = xmlElement->getFirstChildElement(); elem != nullptr; elem = elem->getNextElement())
3321         {
3322             const String& tagName(elem->getTagName());
3323 
3324             // check if we want to load patchbay-mode connections into an external (multi-client) graph
3325             if (tagName == "Patchbay")
3326             {
3327                 if (isPatchbay)
3328                     continue;
3329                 isExternal = false;
3330                 loadingAsExternal = true;
3331             }
3332             // or load external patchbay connections
3333             else if (tagName == "ExternalPatchbay")
3334             {
3335                 if (! isPatchbay)
3336                     loadingAsExternal = true;
3337                 isExternal = true;
3338             }
3339             else
3340             {
3341                 continue;
3342             }
3343 
3344             water::String sourcePort, targetPort;
3345 
3346             for (XmlElement* patchElem = elem->getFirstChildElement(); patchElem != nullptr; patchElem = patchElem->getNextElement())
3347             {
3348                 const String& patchTag(patchElem->getTagName());
3349 
3350                 if (patchTag != "Connection")
3351                     continue;
3352 
3353                 sourcePort.clear();
3354                 targetPort.clear();
3355 
3356                 for (XmlElement* connElem = patchElem->getFirstChildElement(); connElem != nullptr; connElem = connElem->getNextElement())
3357                 {
3358                     const String& tag(connElem->getTagName());
3359                     const String  text(connElem->getAllSubText().trim());
3360 
3361                     /**/ if (tag == "Source")
3362                         sourcePort = xmlSafeString(text, false);
3363                     else if (tag == "Target")
3364                         targetPort = xmlSafeString(text, false);
3365                 }
3366 
3367                 if (sourcePort.isNotEmpty() && targetPort.isNotEmpty())
3368                 {
3369                     std::map<water::String, water::String>& map(loadingAsExternal ? mapGroupNamesExternal
3370                                                                                   : mapGroupNamesInternal);
3371                     std::map<water::String, water::String>::iterator it;
3372 
3373                     if (isExternal && isPatchbay && !loadingAsExternal && sourcePort.startsWith("system:capture_"))
3374                     {
3375                         water::String internalPort = sourcePort.trimCharactersAtStart("system:capture_");
3376 
3377                         if (pData->graph.getNumAudioOuts() < 3)
3378                         {
3379                             /**/ if (internalPort == "1")
3380                                 internalPort = "Audio Input:Left";
3381                             else if (internalPort == "2")
3382                                 internalPort = "Audio Input:Right";
3383                             else if (internalPort == "3")
3384                                 internalPort = "Audio Input:Sidechain";
3385                             else
3386                                 continue;
3387                         }
3388                         else
3389                         {
3390                             internalPort = "Audio Input:Capture " + internalPort;
3391                         }
3392 
3393                         carla_stdout("Converted port name '%s' to '%s' for this session",
3394                                      sourcePort.toRawUTF8(), internalPort.toRawUTF8());
3395                         sourcePort = internalPort;
3396                     }
3397                     else if (!isExternal && isMultiClient && sourcePort.startsWith("Audio Input:"))
3398                     {
3399                         water::String externalPort = sourcePort.trimCharactersAtStart("Audio Input:");
3400 
3401                         /**/ if (externalPort == "Left")
3402                             externalPort = "system:capture_1";
3403                         else if (externalPort == "Right")
3404                             externalPort = "system:capture_2";
3405                         else if (externalPort == "Sidechain")
3406                             externalPort = "system:capture_3";
3407                         else
3408                             externalPort = "system:capture_ " + externalPort.trimCharactersAtStart("Capture ");
3409 
3410                         carla_stdout("Converted port name '%s' to '%s' for this session",
3411                                      sourcePort.toRawUTF8(), externalPort.toRawUTF8());
3412                         sourcePort = externalPort;
3413                     }
3414                     else if ((it = map.find(sourcePort.upToFirstOccurrenceOf(":", false, false))) != map.end())
3415                     {
3416                         sourcePort = it->second + sourcePort.fromFirstOccurrenceOf(":", true, false);
3417                     }
3418 
3419                     if (isExternal && isPatchbay && !loadingAsExternal && targetPort.startsWith("system:playback_"))
3420                     {
3421                         water::String internalPort = targetPort.trimCharactersAtStart("system:playback_");
3422 
3423                         if (pData->graph.getNumAudioOuts() < 3)
3424                         {
3425                             /**/ if (internalPort == "1")
3426                                 internalPort = "Audio Output:Left";
3427                             else if (internalPort == "2")
3428                                 internalPort = "Audio Output:Right";
3429                             else
3430                                 continue;
3431                         }
3432                         else
3433                         {
3434                             internalPort = "Audio Input:Playback " + internalPort;
3435                         }
3436 
3437                         carla_stdout("Converted port name '%s' to '%s' for this session",
3438                                      targetPort.toRawUTF8(), internalPort.toRawUTF8());
3439                         targetPort = internalPort;
3440                     }
3441                     else if (!isExternal && isMultiClient && targetPort.startsWith("Audio Output:"))
3442                     {
3443                         water::String externalPort = targetPort.trimCharactersAtStart("Audio Output:");
3444 
3445                         /**/ if (externalPort == "Left")
3446                             externalPort = "system:playback_1";
3447                         else if (externalPort == "Right")
3448                             externalPort = "system:playback_2";
3449                         else
3450                             externalPort = "system:playback_ " + externalPort.trimCharactersAtStart("Playback ");
3451 
3452                         carla_stdout("Converted port name '%s' to '%s' for this session",
3453                                      targetPort.toRawUTF8(), externalPort.toRawUTF8());
3454                         targetPort = externalPort;
3455                     }
3456                     else if ((it = map.find(targetPort.upToFirstOccurrenceOf(":", false, false))) != map.end())
3457                     {
3458                         targetPort = it->second + targetPort.fromFirstOccurrenceOf(":", true, false);
3459                     }
3460 
3461                     restorePatchbayConnection(loadingAsExternal, sourcePort.toRawUTF8(), targetPort.toRawUTF8());
3462                 }
3463             }
3464             break;
3465         }
3466     }
3467 #endif
3468 
3469     if (pData->options.resetXruns)
3470         clearXruns();
3471 
3472     callback(true, true, ENGINE_CALLBACK_PROJECT_LOAD_FINISHED, 0, 0, 0, 0, 0.0f, nullptr);
3473     callback(true, true, ENGINE_CALLBACK_CANCELABLE_ACTION, 0, 0, 0, 0, 0.0f, "Loading project");
3474 
3475     carla_debug("CarlaEngine::loadProjectInternal(%p, %s) - END", &xmlDoc, bool2str(alwaysLoadConnections));
3476     return true;
3477 
3478 #ifdef BUILD_BRIDGE_ALTERNATIVE_ARCH
3479     // unused
3480     (void)alwaysLoadConnections;
3481 #endif
3482 }
3483 
3484 // -----------------------------------------------------------------------
3485 
3486 CARLA_BACKEND_END_NAMESPACE
3487