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"
34 # include "CarlaOscUtils.hpp"
36 # warning No liblo support, NSM (session state) will not be available
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"
49 // -------------------------------------------------------------------------------------------------------------------
51 using water::ChildProcess
;
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 // -------------------------------------------------------------------------------------------------------------------
69 static const ExternalMidiNote kExternalMidiNoteFallback
= { -1, 0, 0 };
71 // -------------------------------------------------------------------------------------------------------------------
74 virtual ~Announcer() {}
75 virtual void nsmAnnounced(bool hasGui
) = 0;
78 class CarlaPluginJackThread
: public CarlaThread
81 CarlaPluginJackThread(Announcer
* const ann
, CarlaEngine
* const engine
, CarlaPlugin
* const plugin
) noexcept
82 : CarlaThread("CarlaPluginJackThread"),
89 fOscClientAddress(nullptr),
91 fHasOptionalGui(false),
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());
103 fSetupLabel
= setupLabel
;
106 uintptr_t getProcessID() const noexcept
108 CARLA_SAFE_ASSERT_RETURN(fProcess
!= nullptr, 0);
110 return (uintptr_t)fProcess
->getPID();
114 void nsmSave(const char* const setupLabel
)
116 if (fOscClientAddress
== nullptr)
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
)
132 lo_send_from(fOscClientAddress
, fOscServer
, LO_TT_IMMEDIATE
,
133 yesNo
? "/nsm/client/show_optional_gui"
134 : "/nsm/client/hide_optional_gui", "");
140 char* getEnvVarsToExport()
142 const EngineOptions
& options(kEngine
->getOptions());
143 CarlaString
binaryDir(options
.binaryDir
);
145 const int sessionManager
= fSetupLabel
[4U] - '0';
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";
154 ret
+= "export LD_LIBRARY_PATH=" + binaryDir
+ "/jack\n";
155 ret
+= "export LD_PRELOAD=" + binaryDir
+ "/libcarla_interposer-jack-x11.so\n";
158 if (sessionManager
== LIBJACK_SESSION_MANAGER_NSM
)
160 for (int i
=50; fOscServer
== nullptr && --i
>=0;)
163 ret
+= "export NSM_URL=";
164 ret
+= lo_server_get_url(fOscServer
);
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();
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)
198 if ((announced
|| fProject
.path
.isEmpty()) && fProject
.init(kPlugin
->getName(),
199 kEngine
->getCurrentProjectFolder(),
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);
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
,
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
,
287 0, 0, 0.0f
, nullptr);
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
,
308 0, 0, 0.0f
, nullptr);
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
)
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);
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());
356 CARLA_SAFE_ASSERT_RETURN(filename
.isNotEmpty(),);
358 StringArray arguments
;
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");
371 const CarlaString
ldpreload(CarlaString(options
.binaryDir
) + "/libcarla_interposer-jack-x11.dylib");
373 const CarlaString
ldpreload(CarlaString(options
.binaryDir
) + "/libcarla_interposer-jack-x11.so");
376 const ScopedEngineEnvironmentLocker
_seel(kEngine
);
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");
383 const CarlaScopedEnvVar
sev2("LD_LIBRARY_PATH", libjackdir
.buffer());
384 const CarlaScopedEnvVar
sev1("LD_PRELOAD", ldpreload
.isNotEmpty() ? ldpreload
.buffer() : nullptr);
387 const CarlaScopedEnvVar
sev3("NSM_URL", lo_server_get_url(fOscServer
));
390 if (kPlugin
->getHints() & PLUGIN_HAS_CUSTOM_UI
)
391 carla_setenv("CARLA_FRONTEND_WIN_ID", winIdStr
);
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!");
406 for (; (externalProcess
|| fProcess
->isRunning()) && ! shouldThreadExit();)
409 if (sessionManager
== LIBJACK_SESSION_MANAGER_NSM
)
411 lo_server_recv_noblock(fOscServer
, 50);
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;
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");
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
,
470 Announcer
* const kAnnouncer
;
471 CarlaEngine
* const kEngine
;
472 CarlaPlugin
* const kPlugin
;
475 CarlaString fSetupLabel
;
478 lo_address fOscClientAddress
;
479 lo_server fOscServer
;
480 bool fHasOptionalGui
;
486 CarlaString 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
);
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();
515 CARLA_DECLARE_NON_COPYABLE(ProjectData
)
519 CarlaScopedPointer
<ChildProcess
> fProcess
;
521 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginJackThread
)
524 // -------------------------------------------------------------------------------------------------------------------
526 class CarlaPluginJack
: public CarlaPlugin
,
530 CarlaPluginJack(CarlaEngine
* const engine
, const uint id
)
531 : CarlaPlugin(engine
, id
),
536 fProcCanceled(false),
537 fBufferSize(engine
->getBufferSize()),
540 fBridgeThread(this, engine
, this),
542 fShmRtClientControl(),
543 fShmNonRtClientControl(),
544 fShmNonRtServerControl(),
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
558 if (pData
->hints
& PLUGIN_HAS_CUSTOM_UI
)
559 pData
->transientTryCounter
= 0;
562 pData
->singleMutex
.lock();
563 pData
->masterMutex
.lock();
565 if (pData
->client
!= nullptr && pData
->client
->isActive())
566 pData
->client
->deactivate(true);
571 pData
->active
= false;
574 if (fBridgeThread
.isThreadRunning())
576 fShmRtClientControl
.writeOpcode(kPluginBridgeRtClientQuit
);
577 fShmRtClientControl
.commitWrite();
579 fShmNonRtClientControl
.writeOpcode(kPluginBridgeNonRtClientQuit
);
580 fShmNonRtClientControl
.commitWrite();
583 waitForClient("stopping", 3000);
586 fBridgeThread
.stopThread(3000);
588 fShmNonRtServerControl
.clear();
589 fShmNonRtClientControl
.clear();
590 fShmRtClientControl
.clear();
591 fShmAudioPool
.clear();
598 // -------------------------------------------------------------------
600 void nsmAnnounced(bool hasGui
) override
602 if (hasGui
|| (pData
->hints
& PLUGIN_HAS_CUSTOM_UI
) == 0x0)
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
,
616 0, 0, 0.0f
, nullptr);
619 // -------------------------------------------------------------------
620 // Information (base)
622 PluginType
getType() const noexcept override
627 PluginCategory
getCategory() const noexcept override
629 return PLUGIN_CATEGORY_NONE
;
632 // -------------------------------------------------------------------
633 // Information (count)
635 uint32_t getMidiInCount() const noexcept override
640 uint32_t getMidiOutCount() const noexcept override
645 // -------------------------------------------------------------------
646 // Information (current data)
648 // -------------------------------------------------------------------
649 // Information (per-plugin data)
651 uint
getOptionsAvailable() const noexcept override
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
;
669 bool getLabel(char* const strBuf
) const noexcept override
671 std::strncpy(strBuf
, fInfo
.setupLabel
, STR_MAX
);
675 bool getMaker(char* const) const noexcept override
680 bool getCopyright(char* const) const noexcept override
685 bool getRealName(char* const strBuf
) const noexcept override
688 std::strncpy(strBuf
, "Carla's libjack", STR_MAX
);
692 // -------------------------------------------------------------------
695 void prepareForSave(bool) noexcept override
698 if (fInfo
.setupLabel
.length() == 6)
699 setupUniqueProjectID();
703 const CarlaMutexLocker
_cml(fShmNonRtClientControl
.mutex
);
705 fShmNonRtClientControl
.writeOpcode(kPluginBridgeNonRtClientPrepareForSave
);
706 fShmNonRtClientControl
.commitWrite();
710 fBridgeThread
.nsmSave(fInfo
.setupLabel
);
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)
761 const CarlaMutexLocker
_cml(fShmNonRtClientControl
.mutex
);
763 fShmNonRtClientControl
.writeOpcode(kPluginBridgeNonRtClientPingOnOff
);
764 fShmNonRtClientControl
.writeBool(std::strcmp(value
, "true") == 0);
765 fShmNonRtClientControl
.commitWrite();
770 CarlaPlugin::setCustomData(type
, key
, value
, sendGui
);
773 // -------------------------------------------------------------------
776 void showCustomUI(const bool yesNo
) override
778 if (yesNo
&& ! fBridgeThread
.isThreadRunning()) {
779 CARLA_SAFE_ASSERT_RETURN(restartBridgeThread(),);
783 if (! fBridgeThread
.nsmShowGui(yesNo
))
786 const CarlaMutexLocker
_cml(fShmNonRtClientControl
.mutex
);
788 fShmNonRtClientControl
.writeOpcode(yesNo
? kPluginBridgeNonRtClientShowUI
: kPluginBridgeNonRtClientHideUI
);
789 fShmNonRtClientControl
.commitWrite();
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();
809 } CARLA_SAFE_EXCEPTION("handleNonRtData");
816 handleProcessStopped();
818 else if (fProcCanceled
)
820 handleProcessStopped();
821 fProcCanceled
= false;
827 // -------------------------------------------------------------------
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;
850 pData
->audioIn
.createNew(fInfo
.aIns
);
855 pData
->audioOut
.createNew(fInfo
.aOuts
);
865 const uint
portNameSize(pData
->engine
->getMaxPortNameSize());
866 CarlaString portName
;
869 for (uint8_t j
=0; j
< fInfo
.aIns
; ++j
)
873 if (processMode
== ENGINE_PROCESS_MODE_SINGLE_CLIENT
)
875 portName
= pData
->name
;
881 portName
+= "audio_in_";
882 portName
+= CarlaString(j
+1);
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
;
896 for (uint8_t j
=0; j
< fInfo
.aOuts
; ++j
)
900 if (processMode
== ENGINE_PROCESS_MODE_SINGLE_CLIENT
)
902 portName
= pData
->name
;
908 portName
+= "audio_out_";
909 portName
+= CarlaString(j
+1);
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
;
926 if (processMode
== ENGINE_PROCESS_MODE_SINGLE_CLIENT
)
928 portName
= pData
->name
;
932 portName
+= "event-in";
933 portName
.truncate(portNameSize
);
935 pData
->event
.portIn
= (CarlaEngineEventPort
*)pData
->client
->addPort(kEnginePortTypeEvent
, portName
, true, 0);
942 if (processMode
== ENGINE_PROCESS_MODE_SINGLE_CLIENT
)
944 portName
= pData
->name
;
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;
958 pData
->extraHints
|= PLUGIN_EXTRA_HINT_HAS_MIDI_IN
;
961 pData
->extraHints
|= PLUGIN_EXTRA_HINT_HAS_MIDI_OUT
;
963 bufferSizeChanged(pData
->engine
->getBufferSize());
964 reloadPrograms(true);
966 carla_debug("CarlaPluginJack::reload() - end");
969 // -------------------------------------------------------------------
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();
991 waitForClient("activate", 2000);
992 } CARLA_SAFE_EXCEPTION("activate - waitForClient");
995 void deactivate() noexcept override
997 if (! fBridgeThread
.isThreadRunning())
1000 CARLA_SAFE_ASSERT_RETURN(! fTimedError
,);
1003 const CarlaMutexLocker
_cml(fShmNonRtClientControl
.mutex
);
1005 fShmNonRtClientControl
.writeOpcode(kPluginBridgeNonRtClientDeactivate
);
1006 fShmNonRtClientControl
.commitWrite();
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 // --------------------------------------------------------------------------------------------------------
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
);
1030 // --------------------------------------------------------------------------------------------------------
1031 // Check if needs reset
1033 if (pData
->needsReset
)
1037 pData
->needsReset
= false;
1040 // --------------------------------------------------------------------------------------------------------
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
));
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;
1081 for (uint32_t i
=0, numEvents
=pData
->event
.portIn
->getEventCount(); i
< numEvents
; ++i
)
1083 const EngineEvent
& event(pData
->event
.portIn
->getEvent(i
));
1088 case kEngineEventTypeNull
:
1091 case kEngineEventTypeControl
: {
1092 const EngineControlEvent
& ctrlEvent
= event
.ctrl
;
1094 switch (ctrlEvent
.type
)
1096 case kEngineControlEventTypeNull
:
1099 case kEngineControlEventTypeParameter
:
1100 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
1101 // Control backend stuff
1102 if (event
.channel
== pData
->ctrlChannel
)
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)
1121 value
= ctrlEvent
.normalizedValue
/0.5f
- 1.0f
;
1126 right
= (value
*2.0f
)+1.0f
;
1128 else if (value
> 0.0f
)
1130 left
= (value
*2.0f
)-1.0f
;
1139 setBalanceLeftRT(left
, true);
1140 setBalanceRightRT(right
, true);
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
));
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();
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();
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();
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();
1199 fShmRtClientControl
.writeOpcode(kPluginBridgeRtClientControlEventAllNotesOff
);
1200 fShmRtClientControl
.writeUInt(event
.time
);
1201 fShmRtClientControl
.writeByte(event
.channel
);
1202 fShmRtClientControl
.commitWrite();
1205 } // switch (ctrlEvent.type)
1207 } // case kEngineEventTypeControl
1209 case kEngineEventTypeMidi
: {
1210 const EngineMidiEvent
& midiEvent(event
.midi
);
1212 if (midiEvent
.size
== 0 || midiEvent
.size
>= MAX_MIDI_VALUE
)
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
))
1221 if (status
== MIDI_STATUS_CHANNEL_PRESSURE
&& (pData
->options
& PLUGIN_OPTION_SEND_CHANNEL_PRESSURE
) == 0)
1223 if (status
== MIDI_STATUS_CONTROL_CHANGE
&& (pData
->options
& PLUGIN_OPTION_SEND_CONTROL_CHANGES
) == 0)
1225 if (status
== MIDI_STATUS_POLYPHONIC_AFTERTOUCH
&& (pData
->options
& PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH
) == 0)
1227 if (status
== MIDI_STATUS_PITCH_WHEEL_CONTROL
&& (pData
->options
& PLUGIN_OPTION_SEND_PITCHBEND
) == 0)
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]);
1258 pData
->postRtEvents
.trySplice();
1260 } // End of Event Input
1262 if (! processSingle(audioIn
, audioOut
, frames
))
1265 // --------------------------------------------------------------------------------------------------------
1268 if (pData
->event
.portOut
!= nullptr)
1272 const uint8_t* midiData(fShmRtClientControl
.data
->midiOut
);
1274 for (std::size_t read
=0; read
<kBridgeRtClientDataMidiOutSize
-kBridgeBaseMidiOutHeaderSize
;)
1277 time
= *(const uint32_t*)midiData
;
1280 // get port and size
1287 // store midi data advancing as needed
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
;
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();
1329 if (! pData
->singleMutex
.tryLock())
1331 for (uint32_t i
=0; i
< pData
->audioOut
.count
; ++i
)
1332 carla_zeroFloats(audioOut
[i
], frames
);
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 // --------------------------------------------------------------------------------------------------------
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 // --------------------------------------------------------------------------------------------------------
1371 fShmRtClientControl
.writeOpcode(kPluginBridgeRtClientProcess
);
1372 fShmRtClientControl
.writeUInt(frames
);
1373 fShmRtClientControl
.commitWrite();
1376 waitForClient("process", fProcWaitTime
);
1380 pData
->singleMutex
.unlock();
1384 if (fShmRtClientControl
.data
->procFlags
)
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);
1405 float* const oldBufLeft
= pData
->postProc
.extraBuffer
;
1407 for (uint32_t i
=0; i
< pData
->audioOut
.count
; ++i
)
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
));
1424 isPair
= (i
% 2 == 0);
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
)
1440 audioOut
[i
][k
] = oldBufLeft
[k
] * (1.0f
- balRangeL
);
1441 audioOut
[i
][k
] += audioOut
[i
+1][k
] * (1.0f
- balRangeR
);
1446 audioOut
[i
][k
] = audioOut
[i
][k
] * balRangeR
;
1447 audioOut
[i
][k
] += oldBufLeft
[k
] * balRangeL
;
1455 for (uint32_t k
=0; k
< frames
; ++k
)
1456 audioOut
[i
][k
] *= pData
->postProc
.volume
;
1460 } // End of Post-processing
1462 // --------------------------------------------------------------------------------------------------------
1464 pData
->singleMutex
.unlock();
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());
1521 if (opcode
!= kPluginBridgeNonRtServerPong
)
1523 carla_debug("CarlaPluginJack::handleNonRtData() - got opcode: %s", PluginBridgeNonRtServerOpcode2str(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
:
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
);
1565 case kPluginBridgeNonRtServerSetLatency
:
1566 case kPluginBridgeNonRtServerSetParameterText
:
1569 case kPluginBridgeNonRtServerReady
:
1573 case kPluginBridgeNonRtServerSaved
:
1576 case kPluginBridgeNonRtServerUiClosed
:
1577 pData
->engine
->callback(true, true,
1578 ENGINE_CALLBACK_UI_STATE_CHANGED
,
1581 0, 0, 0.0f
, nullptr);
1584 case kPluginBridgeNonRtServerError
: {
1586 const uint32_t errorSize(fShmNonRtServerControl
.readUInt());
1587 char error
[errorSize
+1];
1588 carla_zeroChars(error
, errorSize
+1);
1589 fShmNonRtServerControl
.readCustomData(error
, errorSize
);
1593 pData
->engine
->callback(true, true, ENGINE_CALLBACK_ERROR
, pData
->id
, 0, 0, 0, 0.0f
, error
);
1596 pData
->engine
->setLastError(error
);
1601 pData
->engine
->setLastError(error
);
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 // ---------------------------------------------------------------
1627 if (pData
->client
!= nullptr)
1629 pData
->engine
->setLastError("Plugin client is already registered");
1633 if (filename
== nullptr || filename
[0] == '\0')
1635 pData
->engine
->setLastError("null filename");
1639 if (label
== nullptr || label
[0] == '\0')
1641 pData
->engine
->setLastError("null label");
1645 // ---------------------------------------------------------------
1648 if (std::strlen(label
) < 6)
1650 pData
->engine
->setLastError("invalid application setup received");
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),
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 // ---------------------------------------------------------------
1680 pData
->iconName
= carla_strdup_safe("application");
1682 // ---------------------------------------------------------------
1685 pData
->filename
= carla_strdup(filename
);
1687 if (name
!= nullptr && name
[0] != '\0')
1688 pData
->name
= pData
->engine
->getUniquePluginName(name
);
1690 pData
->name
= pData
->engine
->getUniquePluginName("Jack Application");
1692 std::srand(static_cast<uint
>(std::time(nullptr)));
1694 // ---------------------------------------------------------------
1697 if (! fShmAudioPool
.initializeServer())
1699 carla_stderr("Failed to initialize shared memory audio pool");
1703 if (! fShmRtClientControl
.initializeServer())
1705 carla_stderr("Failed to initialize RT client control");
1706 fShmAudioPool
.clear();
1710 if (! fShmNonRtClientControl
.initializeServer())
1712 carla_stderr("Failed to initialize Non-RT client control");
1713 fShmRtClientControl
.clear();
1714 fShmAudioPool
.clear();
1718 if (! fShmNonRtServerControl
.initializeServer())
1720 carla_stderr("Failed to initialize Non-RT server control");
1721 fShmNonRtClientControl
.clear();
1722 fShmRtClientControl
.clear();
1723 fShmAudioPool
.clear();
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
;
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())
1759 // ---------------------------------------------------------------
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");
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 // ---------------------------------------------------------------
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
;
1816 CarlaPluginJackThread fBridgeThread
;
1818 BridgeAudioPool fShmAudioPool
;
1819 BridgeRtClientControl fShmRtClientControl
;
1820 BridgeNonRtClientControl fShmNonRtClientControl
;
1821 BridgeNonRtServerControl fShmNonRtServerControl
;
1824 uint8_t aIns
, aOuts
;
1825 uint8_t mIns
, mOuts
;
1826 CarlaString setupLabel
;
1827 std::vector
<uint8_t> chunk
;
1837 CARLA_DECLARE_NON_COPYABLE(Info
)
1840 void handleProcessStopped() noexcept
1842 const bool wasActive
= pData
->active
;
1843 pData
->active
= false;
1847 pData
->engine
->callback(true, true,
1848 ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED
,
1856 if (pData
->hints
& PLUGIN_HAS_CUSTOM_UI
)
1857 pData
->engine
->callback(true, true,
1858 ENGINE_CALLBACK_UI_STATE_CHANGED
,
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')
1883 const File
file(engineProjectFolder
);
1884 CARLA_SAFE_ASSERT_RETURN(file
.exists(),);
1893 static const char* const kValidChars
=
1894 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1895 "abcdefghijklmnopqrstuvwxyz"
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
;
1909 const File
newFile(file
.getChildFile(child
));
1911 if (newFile
.existsAsFile())
1914 fInfo
.setupLabel
+= code
;
1915 carla_stdout("new label %s", fInfo
.setupLabel
.buffer());
1920 bool restartBridgeThread()
1924 fTimedError
= false;
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();
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();
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
;
1986 actionName
= "Loading JACK application";
1989 pData
->engine
->setActionCanceled(false);
1990 pData
->engine
->callback(true, true,
1991 ENGINE_CALLBACK_CANCELABLE_ACTION
,
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();
2009 if (pData
->engine
->isAboutToClose() || pData
->engine
->wasActionCanceled())
2015 if (needsCancelableAction
)
2017 pData
->engine
->callback(true, true,
2018 ENGINE_CALLBACK_CANCELABLE_ACTION
,
2022 actionName
.buffer());
2025 if (fInitError
|| ! fInitiated
)
2027 fBridgeThread
.stopThread(6000);
2030 pData
->engine
->setLastError("Timeout while waiting for a response from plugin-bridge\n"
2031 "(or the plugin crashed on initialization?)");
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
))
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
))
2075 init
.engine
->setLastError("JACK Application support not available");
2080 CARLA_BACKEND_END_NAMESPACE
2082 // -------------------------------------------------------------------------------------------------------------------