Implement LV2 ControlInputPort change request feature
[carla.git] / source / backend / plugin / CarlaPluginJack.cpp
blob5be1291635ee38ba550a1d0d42e1f0e274c1de63
1 /*
2 * Carla Plugin JACK
3 * Copyright (C) 2016-2022 Filipe Coelho <falktx@falktx.com>
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.
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.
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
18 #include "CarlaPluginInternal.hpp"
19 #include "CarlaEngine.hpp"
21 #if defined(CARLA_OS_LINUX) || defined(CARLA_OS_MAC)
23 #include "CarlaLibJackHints.h"
24 #include "CarlaBackendUtils.hpp"
25 #include "CarlaBridgeUtils.hpp"
26 #include "CarlaEngineUtils.hpp"
27 #include "CarlaMathUtils.hpp"
28 #include "CarlaPipeUtils.hpp"
29 #include "CarlaScopeUtils.hpp"
30 #include "CarlaShmUtils.hpp"
31 #include "CarlaThread.hpp"
33 #ifdef HAVE_LIBLO
34 # include "CarlaOscUtils.hpp"
35 #else
36 # warning No liblo support, NSM (session state) will not be available
37 #endif
39 #include "water/files/File.h"
40 #include "water/misc/Time.h"
41 #include "water/text/StringArray.h"
42 #include "water/threads/ChildProcess.h"
44 #include "jackbridge/JackBridge.hpp"
46 #include <ctime>
47 #include <vector>
49 // -------------------------------------------------------------------------------------------------------------------
51 using water::ChildProcess;
52 using water::File;
53 using water::String;
54 using water::StringArray;
56 CARLA_BACKEND_START_NAMESPACE
58 static size_t safe_rand(const size_t limit)
60 const int r = std::rand();
61 CARLA_SAFE_ASSERT_RETURN(r >= 0, 0);
63 return static_cast<uint>(r) % limit;
66 // -------------------------------------------------------------------------------------------------------------------
67 // Fallback data
69 static const ExternalMidiNote kExternalMidiNoteFallback = { -1, 0, 0 };
71 // -------------------------------------------------------------------------------------------------------------------
73 struct Announcer {
74 virtual ~Announcer() {}
75 virtual void nsmAnnounced(bool hasGui) = 0;
78 class CarlaPluginJackThread : public CarlaThread
80 public:
81 CarlaPluginJackThread(Announcer* const ann, CarlaEngine* const engine, CarlaPlugin* const plugin) noexcept
82 : CarlaThread("CarlaPluginJackThread"),
83 kAnnouncer(ann),
84 kEngine(engine),
85 kPlugin(plugin),
86 fShmIds(),
87 fSetupLabel(),
88 #ifdef HAVE_LIBLO
89 fOscClientAddress(nullptr),
90 fOscServer(nullptr),
91 fHasOptionalGui(false),
92 fProject(),
93 #endif
94 fProcess() {}
96 void setData(const char* const shmIds, const char* const setupLabel) noexcept
98 CARLA_SAFE_ASSERT_RETURN(shmIds != nullptr && shmIds[0] != '\0',);
99 CARLA_SAFE_ASSERT_RETURN(setupLabel != nullptr && setupLabel[0] != '\0',);
100 CARLA_SAFE_ASSERT(! isThreadRunning());
102 fShmIds = shmIds;
103 fSetupLabel = setupLabel;
106 uintptr_t getProcessID() const noexcept
108 CARLA_SAFE_ASSERT_RETURN(fProcess != nullptr, 0);
110 return (uintptr_t)fProcess->getPID();
113 #ifdef HAVE_LIBLO
114 void nsmSave(const char* const setupLabel)
116 if (fOscClientAddress == nullptr)
117 return;
119 if (fSetupLabel != setupLabel)
120 fSetupLabel = setupLabel;
122 maybeOpenFirstTime(false);
124 lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/save", "");
127 bool nsmShowGui(const bool yesNo)
129 if (fOscClientAddress == nullptr || ! fHasOptionalGui)
130 return false;
132 lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE,
133 yesNo ? "/nsm/client/show_optional_gui"
134 : "/nsm/client/hide_optional_gui", "");
136 return true;
138 #endif
140 char* getEnvVarsToExport()
142 const EngineOptions& options(kEngine->getOptions());
143 CarlaString binaryDir(options.binaryDir);
144 #ifdef HAVE_LIBLO
145 const int sessionManager = fSetupLabel[4U] - '0';
146 #endif
148 CarlaString ret;
149 #ifdef CARLA_OS_MAC
150 ret += "export DYLD_LIBRARY_PATH=" + binaryDir + "/jack\n";
151 ret += "export DYLD_INSERT_LIBRARIES=" + binaryDir + "/libcarla_interposer-jack-x11.dylib\n";
152 ret += "export DYLD_FORCE_FLAT_NAMESPACE=1\n";
153 #else
154 ret += "export LD_LIBRARY_PATH=" + binaryDir + "/jack\n";
155 ret += "export LD_PRELOAD=" + binaryDir + "/libcarla_interposer-jack-x11.so\n";
156 #endif
157 #ifdef HAVE_LIBLO
158 if (sessionManager == LIBJACK_SESSION_MANAGER_NSM)
160 for (int i=50; fOscServer == nullptr && --i>=0;)
161 carla_msleep(100);
163 ret += "export NSM_URL=";
164 ret += lo_server_get_url(fOscServer);
165 ret += "\n";
167 #endif
169 if (kPlugin->getHints() & PLUGIN_HAS_CUSTOM_UI)
170 ret += "export CARLA_FRONTEND_WIN_ID=" + CarlaString(options.frontendWinId) + "\n";
172 ret += "export CARLA_LIBJACK_SETUP=" + fSetupLabel + "\n";
173 ret += "export CARLA_SHM_IDS=" + fShmIds + "\n";
175 return ret.releaseBufferPointer();
178 protected:
179 #ifdef HAVE_LIBLO
180 static void _osc_error_handler(int num, const char* msg, const char* path)
182 carla_stderr2("CarlaPluginJackThread::_osc_error_handler(%i, \"%s\", \"%s\")", num, msg, path);
185 static int _broadcast_handler(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* data)
187 CARLA_SAFE_ASSERT_RETURN(data != nullptr, 0);
188 carla_stdout("CarlaPluginJackThread::_broadcast_handler(%s, %s, %p, %i)", path, types, argv, argc);
190 return ((CarlaPluginJackThread*)data)->handleBroadcast(path, types, argv, msg);
193 void maybeOpenFirstTime(const bool announced)
195 if (fSetupLabel.length() <= 6)
196 return;
198 if ((announced || fProject.path.isEmpty()) && fProject.init(kPlugin->getName(),
199 kEngine->getCurrentProjectFolder(),
200 &fSetupLabel[6U]))
202 carla_stdout("Sending open signal %s %s %s",
203 fProject.path.buffer(), fProject.display.buffer(), fProject.clientName.buffer());
205 lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/open", "sss",
206 fProject.path.buffer(), fProject.display.buffer(), fProject.clientName.buffer());
210 int handleBroadcast(const char* path, const char* types, lo_arg** argv, lo_message msg)
212 if (std::strcmp(path, "/nsm/server/announce") == 0)
214 CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "sssiii") == 0, 0);
216 const lo_address msgAddress(lo_message_get_source(msg));
217 CARLA_SAFE_ASSERT_RETURN(msgAddress != nullptr, 0);
219 char* const msgURL(lo_address_get_url(msgAddress));
220 CARLA_SAFE_ASSERT_RETURN(msgURL != nullptr, 0);
222 if (fOscClientAddress != nullptr)
223 lo_address_free(fOscClientAddress);
225 fOscClientAddress = lo_address_new_from_url(msgURL);
226 CARLA_SAFE_ASSERT_RETURN(fOscClientAddress != nullptr, 0);
228 fProject.appName = &argv[0]->s;
229 fHasOptionalGui = std::strstr(&argv[1]->s, ":optional-gui:") != nullptr;
231 kAnnouncer->nsmAnnounced(fHasOptionalGui);
233 static const char* const featuresG = ":server-control:optional-gui:";
234 static const char* const featuresN = ":server-control:";
236 static const char* const method = "/nsm/server/announce";
237 static const char* const message = "Howdy, what took you so long?";
238 static const char* const smName = "Carla";
240 const char* const features = ((fSetupLabel[5U] - '0') & LIBJACK_FLAG_CONTROL_WINDOW)
241 ? featuresG : featuresN;
243 lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "ssss",
244 method, message, smName, features);
246 maybeOpenFirstTime(true);
247 return 0;
250 CARLA_SAFE_ASSERT_RETURN(fOscClientAddress != nullptr, 0);
252 if (std::strcmp(path, "/reply") == 0)
254 CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "ss") == 0, 0);
256 const char* const method = &argv[0]->s;
257 const char* const message = &argv[1]->s;
259 carla_stdout("Got reply of '%s' as '%s'", method, message);
261 if (std::strcmp(method, "/nsm/client/open") == 0)
263 carla_stdout("Sending 'Session is loaded' to %s", fProject.appName.buffer());
264 lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/session_is_loaded", "");
268 else if (std::strcmp(path, "/nsm/client/gui_is_shown") == 0)
270 CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "") == 0, 0);
272 kEngine->callback(true, true,
273 ENGINE_CALLBACK_UI_STATE_CHANGED,
274 kPlugin->getId(),
276 0, 0, 0.0f, nullptr);
279 else if (std::strcmp(path, "/nsm/client/gui_is_hidden") == 0)
281 CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "") == 0, 0);
283 kEngine->callback(true, true,
284 ENGINE_CALLBACK_UI_STATE_CHANGED,
285 kPlugin->getId(),
287 0, 0, 0.0f, nullptr);
290 // special messages
291 else if (std::strcmp(path, "/nsm/gui/client/save") == 0)
293 CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "s") == 0, 0);
295 lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/save", "");
298 else if (std::strcmp(path, "/nsm/server/stop") == 0)
300 CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "s") == 0, 0);
302 lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/hide_optional_gui", "");
304 kEngine->callback(true, true,
305 ENGINE_CALLBACK_UI_STATE_CHANGED,
306 kPlugin->getId(),
308 0, 0, 0.0f, nullptr);
311 return 0;
313 #endif
315 void run()
317 #ifdef HAVE_LIBLO
318 if (fOscClientAddress != nullptr)
320 lo_address_free(fOscClientAddress);
321 fOscClientAddress = nullptr;
324 const int sessionManager = fSetupLabel[4U] - '0';
326 if (sessionManager == LIBJACK_SESSION_MANAGER_NSM)
328 // NSM support
329 fOscServer = lo_server_new_with_proto(nullptr, LO_UDP, _osc_error_handler);
330 CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr,);
332 lo_server_add_method(fOscServer, nullptr, nullptr, _broadcast_handler, this);
334 #endif
336 const bool externalProcess = ((fSetupLabel[5U] - '0') & LIBJACK_FLAG_EXTERNAL_START)
337 && ! kEngine->isLoadingProject();
339 if (! externalProcess)
341 if (fProcess == nullptr)
343 fProcess = new ChildProcess();
345 else if (fProcess->isRunning())
347 carla_stderr("CarlaPluginJackThread::run() - already running");
350 String name(kPlugin->getName());
351 String filename(kPlugin->getFilename());
353 if (name.isEmpty())
354 name = "(none)";
356 CARLA_SAFE_ASSERT_RETURN(filename.isNotEmpty(),);
358 StringArray arguments;
360 // binary
361 arguments.addTokens(filename, true);
363 const EngineOptions& options(kEngine->getOptions());
365 char winIdStr[STR_MAX+1];
366 std::snprintf(winIdStr, STR_MAX, P_UINTPTR, options.frontendWinId);
367 winIdStr[STR_MAX] = '\0';
369 const CarlaString libjackdir(CarlaString(options.binaryDir) + "/jack");
370 #ifdef CARLA_OS_MAC
371 const CarlaString ldpreload(CarlaString(options.binaryDir) + "/libcarla_interposer-jack-x11.dylib");
372 #else
373 const CarlaString ldpreload(CarlaString(options.binaryDir) + "/libcarla_interposer-jack-x11.so");
374 #endif
376 const ScopedEngineEnvironmentLocker _seel(kEngine);
378 #ifdef CARLA_OS_MAC
379 const CarlaScopedEnvVar sev2("DYLD_LIBRARY_PATH", libjackdir.buffer());
380 const CarlaScopedEnvVar sev1("DYLD_INSERT_LIBRARIES", ldpreload.isNotEmpty() ? ldpreload.buffer() : nullptr);
381 const CarlaScopedEnvVar sev0("DYLD_FORCE_FLAT_NAMESPACE", "1");
382 #else
383 const CarlaScopedEnvVar sev2("LD_LIBRARY_PATH", libjackdir.buffer());
384 const CarlaScopedEnvVar sev1("LD_PRELOAD", ldpreload.isNotEmpty() ? ldpreload.buffer() : nullptr);
385 #endif
386 #ifdef HAVE_LIBLO
387 const CarlaScopedEnvVar sev3("NSM_URL", lo_server_get_url(fOscServer));
388 #endif
390 if (kPlugin->getHints() & PLUGIN_HAS_CUSTOM_UI)
391 carla_setenv("CARLA_FRONTEND_WIN_ID", winIdStr);
392 else
393 carla_unsetenv("CARLA_FRONTEND_WIN_ID");
395 carla_setenv("CARLA_LIBJACK_SETUP", fSetupLabel.buffer());
396 carla_setenv("CARLA_SHM_IDS", fShmIds.buffer());
398 if (! fProcess->start(arguments))
400 carla_stdout("failed!");
401 fProcess = nullptr;
402 return;
406 for (; (externalProcess || fProcess->isRunning()) && ! shouldThreadExit();)
408 #ifdef HAVE_LIBLO
409 if (sessionManager == LIBJACK_SESSION_MANAGER_NSM)
411 lo_server_recv_noblock(fOscServer, 50);
413 else
414 #endif
416 carla_msleep(50);
420 #ifdef HAVE_LIBLO
421 if (sessionManager == LIBJACK_SESSION_MANAGER_NSM)
423 lo_server_free(fOscServer);
424 fOscServer = nullptr;
426 if (fOscClientAddress != nullptr)
428 lo_address_free(fOscClientAddress);
429 fOscClientAddress = nullptr;
432 #endif
434 if (! externalProcess)
436 // we only get here if bridge crashed or thread asked to exit
437 if (fProcess->isRunning() && shouldThreadExit())
439 fProcess->waitForProcessToFinish(2000);
441 if (fProcess->isRunning())
443 carla_stdout("CarlaPluginJackThread::run() - application refused to close, force kill now");
444 fProcess->kill();
447 else
449 // forced quit, may have crashed
450 if (fProcess->getExitCodeAndClearPID() != 0)
452 carla_stderr("CarlaPluginJackThread::run() - application crashed");
454 CarlaString errorString("Plugin '" + CarlaString(kPlugin->getName()) + "' has crashed!\n"
455 "Saving now will lose its current settings.\n"
456 "Please remove this plugin, and not rely on it from this point.");
457 kEngine->callback(true, true,
458 ENGINE_CALLBACK_ERROR,
459 kPlugin->getId(),
460 0, 0, 0, 0.0f,
461 errorString);
466 fProcess = nullptr;
469 private:
470 Announcer* const kAnnouncer;
471 CarlaEngine* const kEngine;
472 CarlaPlugin* const kPlugin;
474 CarlaString fShmIds;
475 CarlaString fSetupLabel;
477 #ifdef HAVE_LIBLO
478 lo_address fOscClientAddress;
479 lo_server fOscServer;
480 bool fHasOptionalGui;
482 struct ProjectData {
483 CarlaString appName;
484 CarlaString path;
485 CarlaString display;
486 CarlaString clientName;
488 ProjectData()
489 : appName(),
490 path(),
491 display(),
492 clientName() {}
494 bool init(const char* const pluginName,
495 const char* const engineProjectFolder,
496 const char* const uniqueCodeID)
498 CARLA_SAFE_ASSERT_RETURN(engineProjectFolder != nullptr && engineProjectFolder[0] != '\0', false);
499 CARLA_SAFE_ASSERT_RETURN(uniqueCodeID != nullptr && uniqueCodeID[0] != '\0', false);
500 CARLA_SAFE_ASSERT_RETURN(appName.isNotEmpty(), false);
502 String child(pluginName);
503 child += ".";
504 child += uniqueCodeID;
506 const File file(File(engineProjectFolder).getChildFile(child));
508 clientName = appName + "." + uniqueCodeID;
509 path = file.getFullPathName().toRawUTF8();
510 display = file.getFileNameWithoutExtension().toRawUTF8();
512 return true;
515 CARLA_DECLARE_NON_COPYABLE(ProjectData)
516 } fProject;
517 #endif
519 CarlaScopedPointer<ChildProcess> fProcess;
521 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginJackThread)
524 // -------------------------------------------------------------------------------------------------------------------
526 class CarlaPluginJack : public CarlaPlugin,
527 public Announcer
529 public:
530 CarlaPluginJack(CarlaEngine* const engine, const uint id)
531 : CarlaPlugin(engine, id),
532 fInitiated(false),
533 fInitError(false),
534 fTimedOut(false),
535 fTimedError(false),
536 fProcCanceled(false),
537 fBufferSize(engine->getBufferSize()),
538 fProcWaitTime(0),
539 fSetupHints(0x0),
540 fBridgeThread(this, engine, this),
541 fShmAudioPool(),
542 fShmRtClientControl(),
543 fShmNonRtClientControl(),
544 fShmNonRtServerControl(),
545 fInfo()
547 carla_debug("CarlaPluginJack::CarlaPluginJack(%p, %i)", engine, id);
549 pData->hints |= PLUGIN_IS_BRIDGE;
552 ~CarlaPluginJack() override
554 carla_debug("CarlaPluginJack::~CarlaPluginJack()");
556 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
557 // close UI
558 if (pData->hints & PLUGIN_HAS_CUSTOM_UI)
559 pData->transientTryCounter = 0;
560 #endif
562 pData->singleMutex.lock();
563 pData->masterMutex.lock();
565 if (pData->client != nullptr && pData->client->isActive())
566 pData->client->deactivate(true);
568 if (pData->active)
570 deactivate();
571 pData->active = false;
574 if (fBridgeThread.isThreadRunning())
576 fShmRtClientControl.writeOpcode(kPluginBridgeRtClientQuit);
577 fShmRtClientControl.commitWrite();
579 fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientQuit);
580 fShmNonRtClientControl.commitWrite();
582 if (! fTimedOut)
583 waitForClient("stopping", 3000);
586 fBridgeThread.stopThread(3000);
588 fShmNonRtServerControl.clear();
589 fShmNonRtClientControl.clear();
590 fShmRtClientControl.clear();
591 fShmAudioPool.clear();
593 clearBuffers();
595 fInfo.chunk.clear();
598 // -------------------------------------------------------------------
600 void nsmAnnounced(bool hasGui) override
602 if (hasGui || (pData->hints & PLUGIN_HAS_CUSTOM_UI) == 0x0)
603 return;
606 const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex);
608 fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientShowUI);
609 fShmNonRtClientControl.commitWrite();
612 pData->engine->callback(true, true,
613 ENGINE_CALLBACK_UI_STATE_CHANGED,
614 pData->id,
616 0, 0, 0.0f, nullptr);
619 // -------------------------------------------------------------------
620 // Information (base)
622 PluginType getType() const noexcept override
624 return PLUGIN_JACK;
627 PluginCategory getCategory() const noexcept override
629 return PLUGIN_CATEGORY_NONE;
632 // -------------------------------------------------------------------
633 // Information (count)
635 uint32_t getMidiInCount() const noexcept override
637 return fInfo.mIns;
640 uint32_t getMidiOutCount() const noexcept override
642 return fInfo.mOuts;
645 // -------------------------------------------------------------------
646 // Information (current data)
648 // -------------------------------------------------------------------
649 // Information (per-plugin data)
651 uint getOptionsAvailable() const noexcept override
653 uint options = 0x0;
655 if (fInfo.mIns > 0)
657 options |= PLUGIN_OPTION_SEND_CONTROL_CHANGES;
658 options |= PLUGIN_OPTION_SEND_CHANNEL_PRESSURE;
659 options |= PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH;
660 options |= PLUGIN_OPTION_SEND_PITCHBEND;
661 options |= PLUGIN_OPTION_SEND_ALL_SOUND_OFF;
662 options |= PLUGIN_OPTION_SEND_PROGRAM_CHANGES;
663 options |= PLUGIN_OPTION_SKIP_SENDING_NOTES;
666 return options;
669 bool getLabel(char* const strBuf) const noexcept override
671 std::strncpy(strBuf, fInfo.setupLabel, STR_MAX);
672 return true;
675 bool getMaker(char* const) const noexcept override
677 return false;
680 bool getCopyright(char* const) const noexcept override
682 return false;
685 bool getRealName(char* const strBuf) const noexcept override
687 // FIXME
688 std::strncpy(strBuf, "Carla's libjack", STR_MAX);
689 return true;
692 // -------------------------------------------------------------------
693 // Set data (state)
695 void prepareForSave(bool) noexcept override
697 #ifdef HAVE_LIBLO
698 if (fInfo.setupLabel.length() == 6)
699 setupUniqueProjectID();
700 #endif
703 const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex);
705 fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientPrepareForSave);
706 fShmNonRtClientControl.commitWrite();
709 #ifdef HAVE_LIBLO
710 fBridgeThread.nsmSave(fInfo.setupLabel);
711 #endif
714 // -------------------------------------------------------------------
715 // Set data (internal stuff)
717 void setOption(const uint option, const bool yesNo, const bool sendCallback) override
720 const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex);
722 fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientSetOption);
723 fShmNonRtClientControl.writeUInt(option);
724 fShmNonRtClientControl.writeBool(yesNo);
725 fShmNonRtClientControl.commitWrite();
728 CarlaPlugin::setOption(option, yesNo, sendCallback);
731 void setCtrlChannel(const int8_t channel, const bool sendOsc, const bool sendCallback) noexcept override
733 CARLA_SAFE_ASSERT_RETURN(sendOsc || sendCallback,); // never call this from RT
736 const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex);
738 fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientSetCtrlChannel);
739 fShmNonRtClientControl.writeShort(channel);
740 fShmNonRtClientControl.commitWrite();
743 CarlaPlugin::setCtrlChannel(channel, sendOsc, sendCallback);
746 // -------------------------------------------------------------------
747 // Set data (plugin-specific stuff)
749 void setCustomData(const char* const type, const char* const key, const char* const value, const bool sendGui) override
751 CARLA_SAFE_ASSERT_RETURN(type != nullptr && type[0] != '\0',);
752 CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
753 CARLA_SAFE_ASSERT_RETURN(value != nullptr,);
755 if (std::strcmp(type, CUSTOM_DATA_TYPE_PROPERTY) == 0)
756 return CarlaPlugin::setCustomData(type, key, value, sendGui);
758 if (std::strcmp(type, CUSTOM_DATA_TYPE_STRING) == 0 && std::strcmp(key, "__CarlaPingOnOff__") == 0)
760 #if 0
761 const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex);
763 fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientPingOnOff);
764 fShmNonRtClientControl.writeBool(std::strcmp(value, "true") == 0);
765 fShmNonRtClientControl.commitWrite();
766 #endif
767 return;
770 CarlaPlugin::setCustomData(type, key, value, sendGui);
773 // -------------------------------------------------------------------
774 // Set ui stuff
776 void showCustomUI(const bool yesNo) override
778 if (yesNo && ! fBridgeThread.isThreadRunning()) {
779 CARLA_SAFE_ASSERT_RETURN(restartBridgeThread(),);
782 #ifdef HAVE_LIBLO
783 if (! fBridgeThread.nsmShowGui(yesNo))
784 #endif
786 const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex);
788 fShmNonRtClientControl.writeOpcode(yesNo ? kPluginBridgeNonRtClientShowUI : kPluginBridgeNonRtClientHideUI);
789 fShmNonRtClientControl.commitWrite();
793 void idle() override
795 if (fBridgeThread.isThreadRunning())
797 if (fInitiated && fTimedOut && pData->active)
798 setActive(false, true, true);
801 const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex);
803 fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientPing);
804 fShmNonRtClientControl.commitWrite();
807 try {
808 handleNonRtData();
809 } CARLA_SAFE_EXCEPTION("handleNonRtData");
811 else if (fInitiated)
813 fTimedOut = true;
814 fTimedError = true;
815 fInitiated = false;
816 handleProcessStopped();
818 else if (fProcCanceled)
820 handleProcessStopped();
821 fProcCanceled = false;
824 CarlaPlugin::idle();
827 // -------------------------------------------------------------------
828 // Plugin state
830 void reload() override
832 CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr,);
833 carla_debug("CarlaPluginJack::reload() - start");
835 const EngineProcessMode processMode(pData->engine->getProccessMode());
837 // Safely disable plugin for reload
838 const ScopedDisabler sd(this);
840 // cleanup of previous data
841 pData->audioIn.clear();
842 pData->audioOut.clear();
843 pData->event.clear();
845 bool needsCtrlIn, needsCtrlOut;
846 needsCtrlIn = needsCtrlOut = false;
848 if (fInfo.aIns > 0)
850 pData->audioIn.createNew(fInfo.aIns);
853 if (fInfo.aOuts > 0)
855 pData->audioOut.createNew(fInfo.aOuts);
856 needsCtrlIn = true;
859 if (fInfo.mIns > 0)
860 needsCtrlIn = true;
862 if (fInfo.mOuts > 0)
863 needsCtrlOut = true;
865 const uint portNameSize(pData->engine->getMaxPortNameSize());
866 CarlaString portName;
868 // Audio Ins
869 for (uint8_t j=0; j < fInfo.aIns; ++j)
871 portName.clear();
873 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
875 portName = pData->name;
876 portName += ":";
879 if (fInfo.aIns > 1)
881 portName += "audio_in_";
882 portName += CarlaString(j+1);
884 else
886 portName += "audio_in";
889 portName.truncate(portNameSize);
891 pData->audioIn.ports[j].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, true, j);
892 pData->audioIn.ports[j].rindex = j;
895 // Audio Outs
896 for (uint8_t j=0; j < fInfo.aOuts; ++j)
898 portName.clear();
900 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
902 portName = pData->name;
903 portName += ":";
906 if (fInfo.aOuts > 1)
908 portName += "audio_out_";
909 portName += CarlaString(j+1);
911 else
913 portName += "audio_out";
916 portName.truncate(portNameSize);
918 pData->audioOut.ports[j].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, false, j);
919 pData->audioOut.ports[j].rindex = j;
922 if (needsCtrlIn)
924 portName.clear();
926 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
928 portName = pData->name;
929 portName += ":";
932 portName += "event-in";
933 portName.truncate(portNameSize);
935 pData->event.portIn = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent, portName, true, 0);
938 if (needsCtrlOut)
940 portName.clear();
942 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
944 portName = pData->name;
945 portName += ":";
948 portName += "event-out";
949 portName.truncate(portNameSize);
951 pData->event.portOut = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent, portName, false, 0);
954 // extra plugin hints
955 pData->extraHints = 0x0;
957 if (fInfo.mIns > 0)
958 pData->extraHints |= PLUGIN_EXTRA_HINT_HAS_MIDI_IN;
960 if (fInfo.mOuts > 0)
961 pData->extraHints |= PLUGIN_EXTRA_HINT_HAS_MIDI_OUT;
963 bufferSizeChanged(pData->engine->getBufferSize());
964 reloadPrograms(true);
966 carla_debug("CarlaPluginJack::reload() - end");
969 // -------------------------------------------------------------------
970 // Plugin processing
972 void activate() noexcept override
974 if (! fBridgeThread.isThreadRunning())
976 CARLA_SAFE_ASSERT_RETURN(restartBridgeThread(),);
979 CARLA_SAFE_ASSERT_RETURN(! fTimedError,);
982 const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex);
984 fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientActivate);
985 fShmNonRtClientControl.commitWrite();
988 fTimedOut = false;
990 try {
991 waitForClient("activate", 2000);
992 } CARLA_SAFE_EXCEPTION("activate - waitForClient");
995 void deactivate() noexcept override
997 if (! fBridgeThread.isThreadRunning())
998 return;
1000 CARLA_SAFE_ASSERT_RETURN(! fTimedError,);
1003 const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex);
1005 fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientDeactivate);
1006 fShmNonRtClientControl.commitWrite();
1009 fTimedOut = false;
1011 try {
1012 waitForClient("deactivate", 2000);
1013 } CARLA_SAFE_EXCEPTION("deactivate - waitForClient");
1016 void process(const float* const* const audioIn, float** const audioOut,
1017 const float* const*, float**, const uint32_t frames) override
1019 // --------------------------------------------------------------------------------------------------------
1020 // Check if active
1022 if (fProcCanceled || fTimedOut || fTimedError || ! pData->active)
1024 // disable any output sound
1025 for (uint32_t i=0; i < pData->audioOut.count; ++i)
1026 carla_zeroFloats(audioOut[i], frames);
1027 return;
1030 // --------------------------------------------------------------------------------------------------------
1031 // Check if needs reset
1033 if (pData->needsReset)
1035 // TODO
1037 pData->needsReset = false;
1040 // --------------------------------------------------------------------------------------------------------
1041 // Event Input
1043 if (pData->event.portIn != nullptr)
1045 // ----------------------------------------------------------------------------------------------------
1046 // MIDI Input (External)
1048 if (pData->extNotes.mutex.tryLock())
1050 for (RtLinkedList<ExternalMidiNote>::Itenerator it = pData->extNotes.data.begin2(); it.valid(); it.next())
1052 const ExternalMidiNote& note(it.getValue(kExternalMidiNoteFallback));
1053 CARLA_SAFE_ASSERT_CONTINUE(note.channel >= 0 && note.channel < MAX_MIDI_CHANNELS);
1055 uint8_t data1, data2, data3;
1056 data1 = uint8_t((note.velo > 0 ? MIDI_STATUS_NOTE_ON : MIDI_STATUS_NOTE_OFF) | (note.channel & MIDI_CHANNEL_BIT));
1057 data2 = note.note;
1058 data3 = note.velo;
1060 fShmRtClientControl.writeOpcode(kPluginBridgeRtClientMidiEvent);
1061 fShmRtClientControl.writeUInt(0); // time
1062 fShmRtClientControl.writeByte(0); // port
1063 fShmRtClientControl.writeByte(3); // size
1064 fShmRtClientControl.writeByte(data1);
1065 fShmRtClientControl.writeByte(data2);
1066 fShmRtClientControl.writeByte(data3);
1067 fShmRtClientControl.commitWrite();
1070 pData->extNotes.data.clear();
1071 pData->extNotes.mutex.unlock();
1073 } // End of MIDI Input (External)
1075 // ----------------------------------------------------------------------------------------------------
1076 // Event Input (System)
1078 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1079 bool allNotesOffSent = false;
1080 #endif
1081 for (uint32_t i=0, numEvents=pData->event.portIn->getEventCount(); i < numEvents; ++i)
1083 const EngineEvent& event(pData->event.portIn->getEvent(i));
1085 // Control change
1086 switch (event.type)
1088 case kEngineEventTypeNull:
1089 break;
1091 case kEngineEventTypeControl: {
1092 const EngineControlEvent& ctrlEvent = event.ctrl;
1094 switch (ctrlEvent.type)
1096 case kEngineControlEventTypeNull:
1097 break;
1099 case kEngineControlEventTypeParameter:
1100 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1101 // Control backend stuff
1102 if (event.channel == pData->ctrlChannel)
1104 float value;
1106 if (MIDI_IS_CONTROL_BREATH_CONTROLLER(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_DRYWET) != 0)
1108 value = ctrlEvent.normalizedValue;
1109 setDryWetRT(value, true);
1112 if (MIDI_IS_CONTROL_CHANNEL_VOLUME(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_VOLUME) != 0)
1114 value = ctrlEvent.normalizedValue*127.0f/100.0f;
1115 setVolumeRT(value, true);
1118 if (MIDI_IS_CONTROL_BALANCE(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_BALANCE) != 0)
1120 float left, right;
1121 value = ctrlEvent.normalizedValue/0.5f - 1.0f;
1123 if (value < 0.0f)
1125 left = -1.0f;
1126 right = (value*2.0f)+1.0f;
1128 else if (value > 0.0f)
1130 left = (value*2.0f)-1.0f;
1131 right = 1.0f;
1133 else
1135 left = -1.0f;
1136 right = 1.0f;
1139 setBalanceLeftRT(left, true);
1140 setBalanceRightRT(right, true);
1143 #endif
1144 if ((pData->options & PLUGIN_OPTION_SEND_CONTROL_CHANGES) != 0 && ctrlEvent.param < MAX_MIDI_VALUE)
1146 fShmRtClientControl.writeOpcode(kPluginBridgeRtClientMidiEvent);
1147 fShmRtClientControl.writeUInt(event.time);
1148 fShmRtClientControl.writeByte(0); // port
1149 fShmRtClientControl.writeByte(3); // size
1150 fShmRtClientControl.writeByte(uint8_t(MIDI_STATUS_CONTROL_CHANGE | (event.channel & MIDI_CHANNEL_BIT)));
1151 fShmRtClientControl.writeByte(uint8_t(ctrlEvent.param));
1152 fShmRtClientControl.writeByte(uint8_t(ctrlEvent.normalizedValue*127.0f + 0.5f));
1154 break;
1156 case kEngineControlEventTypeMidiBank:
1157 if (pData->options & PLUGIN_OPTION_MAP_PROGRAM_CHANGES)
1159 fShmRtClientControl.writeOpcode(kPluginBridgeRtClientControlEventMidiBank);
1160 fShmRtClientControl.writeUInt(event.time);
1161 fShmRtClientControl.writeByte(event.channel);
1162 fShmRtClientControl.writeUShort(event.ctrl.param);
1163 fShmRtClientControl.commitWrite();
1165 break;
1167 case kEngineControlEventTypeMidiProgram:
1168 if (pData->options & PLUGIN_OPTION_MAP_PROGRAM_CHANGES)
1170 fShmRtClientControl.writeOpcode(kPluginBridgeRtClientControlEventMidiProgram);
1171 fShmRtClientControl.writeUInt(event.time);
1172 fShmRtClientControl.writeByte(event.channel);
1173 fShmRtClientControl.writeUShort(event.ctrl.param);
1174 fShmRtClientControl.commitWrite();
1176 break;
1178 case kEngineControlEventTypeAllSoundOff:
1179 if (pData->options & PLUGIN_OPTION_SEND_ALL_SOUND_OFF)
1181 fShmRtClientControl.writeOpcode(kPluginBridgeRtClientControlEventAllSoundOff);
1182 fShmRtClientControl.writeUInt(event.time);
1183 fShmRtClientControl.writeByte(event.channel);
1184 fShmRtClientControl.commitWrite();
1186 break;
1188 case kEngineControlEventTypeAllNotesOff:
1189 if (pData->options & PLUGIN_OPTION_SEND_ALL_SOUND_OFF)
1191 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1192 if (event.channel == pData->ctrlChannel && ! allNotesOffSent)
1194 allNotesOffSent = true;
1195 postponeRtAllNotesOff();
1197 #endif
1199 fShmRtClientControl.writeOpcode(kPluginBridgeRtClientControlEventAllNotesOff);
1200 fShmRtClientControl.writeUInt(event.time);
1201 fShmRtClientControl.writeByte(event.channel);
1202 fShmRtClientControl.commitWrite();
1204 break;
1205 } // switch (ctrlEvent.type)
1206 break;
1207 } // case kEngineEventTypeControl
1209 case kEngineEventTypeMidi: {
1210 const EngineMidiEvent& midiEvent(event.midi);
1212 if (midiEvent.size == 0 || midiEvent.size >= MAX_MIDI_VALUE)
1213 continue;
1215 const uint8_t* const midiData(midiEvent.size > EngineMidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data);
1217 uint8_t status = uint8_t(MIDI_GET_STATUS_FROM_DATA(midiData));
1219 if ((status == MIDI_STATUS_NOTE_OFF || status == MIDI_STATUS_NOTE_ON) && (pData->options & PLUGIN_OPTION_SKIP_SENDING_NOTES))
1220 continue;
1221 if (status == MIDI_STATUS_CHANNEL_PRESSURE && (pData->options & PLUGIN_OPTION_SEND_CHANNEL_PRESSURE) == 0)
1222 continue;
1223 if (status == MIDI_STATUS_CONTROL_CHANGE && (pData->options & PLUGIN_OPTION_SEND_CONTROL_CHANGES) == 0)
1224 continue;
1225 if (status == MIDI_STATUS_POLYPHONIC_AFTERTOUCH && (pData->options & PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH) == 0)
1226 continue;
1227 if (status == MIDI_STATUS_PITCH_WHEEL_CONTROL && (pData->options & PLUGIN_OPTION_SEND_PITCHBEND) == 0)
1228 continue;
1230 // Fix bad note-off
1231 if (status == MIDI_STATUS_NOTE_ON && midiData[2] == 0)
1232 status = MIDI_STATUS_NOTE_OFF;
1234 fShmRtClientControl.writeOpcode(kPluginBridgeRtClientMidiEvent);
1235 fShmRtClientControl.writeUInt(event.time);
1236 fShmRtClientControl.writeByte(midiEvent.port);
1237 fShmRtClientControl.writeByte(midiEvent.size);
1239 fShmRtClientControl.writeByte(uint8_t(midiData[0] | (event.channel & MIDI_CHANNEL_BIT)));
1241 for (uint8_t j=1; j < midiEvent.size; ++j)
1242 fShmRtClientControl.writeByte(midiData[j]);
1244 fShmRtClientControl.commitWrite();
1246 if (status == MIDI_STATUS_NOTE_ON)
1248 pData->postponeNoteOnRtEvent(true, event.channel, midiData[1], midiData[2]);
1250 else if (status == MIDI_STATUS_NOTE_OFF)
1252 pData->postponeNoteOffRtEvent(true, event.channel, midiData[1]);
1254 } break;
1258 pData->postRtEvents.trySplice();
1260 } // End of Event Input
1262 if (! processSingle(audioIn, audioOut, frames))
1263 return;
1265 // --------------------------------------------------------------------------------------------------------
1266 // MIDI Output
1268 if (pData->event.portOut != nullptr)
1270 uint32_t time;
1271 uint8_t port, size;
1272 const uint8_t* midiData(fShmRtClientControl.data->midiOut);
1274 for (std::size_t read=0; read<kBridgeRtClientDataMidiOutSize-kBridgeBaseMidiOutHeaderSize;)
1276 // get time
1277 time = *(const uint32_t*)midiData;
1278 midiData += 4;
1280 // get port and size
1281 port = *midiData++;
1282 size = *midiData++;
1284 if (size == 0)
1285 break;
1287 // store midi data advancing as needed
1288 uint8_t data[size];
1290 for (uint8_t j=0; j<size; ++j)
1291 data[j] = *midiData++;
1293 pData->event.portOut->writeMidiEvent(time, size, data);
1295 read += kBridgeBaseMidiOutHeaderSize + size;
1298 // TODO
1299 (void)port;
1301 } // End of Control and MIDI Output
1304 bool processSingle(const float* const* const audioIn, float** const audioOut, const uint32_t frames)
1306 CARLA_SAFE_ASSERT_RETURN(! fTimedError, false);
1307 CARLA_SAFE_ASSERT_RETURN(frames > 0, false);
1308 CARLA_SAFE_ASSERT_RETURN(frames <= fBufferSize, false);
1310 if (pData->audioIn.count > 0)
1312 CARLA_SAFE_ASSERT_RETURN(audioIn != nullptr, false);
1314 if (pData->audioOut.count > 0)
1316 CARLA_SAFE_ASSERT_RETURN(audioOut != nullptr, false);
1319 // --------------------------------------------------------------------------------------------------------
1320 // Try lock, silence otherwise
1322 #ifndef STOAT_TEST_BUILD
1323 if (pData->engine->isOffline())
1325 pData->singleMutex.lock();
1327 else
1328 #endif
1329 if (! pData->singleMutex.tryLock())
1331 for (uint32_t i=0; i < pData->audioOut.count; ++i)
1332 carla_zeroFloats(audioOut[i], frames);
1333 return false;
1336 // --------------------------------------------------------------------------------------------------------
1337 // Reset audio buffers
1339 for (uint32_t i=0; i < fInfo.aIns; ++i)
1340 carla_copyFloats(fShmAudioPool.data + (i * fBufferSize), audioIn[i], frames);
1342 // --------------------------------------------------------------------------------------------------------
1343 // TimeInfo
1345 const EngineTimeInfo timeInfo(pData->engine->getTimeInfo());
1346 BridgeTimeInfo& bridgeTimeInfo(fShmRtClientControl.data->timeInfo);
1348 bridgeTimeInfo.playing = timeInfo.playing;
1349 bridgeTimeInfo.frame = timeInfo.frame;
1350 bridgeTimeInfo.usecs = timeInfo.usecs;
1351 bridgeTimeInfo.validFlags = timeInfo.bbt.valid ? kPluginBridgeTimeInfoValidBBT : 0x0;
1353 if (timeInfo.bbt.valid)
1355 bridgeTimeInfo.bar = timeInfo.bbt.bar;
1356 bridgeTimeInfo.beat = timeInfo.bbt.beat;
1357 bridgeTimeInfo.tick = timeInfo.bbt.tick;
1359 bridgeTimeInfo.beatsPerBar = timeInfo.bbt.beatsPerBar;
1360 bridgeTimeInfo.beatType = timeInfo.bbt.beatType;
1362 bridgeTimeInfo.ticksPerBeat = timeInfo.bbt.ticksPerBeat;
1363 bridgeTimeInfo.beatsPerMinute = timeInfo.bbt.beatsPerMinute;
1364 bridgeTimeInfo.barStartTick = timeInfo.bbt.barStartTick;
1367 // --------------------------------------------------------------------------------------------------------
1368 // Run plugin
1371 fShmRtClientControl.writeOpcode(kPluginBridgeRtClientProcess);
1372 fShmRtClientControl.writeUInt(frames);
1373 fShmRtClientControl.commitWrite();
1376 waitForClient("process", fProcWaitTime);
1378 if (fTimedOut)
1380 pData->singleMutex.unlock();
1381 return false;
1384 if (fShmRtClientControl.data->procFlags)
1386 fInitiated = false;
1387 fProcCanceled = true;
1390 for (uint32_t i=0; i < fInfo.aOuts; ++i)
1391 carla_copyFloats(audioOut[i], fShmAudioPool.data + ((i + fInfo.aIns) * fBufferSize), frames);
1393 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1394 // --------------------------------------------------------------------------------------------------------
1395 // Post-processing (dry/wet, volume and balance)
1398 const bool doVolume = (pData->hints & PLUGIN_CAN_VOLUME) != 0 && carla_isNotEqual(pData->postProc.volume, 1.0f);
1399 const bool doDryWet = (pData->hints & PLUGIN_CAN_DRYWET) != 0 && carla_isNotEqual(pData->postProc.dryWet, 1.0f);
1400 const bool doBalance = (pData->hints & PLUGIN_CAN_BALANCE) != 0 && ! (carla_isEqual(pData->postProc.balanceLeft, -1.0f) && carla_isEqual(pData->postProc.balanceRight, 1.0f));
1401 const bool isMono = (pData->audioIn.count == 1);
1403 bool isPair;
1404 float bufValue;
1405 float* const oldBufLeft = pData->postProc.extraBuffer;
1407 for (uint32_t i=0; i < pData->audioOut.count; ++i)
1409 // Dry/Wet
1410 if (doDryWet)
1412 const uint32_t c = isMono ? 0 : i;
1414 for (uint32_t k=0; k < frames; ++k)
1416 bufValue = audioIn[c][k];
1417 audioOut[i][k] = (audioOut[i][k] * pData->postProc.dryWet) + (bufValue * (1.0f - pData->postProc.dryWet));
1421 // Balance
1422 if (doBalance)
1424 isPair = (i % 2 == 0);
1426 if (isPair)
1428 CARLA_ASSERT(i+1 < pData->audioOut.count);
1429 carla_copyFloats(oldBufLeft, audioOut[i], frames);
1432 float balRangeL = (pData->postProc.balanceLeft + 1.0f)/2.0f;
1433 float balRangeR = (pData->postProc.balanceRight + 1.0f)/2.0f;
1435 for (uint32_t k=0; k < frames; ++k)
1437 if (isPair)
1439 // left
1440 audioOut[i][k] = oldBufLeft[k] * (1.0f - balRangeL);
1441 audioOut[i][k] += audioOut[i+1][k] * (1.0f - balRangeR);
1443 else
1445 // right
1446 audioOut[i][k] = audioOut[i][k] * balRangeR;
1447 audioOut[i][k] += oldBufLeft[k] * balRangeL;
1452 // Volume
1453 if (doVolume)
1455 for (uint32_t k=0; k < frames; ++k)
1456 audioOut[i][k] *= pData->postProc.volume;
1460 } // End of Post-processing
1461 #endif
1462 // --------------------------------------------------------------------------------------------------------
1464 pData->singleMutex.unlock();
1465 return true;
1468 void bufferSizeChanged(const uint32_t newBufferSize) override
1470 fBufferSize = newBufferSize;
1471 resizeAudioPool(newBufferSize);
1474 fShmRtClientControl.writeOpcode(kPluginBridgeRtClientSetBufferSize);
1475 fShmRtClientControl.writeUInt(newBufferSize);
1476 fShmRtClientControl.commitWrite();
1479 //fProcWaitTime = newBufferSize*1000/pData->engine->getSampleRate();
1480 fProcWaitTime = 1000;
1482 waitForClient("buffersize", 1000);
1485 void sampleRateChanged(const double newSampleRate) override
1488 fShmRtClientControl.writeOpcode(kPluginBridgeRtClientSetSampleRate);
1489 fShmRtClientControl.writeDouble(newSampleRate);
1490 fShmRtClientControl.commitWrite();
1493 //fProcWaitTime = pData->engine->getBufferSize()*1000/newSampleRate;
1494 fProcWaitTime = 1000;
1496 waitForClient("samplerate", 1000);
1499 void offlineModeChanged(const bool isOffline) override
1502 fShmRtClientControl.writeOpcode(kPluginBridgeRtClientSetOnline);
1503 fShmRtClientControl.writeBool(isOffline);
1504 fShmRtClientControl.commitWrite();
1507 waitForClient("offline", 1000);
1510 // -------------------------------------------------------------------
1511 // Post-poned UI Stuff
1513 // -------------------------------------------------------------------
1515 void handleNonRtData()
1517 for (; fShmNonRtServerControl.isDataAvailableForReading();)
1519 const PluginBridgeNonRtServerOpcode opcode(fShmNonRtServerControl.readOpcode());
1520 //#ifdef DEBUG
1521 if (opcode != kPluginBridgeNonRtServerPong)
1523 carla_debug("CarlaPluginJack::handleNonRtData() - got opcode: %s", PluginBridgeNonRtServerOpcode2str(opcode));
1525 //#endif
1526 switch (opcode)
1528 case kPluginBridgeNonRtServerNull:
1529 case kPluginBridgeNonRtServerPong:
1530 case kPluginBridgeNonRtServerPluginInfo1:
1531 case kPluginBridgeNonRtServerPluginInfo2:
1532 case kPluginBridgeNonRtServerAudioCount:
1533 case kPluginBridgeNonRtServerMidiCount:
1534 case kPluginBridgeNonRtServerCvCount:
1535 case kPluginBridgeNonRtServerParameterCount:
1536 case kPluginBridgeNonRtServerProgramCount:
1537 case kPluginBridgeNonRtServerMidiProgramCount:
1538 case kPluginBridgeNonRtServerPortName:
1539 case kPluginBridgeNonRtServerParameterData1:
1540 case kPluginBridgeNonRtServerParameterData2:
1541 case kPluginBridgeNonRtServerParameterRanges:
1542 case kPluginBridgeNonRtServerParameterValue:
1543 case kPluginBridgeNonRtServerParameterValue2:
1544 case kPluginBridgeNonRtServerParameterTouch:
1545 case kPluginBridgeNonRtServerDefaultValue:
1546 case kPluginBridgeNonRtServerCurrentProgram:
1547 case kPluginBridgeNonRtServerCurrentMidiProgram:
1548 case kPluginBridgeNonRtServerProgramName:
1549 case kPluginBridgeNonRtServerMidiProgramData:
1550 case kPluginBridgeNonRtServerSetCustomData:
1551 case kPluginBridgeNonRtServerVersion:
1552 case kPluginBridgeNonRtServerRespEmbedUI:
1553 case kPluginBridgeNonRtServerResizeEmbedUI:
1554 break;
1556 case kPluginBridgeNonRtServerSetChunkDataFile:
1557 // uint/size, str[] (filename)
1558 if (const uint32_t chunkFilePathSize = fShmNonRtServerControl.readUInt())
1560 char chunkFilePath[chunkFilePathSize];
1561 fShmNonRtServerControl.readCustomData(chunkFilePath, chunkFilePathSize);
1563 break;
1565 case kPluginBridgeNonRtServerSetLatency:
1566 case kPluginBridgeNonRtServerSetParameterText:
1567 break;
1569 case kPluginBridgeNonRtServerReady:
1570 fInitiated = true;
1571 break;
1573 case kPluginBridgeNonRtServerSaved:
1574 break;
1576 case kPluginBridgeNonRtServerUiClosed:
1577 pData->engine->callback(true, true,
1578 ENGINE_CALLBACK_UI_STATE_CHANGED,
1579 pData->id,
1581 0, 0, 0.0f, nullptr);
1582 break;
1584 case kPluginBridgeNonRtServerError: {
1585 // error
1586 const uint32_t errorSize(fShmNonRtServerControl.readUInt());
1587 char error[errorSize+1];
1588 carla_zeroChars(error, errorSize+1);
1589 fShmNonRtServerControl.readCustomData(error, errorSize);
1591 if (fInitiated)
1593 pData->engine->callback(true, true, ENGINE_CALLBACK_ERROR, pData->id, 0, 0, 0, 0.0f, error);
1595 // just in case
1596 pData->engine->setLastError(error);
1597 fInitError = true;
1599 else
1601 pData->engine->setLastError(error);
1602 fInitError = true;
1603 fInitiated = true;
1605 } break;
1610 // -------------------------------------------------------------------
1612 uintptr_t getUiBridgeProcessId() const noexcept override
1614 return fBridgeThread.getProcessID();
1617 // -------------------------------------------------------------------
1619 bool init(const CarlaPluginPtr plugin,
1620 const char* const filename, const char* const name, const char* const label, const uint options)
1622 CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr, false);
1624 // ---------------------------------------------------------------
1625 // first checks
1627 if (pData->client != nullptr)
1629 pData->engine->setLastError("Plugin client is already registered");
1630 return false;
1633 if (filename == nullptr || filename[0] == '\0')
1635 pData->engine->setLastError("null filename");
1636 return false;
1639 if (label == nullptr || label[0] == '\0')
1641 pData->engine->setLastError("null label");
1642 return false;
1645 // ---------------------------------------------------------------
1646 // check setup
1648 if (std::strlen(label) < 6)
1650 pData->engine->setLastError("invalid application setup received");
1651 return false;
1654 for (int i=4; --i >= 0;) {
1655 CARLA_SAFE_ASSERT_INT2_RETURN(label[i] >= '0' && label[i] <= '0'+64, i, label[i], false);
1657 CARLA_SAFE_ASSERT_INT2_RETURN(label[4] >= '0' && label[4] < '0'+0x4f, 4, label[4], false);
1658 CARLA_SAFE_ASSERT_UINT2_RETURN(static_cast<uchar>(label[5]) >= '0' &&
1659 static_cast<uchar>(label[5]) <= '0'+0x73,
1660 static_cast<uchar>(label[5]),
1661 static_cast<uchar>('0'+0x73),
1662 false);
1664 fInfo.aIns = static_cast<uint8_t>(label[0] - '0');
1665 fInfo.aOuts = static_cast<uint8_t>(label[1] - '0');
1666 fInfo.mIns = static_cast<uint8_t>(carla_minPositive(label[2] - '0', 1));
1667 fInfo.mOuts = static_cast<uint8_t>(carla_minPositive(label[3] - '0', 1));
1669 fInfo.setupLabel = label;
1671 // ---------------------------------------------------------------
1672 // set project unique id
1674 if (label[6] == '\0')
1675 setupUniqueProjectID();
1677 // ---------------------------------------------------------------
1678 // set icon
1680 pData->iconName = carla_strdup_safe("application");
1682 // ---------------------------------------------------------------
1683 // set info
1685 pData->filename = carla_strdup(filename);
1687 if (name != nullptr && name[0] != '\0')
1688 pData->name = pData->engine->getUniquePluginName(name);
1689 else
1690 pData->name = pData->engine->getUniquePluginName("Jack Application");
1692 std::srand(static_cast<uint>(std::time(nullptr)));
1694 // ---------------------------------------------------------------
1695 // init sem/shm
1697 if (! fShmAudioPool.initializeServer())
1699 carla_stderr("Failed to initialize shared memory audio pool");
1700 return false;
1703 if (! fShmRtClientControl.initializeServer())
1705 carla_stderr("Failed to initialize RT client control");
1706 fShmAudioPool.clear();
1707 return false;
1710 if (! fShmNonRtClientControl.initializeServer())
1712 carla_stderr("Failed to initialize Non-RT client control");
1713 fShmRtClientControl.clear();
1714 fShmAudioPool.clear();
1715 return false;
1718 if (! fShmNonRtServerControl.initializeServer())
1720 carla_stderr("Failed to initialize Non-RT server control");
1721 fShmNonRtClientControl.clear();
1722 fShmRtClientControl.clear();
1723 fShmAudioPool.clear();
1724 return false;
1727 // ---------------------------------------------------------------
1728 // setup hints and options
1730 fSetupHints = static_cast<uint>(static_cast<uchar>(label[5]) - '0');
1732 // FIXME dryWet broken
1733 pData->hints = PLUGIN_IS_BRIDGE;
1734 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1735 pData->hints |= /*PLUGIN_CAN_DRYWET |*/ PLUGIN_CAN_VOLUME | PLUGIN_CAN_BALANCE;
1736 #endif
1738 if (fSetupHints & LIBJACK_FLAG_CONTROL_WINDOW)
1739 pData->hints |= PLUGIN_HAS_CUSTOM_UI;
1741 // ---------------------------------------------------------------
1742 // init bridge thread
1745 char shmIdsStr[6*4+1];
1746 carla_zeroChars(shmIdsStr, 6*4+1);
1748 std::strncpy(shmIdsStr+6*0, &fShmAudioPool.filename[fShmAudioPool.filename.length()-6], 6);
1749 std::strncpy(shmIdsStr+6*1, &fShmRtClientControl.filename[fShmRtClientControl.filename.length()-6], 6);
1750 std::strncpy(shmIdsStr+6*2, &fShmNonRtClientControl.filename[fShmNonRtClientControl.filename.length()-6], 6);
1751 std::strncpy(shmIdsStr+6*3, &fShmNonRtServerControl.filename[fShmNonRtServerControl.filename.length()-6], 6);
1753 fBridgeThread.setData(shmIdsStr, fInfo.setupLabel);
1756 if (! restartBridgeThread())
1757 return false;
1759 // ---------------------------------------------------------------
1760 // register client
1762 if (pData->name == nullptr)
1763 pData->name = pData->engine->getUniquePluginName("unknown");
1765 pData->client = pData->engine->addClient(plugin);
1767 if (pData->client == nullptr || ! pData->client->isOk())
1769 pData->engine->setLastError("Failed to register plugin client");
1770 return false;
1773 // remove unprintable characters if needed
1774 if (fSetupHints & LIBJACK_FLAG_EXTERNAL_START)
1775 fInfo.setupLabel[5U] = static_cast<char>('0' + (fSetupHints ^ LIBJACK_FLAG_EXTERNAL_START));
1777 // ---------------------------------------------------------------
1778 // set options
1780 pData->options = PLUGIN_OPTION_FIXED_BUFFERS;
1782 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_CONTROL_CHANGES))
1783 pData->options |= PLUGIN_OPTION_SEND_CONTROL_CHANGES;
1785 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_CHANNEL_PRESSURE))
1786 pData->options |= PLUGIN_OPTION_SEND_CHANNEL_PRESSURE;
1788 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH))
1789 pData->options |= PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH;
1791 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_PITCHBEND))
1792 pData->options |= PLUGIN_OPTION_SEND_PITCHBEND;
1794 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_ALL_SOUND_OFF))
1795 pData->options |= PLUGIN_OPTION_SEND_ALL_SOUND_OFF;
1797 if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_PROGRAM_CHANGES))
1798 pData->options |= PLUGIN_OPTION_SEND_PROGRAM_CHANGES;
1800 if (isPluginOptionInverseEnabled(options, PLUGIN_OPTION_SKIP_SENDING_NOTES))
1801 pData->options |= PLUGIN_OPTION_SKIP_SENDING_NOTES;
1803 return true;
1806 private:
1807 bool fInitiated;
1808 bool fInitError;
1809 bool fTimedOut;
1810 bool fTimedError;
1811 bool fProcCanceled;
1812 uint fBufferSize;
1813 uint fProcWaitTime;
1814 uint fSetupHints;
1816 CarlaPluginJackThread fBridgeThread;
1818 BridgeAudioPool fShmAudioPool;
1819 BridgeRtClientControl fShmRtClientControl;
1820 BridgeNonRtClientControl fShmNonRtClientControl;
1821 BridgeNonRtServerControl fShmNonRtServerControl;
1823 struct Info {
1824 uint8_t aIns, aOuts;
1825 uint8_t mIns, mOuts;
1826 CarlaString setupLabel;
1827 std::vector<uint8_t> chunk;
1829 Info()
1830 : aIns(0),
1831 aOuts(0),
1832 mIns(0),
1833 mOuts(0),
1834 setupLabel(),
1835 chunk() {}
1837 CARLA_DECLARE_NON_COPYABLE(Info)
1838 } fInfo;
1840 void handleProcessStopped() noexcept
1842 const bool wasActive = pData->active;
1843 pData->active = false;
1845 if (wasActive)
1847 pData->engine->callback(true, true,
1848 ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED,
1849 pData->id,
1850 PARAMETER_ACTIVE,
1851 0, 0,
1852 0.0f,
1853 nullptr);
1856 if (pData->hints & PLUGIN_HAS_CUSTOM_UI)
1857 pData->engine->callback(true, true,
1858 ENGINE_CALLBACK_UI_STATE_CHANGED,
1859 pData->id,
1861 0, 0, 0.0f, nullptr);
1864 void resizeAudioPool(const uint32_t bufferSize)
1866 fShmAudioPool.resize(bufferSize, static_cast<uint32_t>(fInfo.aIns+fInfo.aOuts), 0);
1868 fShmRtClientControl.writeOpcode(kPluginBridgeRtClientSetAudioPool);
1869 fShmRtClientControl.writeULong(static_cast<uint64_t>(fShmAudioPool.dataSize));
1870 fShmRtClientControl.commitWrite();
1872 waitForClient("resize-pool", 5000);
1875 void setupUniqueProjectID()
1877 const char* const engineProjectFolder = pData->engine->getCurrentProjectFolder();
1878 carla_stdout("setupUniqueProjectID %s", engineProjectFolder);
1880 if (engineProjectFolder == nullptr || engineProjectFolder[0] == '\0')
1881 return;
1883 const File file(engineProjectFolder);
1884 CARLA_SAFE_ASSERT_RETURN(file.exists(),);
1886 char code[6];
1887 code[5] = '\0';
1889 String child;
1891 for (;;)
1893 static const char* const kValidChars =
1894 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1895 "abcdefghijklmnopqrstuvwxyz"
1896 "0123456789";
1898 static const size_t kValidCharsLen(std::strlen(kValidChars)-1U);
1900 code[0] = kValidChars[safe_rand(kValidCharsLen)];
1901 code[1] = kValidChars[safe_rand(kValidCharsLen)];
1902 code[2] = kValidChars[safe_rand(kValidCharsLen)];
1903 code[3] = kValidChars[safe_rand(kValidCharsLen)];
1904 code[4] = kValidChars[safe_rand(kValidCharsLen)];
1906 child = pData->name;
1907 child += ".";
1908 child += code;
1909 const File newFile(file.getChildFile(child));
1911 if (newFile.existsAsFile())
1912 continue;
1914 fInfo.setupLabel += code;
1915 carla_stdout("new label %s", fInfo.setupLabel.buffer());
1916 break;
1920 bool restartBridgeThread()
1922 fInitiated = false;
1923 fInitError = false;
1924 fTimedError = false;
1926 // reset memory
1927 fProcCanceled = false;
1928 fShmRtClientControl.data->procFlags = 0;
1929 carla_zeroStruct(fShmRtClientControl.data->timeInfo);
1930 carla_zeroBytes(fShmRtClientControl.data->midiOut, kBridgeRtClientDataMidiOutSize);
1932 fShmRtClientControl.clearData();
1933 fShmNonRtClientControl.clearData();
1934 fShmNonRtServerControl.clearData();
1936 // initial values
1937 fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientVersion);
1938 fShmNonRtClientControl.writeUInt(CARLA_PLUGIN_BRIDGE_API_VERSION_CURRENT);
1940 fShmNonRtClientControl.writeUInt(static_cast<uint32_t>(sizeof(BridgeRtClientData)));
1941 fShmNonRtClientControl.writeUInt(static_cast<uint32_t>(sizeof(BridgeNonRtClientData)));
1942 fShmNonRtClientControl.writeUInt(static_cast<uint32_t>(sizeof(BridgeNonRtServerData)));
1944 fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientInitialSetup);
1945 fShmNonRtClientControl.writeUInt(pData->engine->getBufferSize());
1946 fShmNonRtClientControl.writeDouble(pData->engine->getSampleRate());
1948 fShmNonRtClientControl.commitWrite();
1950 if (fShmAudioPool.dataSize != 0)
1952 fShmRtClientControl.writeOpcode(kPluginBridgeRtClientSetAudioPool);
1953 fShmRtClientControl.writeULong(static_cast<uint64_t>(fShmAudioPool.dataSize));
1954 fShmRtClientControl.commitWrite();
1956 else
1958 // testing dummy message
1959 fShmRtClientControl.writeOpcode(kPluginBridgeRtClientNull);
1960 fShmRtClientControl.commitWrite();
1963 fBridgeThread.startThread();
1965 const bool needsCancelableAction = ! pData->engine->isLoadingProject();
1966 const bool needsEngineIdle = pData->engine->getType() != kEngineTypePlugin;
1968 CarlaString actionName;
1970 if (needsCancelableAction)
1972 if (fSetupHints & LIBJACK_FLAG_EXTERNAL_START)
1974 const EngineOptions& options(pData->engine->getOptions());
1975 CarlaString binaryDir(options.binaryDir);
1977 char* const hwVars = fBridgeThread.getEnvVarsToExport();
1979 actionName = "Waiting for external JACK application start, please use the following environment variables:\n";
1980 actionName += hwVars;
1982 delete[] hwVars;
1984 else
1986 actionName = "Loading JACK application";
1989 pData->engine->setActionCanceled(false);
1990 pData->engine->callback(true, true,
1991 ENGINE_CALLBACK_CANCELABLE_ACTION,
1992 pData->id,
1994 0, 0, 0.0f,
1995 actionName.buffer());
1998 for (;fBridgeThread.isThreadRunning();)
2000 pData->engine->callback(true, false, ENGINE_CALLBACK_IDLE, 0, 0, 0, 0, 0.0f, nullptr);
2002 if (needsEngineIdle)
2003 pData->engine->idle();
2005 idle();
2007 if (fInitiated)
2008 break;
2009 if (pData->engine->isAboutToClose() || pData->engine->wasActionCanceled())
2010 break;
2012 carla_msleep(5);
2015 if (needsCancelableAction)
2017 pData->engine->callback(true, true,
2018 ENGINE_CALLBACK_CANCELABLE_ACTION,
2019 pData->id,
2021 0, 0, 0.0f,
2022 actionName.buffer());
2025 if (fInitError || ! fInitiated)
2027 fBridgeThread.stopThread(6000);
2029 if (! fInitError)
2030 pData->engine->setLastError("Timeout while waiting for a response from plugin-bridge\n"
2031 "(or the plugin crashed on initialization?)");
2033 return false;
2036 return true;
2039 void waitForClient(const char* const action, const uint msecs)
2041 CARLA_SAFE_ASSERT_RETURN(! fTimedOut,);
2042 CARLA_SAFE_ASSERT_RETURN(! fTimedError,);
2044 if (fShmRtClientControl.waitForClient(msecs))
2045 return;
2047 fTimedOut = true;
2048 carla_stderr2("waitForClient(%s) timed out", action);
2051 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginJack)
2054 CARLA_BACKEND_END_NAMESPACE
2056 #endif // CARLA_OS_LINUX
2058 // -------------------------------------------------------------------------------------------------------------------
2060 CARLA_BACKEND_START_NAMESPACE
2062 CarlaPluginPtr CarlaPlugin::newJackApp(const Initializer& init)
2064 carla_debug("CarlaPlugin::newJackApp({%p, \"%s\", \"%s\", \"%s\"})",
2065 init.engine, init.filename, init.name, init.label);
2067 #if defined(CARLA_OS_LINUX) || defined(CARLA_OS_MAC)
2068 std::shared_ptr<CarlaPluginJack> plugin(new CarlaPluginJack(init.engine, init.id));
2070 if (! plugin->init(plugin, init.filename, init.name, init.label, init.options))
2071 return nullptr;
2073 return plugin;
2074 #else
2075 init.engine->setLastError("JACK Application support not available");
2076 return nullptr;
2077 #endif
2080 CARLA_BACKEND_END_NAMESPACE
2082 // -------------------------------------------------------------------------------------------------------------------