diff --git a/source/backend/plugin/CarlaPluginCLAP.cpp b/source/backend/plugin/CarlaPluginCLAP.cpp index ba9ca7c5d..e646f4a48 100644 --- a/source/backend/plugin/CarlaPluginCLAP.cpp +++ b/source/backend/plugin/CarlaPluginCLAP.cpp @@ -18,17 +18,597 @@ #include "CarlaPluginInternal.hpp" #include "CarlaEngine.hpp" +#include "CarlaBackendUtils.hpp" +#include "CarlaClapUtils.hpp" +#include "CarlaMathUtils.hpp" + +#include "CarlaPluginUI.hpp" + +#include "water/files/File.h" + CARLA_BACKEND_START_NAMESPACE // -------------------------------------------------------------------------------------------------------------------- +struct carla_clap_host : clap_host_t { + carla_clap_host() + { + clap_version = CLAP_VERSION; + host_data = this; + name = "Carla"; + vendor = "falkTX"; + url = "https://kx.studio/carla"; + version = CARLA_VERSION_STRING; + get_extension = carla_get_extension; + request_restart = carla_request_restart; + request_process = carla_request_process; + request_callback = carla_request_callback; + } + + static const void* carla_get_extension(const clap_host_t*, const char*) { return nullptr; } + static void carla_request_restart(const clap_host_t*) {} + static void carla_request_process(const clap_host_t*) {} + static void carla_request_callback(const clap_host_t*) {} +}; + +// -------------------------------------------------------------------------------------------------------------------- + +class CarlaPluginCLAP : public CarlaPlugin, + private CarlaPluginUI::Callback +{ +public: + CarlaPluginCLAP(CarlaEngine* const engine, const uint id) + : CarlaPlugin(engine, id), + fPlugin(nullptr), + fPluginDescriptor(nullptr), + fPluginEntry(nullptr), + fHost() + { + carla_debug("CarlaPluginCLAP::CarlaPluginCLAP(%p, %i)", engine, id); + + } + + ~CarlaPluginCLAP() override + { + carla_debug("CarlaPluginCLAP::~CarlaPluginCLAP()"); + + // close UI +// if (pData->hints & PLUGIN_HAS_CUSTOM_UI) +// { +// if (! fUI.isEmbed) +// showCustomUI(false); +// +// if (fUI.isOpen) +// { +// fUI.isOpen = false; +// dispatcher(effEditClose); +// } +// } + + pData->singleMutex.lock(); + pData->masterMutex.lock(); + + if (pData->client != nullptr && pData->client->isActive()) + pData->client->deactivate(true); + +// CARLA_ASSERT(! fIsProcessing); + + if (pData->active) + { + deactivate(); + pData->active = false; + } + + if (fPlugin != nullptr) + { + fPlugin->destroy(fPlugin); + fPlugin = nullptr; + } + + clearBuffers(); + + if (fPluginEntry != nullptr) + { + fPluginEntry->deinit(); + fPluginEntry = nullptr; + } + } + + // ------------------------------------------------------------------- + // Information (base) + + PluginType getType() const noexcept override + { + return PLUGIN_CLAP; + } + + /* + PluginCategory getCategory() const noexcept override + { + } + + uint32_t getLatencyInFrames() const noexcept override + { + } + */ + + // ------------------------------------------------------------------- + // Information (count) + + // nothing + + // ------------------------------------------------------------------- + // Information (current data) + + /* + std::size_t getChunkData(void** const dataPtr) noexcept override + { + } + */ + + // ------------------------------------------------------------------- + // Information (per-plugin data) + + /* + uint getOptionsAvailable() const noexcept override + { + } + + float getParameterValue(const uint32_t parameterId) const noexcept override + { + } + */ + + bool getLabel(char* const strBuf) const noexcept override + { + CARLA_SAFE_ASSERT_RETURN(fPluginDescriptor != nullptr, false); + + std::strncpy(strBuf, fPluginDescriptor->id, STR_MAX); + return true; + } + + bool getMaker(char* const strBuf) const noexcept override + { + CARLA_SAFE_ASSERT_RETURN(fPluginDescriptor != nullptr, false); + + std::strncpy(strBuf, fPluginDescriptor->vendor, STR_MAX); + return true; + } + + bool getCopyright(char* const strBuf) const noexcept override + { + return getMaker(strBuf); + } + + bool getRealName(char* const strBuf) const noexcept override + { + CARLA_SAFE_ASSERT_RETURN(fPluginDescriptor != nullptr, false); + + std::strncpy(strBuf, fPluginDescriptor->name, STR_MAX); + return true; + } + + /* + bool getParameterName(const uint32_t parameterId, char* const strBuf) const noexcept override + { + } + + bool getParameterText(const uint32_t parameterId, char* const strBuf) noexcept override + { + } + + bool getParameterUnit(const uint32_t parameterId, char* const strBuf) const noexcept override + { + } + + bool getParameterGroupName(const uint32_t parameterId, char* const strBuf) const noexcept override + { + } + */ + + // ------------------------------------------------------------------- + // Set data (state) + + // nothing + + // ------------------------------------------------------------------- + // Set data (internal stuff) + + /* + void setName(const char* const newName) override + { + } + */ + + // ------------------------------------------------------------------- + // Set data (plugin-specific stuff) + + /* + void setParameterValue(const uint32_t parameterId, const float value, const bool sendGui, const bool sendOsc, const bool sendCallback) noexcept override + { + } + + void setParameterValueRT(const uint32_t parameterId, const float value, const uint32_t frameOffset, const bool sendCallbackLater) noexcept override + { + } + + void setChunkData(const void* const data, const std::size_t dataSize) override + { + } + + void setProgram(const int32_t index, const bool sendGui, const bool sendOsc, const bool sendCallback, const bool doingInit) noexcept override + { + } + + void setProgramRT(const uint32_t uindex, const bool sendCallbackLater) noexcept override + { + } + */ + + // ------------------------------------------------------------------- + // Set ui stuff + + /* + void setCustomUITitle(const char* const title) noexcept override + { + } + + void showCustomUI(const bool yesNo) override + { + } + + void* embedCustomUI(void* const ptr) override + { + } + */ + + void idle() override + { + CarlaPlugin::idle(); + } + + void uiIdle() override + { + CarlaPlugin::uiIdle(); + } + + // ------------------------------------------------------------------- + // Plugin state + + void reload() override + { + CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr,); + // CARLA_SAFE_ASSERT_RETURN(fEffect != nullptr,); + carla_debug("CarlaPluginCLAP::reload() - start"); + + const EngineProcessMode processMode = pData->engine->getProccessMode(); + + // Safely disable plugin for reload + const ScopedDisabler sd(this); + + const clap_plugin_audio_ports_t* const audioPorts = static_cast( + fPlugin->get_extension(fPlugin, CLAP_EXT_AUDIO_PORTS)); + + const clap_plugin_note_ports_t* const notePorts = static_cast( + fPlugin->get_extension(fPlugin, CLAP_EXT_NOTE_PORTS)); + + const clap_plugin_params_t* const params = static_cast( + fPlugin->get_extension(fPlugin, CLAP_EXT_PARAMS)); + + carla_debug("CarlaPluginCLAP::reload() - end"); + } + + void reloadPrograms(const bool doInit) override + { + carla_debug("CarlaPluginCLAP::reloadPrograms(%s)", bool2str(doInit)); + } + + // ------------------------------------------------------------------- + // Plugin processing + + void activate() noexcept override + { + } + + void deactivate() noexcept override + { + } + + void process(const float* const*, + float** const audioOut, + const float* const*, + float** const, + const uint32_t frames) override + { + // -------------------------------------------------------------------------------------------------------- + // Check if active + + if (! pData->active) + { + // disable any output sound + for (uint32_t i=0; i < pData->audioOut.count; ++i) + carla_zeroFloats(audioOut[i], frames); + return; + } + + } + + void bufferSizeChanged(const uint32_t newBufferSize) override + { + CARLA_ASSERT_INT(newBufferSize > 0, newBufferSize); + carla_debug("CarlaPluginCLAP::bufferSizeChanged(%i)", newBufferSize); + } + + void sampleRateChanged(const double newSampleRate) override + { + CARLA_ASSERT_INT(newSampleRate > 0.0, newSampleRate); + carla_debug("CarlaPluginCLAP::sampleRateChanged(%g)", newSampleRate); + } + + // ------------------------------------------------------------------- + // Plugin buffers + + void clearBuffers() noexcept override + { + carla_debug("CarlaPluginCLAP::clearBuffers() - start"); + + CarlaPlugin::clearBuffers(); + + carla_debug("CarlaPluginCLAP::clearBuffers() - end"); + } + + // ------------------------------------------------------------------- + // Post-poned UI Stuff + + // nothing + + // ------------------------------------------------------------------- + +protected: + void handlePluginUIClosed() override + { + // CARLA_SAFE_ASSERT_RETURN(fUI.window != nullptr,); + carla_debug("CarlaPluginCLAP::handlePluginUIClosed()"); + + showCustomUI(false); + pData->engine->callback(true, true, + ENGINE_CALLBACK_UI_STATE_CHANGED, + pData->id, + 0, + 0, 0, 0.0f, nullptr); + } + + void handlePluginUIResized(const uint width, const uint height) override + { + // CARLA_SAFE_ASSERT_RETURN(fUI.window != nullptr,); + carla_debug("CarlaPluginCLAP::handlePluginUIResized(%u, %u)", width, height); + } + + // ------------------------------------------------------------------- + +public: + bool init(const CarlaPluginPtr plugin, + const char* const filename, const char* const name, const char* const id, const uint options) + { + CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr, false); + + // --------------------------------------------------------------- + // first checks + + if (pData->client != nullptr) + { + pData->engine->setLastError("Plugin client is already registered"); + return false; + } + + if (filename == nullptr || filename[0] == '\0') + { + pData->engine->setLastError("null filename"); + return false; + } + + if (id == nullptr || id[0] == '\0') + { + pData->engine->setLastError("null label/id"); + return false; + } + + // --------------------------------------------------------------- + + const clap_plugin_entry_t* entry; + + #ifdef CARLA_OS_MAC + if (!File(filename).existsAsFile()) + { + if (! fBundleLoader.load(filename)) + { + pData->engine->setLastError("Failed to load CLAP bundle executable"); + return false; + } + + entry = fBundleLoader.getSymbol(CFSTR("clap_entry")); + } + else + #endif + { + if (! pData->libOpen(filename)) + { + pData->engine->setLastError(pData->libError(filename)); + return false; + } + + entry = pData->libSymbol("clap_entry"); + } + + if (entry == nullptr) + { + pData->engine->setLastError("Could not find the CLAP entry in the plugin library"); + return false; + } + + if (entry->init == nullptr || entry->deinit == nullptr || entry->get_factory == nullptr) + { + pData->engine->setLastError("CLAP factory entries are null"); + return false; + } + + if (!clap_version_is_compatible(entry->clap_version)) + { + pData->engine->setLastError("Incompatible CLAP plugin"); + return false; + } + + // --------------------------------------------------------------- + + const water::String pluginPath(water::File(filename).getParentDirectory().getFullPathName()); + + if (!entry->init(pluginPath.toRawUTF8())) + { + pData->engine->setLastError("Plugin entry failed to initialize"); + return false; + } + + fPluginEntry = entry; + + // --------------------------------------------------------------- + + const clap_plugin_factory_t* const factory = static_cast( + entry->get_factory(CLAP_PLUGIN_FACTORY_ID)); + + if (factory == nullptr + || factory->get_plugin_count == nullptr + || factory->get_plugin_descriptor == nullptr + || factory->create_plugin == nullptr) + { + pData->engine->setLastError("Plugin is missing factory methods"); + return false; + } + + // --------------------------------------------------------------- + + if (const uint32_t count = factory->get_plugin_count(factory)) + { + for (uint32_t i=0; iget_plugin_descriptor(factory, i); + CARLA_SAFE_ASSERT_CONTINUE(desc != nullptr); + CARLA_SAFE_ASSERT_CONTINUE(desc->id != nullptr); + + if (std::strcmp(desc->id, id) == 0) + { + fPluginDescriptor = desc; + break; + } + } + } + else + { + pData->engine->setLastError("Plugin library contains no plugins"); + return false; + } + + if (fPluginDescriptor == nullptr) + { + pData->engine->setLastError("Plugin library does not contain the requested plugin"); + return false; + } + + // --------------------------------------------------------------- + + fPlugin = factory->create_plugin(factory, &fHost, fPluginDescriptor->id); + + if (fPlugin == nullptr) + { + pData->engine->setLastError("Failed to create CLAP plugin instance"); + return false; + } + + if (!fPlugin->init(fPlugin)) + { + pData->engine->setLastError("Failed to initialize CLAP plugin instance"); + return false; + } + + // --------------------------------------------------------------- + // get info + + pData->name = pData->engine->getUniquePluginName(name != nullptr && name[0] != '\0' + ? name + : fPluginDescriptor->name); + pData->filename = carla_strdup(filename); + + // --------------------------------------------------------------- + // register client + + pData->client = pData->engine->addClient(plugin); + + if (pData->client == nullptr || ! pData->client->isOk()) + { + pData->engine->setLastError("Failed to register plugin client"); + return false; + } + + // --------------------------------------------------------------- + // set default options + + pData->options = 0x0; + +// if (fEffect->initialDelay > 0 || hasMidiOutput() || isPluginOptionEnabled(options, PLUGIN_OPTION_FIXED_BUFFERS)) +// pData->options |= PLUGIN_OPTION_FIXED_BUFFERS; +// +// if (fEffect->flags & effFlagsProgramChunks) +// if (isPluginOptionEnabled(options, PLUGIN_OPTION_USE_CHUNKS)) +// pData->options |= PLUGIN_OPTION_USE_CHUNKS; +// +// if (hasMidiInput()) +// { +// if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_CONTROL_CHANGES)) +// pData->options |= PLUGIN_OPTION_SEND_CONTROL_CHANGES; +// if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_CHANNEL_PRESSURE)) +// pData->options |= PLUGIN_OPTION_SEND_CHANNEL_PRESSURE; +// if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH)) +// pData->options |= PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH; +// if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_PITCHBEND)) +// pData->options |= PLUGIN_OPTION_SEND_PITCHBEND; +// if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_ALL_SOUND_OFF)) +// pData->options |= PLUGIN_OPTION_SEND_ALL_SOUND_OFF; +// if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_PROGRAM_CHANGES)) +// pData->options |= PLUGIN_OPTION_SEND_PROGRAM_CHANGES; +// if (isPluginOptionInverseEnabled(options, PLUGIN_OPTION_SKIP_SENDING_NOTES)) +// pData->options |= PLUGIN_OPTION_SKIP_SENDING_NOTES; +// } +// +// if (fEffect->numPrograms > 1 && (pData->options & PLUGIN_OPTION_SEND_PROGRAM_CHANGES) == 0) +// if (isPluginOptionEnabled(options, PLUGIN_OPTION_MAP_PROGRAM_CHANGES)) +// pData->options |= PLUGIN_OPTION_MAP_PROGRAM_CHANGES; + + return true; + } + +private: + const clap_plugin_t* fPlugin; + const clap_plugin_descriptor_t* fPluginDescriptor; + const clap_plugin_entry_t* fPluginEntry; + const carla_clap_host fHost; + + #ifdef CARLA_OS_MAC + BundleLoader fBundleLoader; + #endif +}; + +// -------------------------------------------------------------------------------------------------------------------- + CarlaPluginPtr CarlaPlugin::newCLAP(const Initializer& init) { carla_debug("CarlaPlugin::newCLAP({%p, \"%s\", \"%s\", \"%s\"})", init.engine, init.filename, init.name, init.label); - init.engine->setLastError("Not implemented yet"); - return nullptr; + std::shared_ptr plugin(new CarlaPluginCLAP(init.engine, init.id)); + + if (! plugin->init(plugin, init.filename, init.name, init.label, init.options)) + return nullptr; + + return plugin; } // ------------------------------------------------------------------------------------------------------------------- diff --git a/source/backend/plugin/CarlaPluginVST2.cpp b/source/backend/plugin/CarlaPluginVST2.cpp index 8d1b4e8c6..b770b7bb4 100644 --- a/source/backend/plugin/CarlaPluginVST2.cpp +++ b/source/backend/plugin/CarlaPluginVST2.cpp @@ -84,10 +84,9 @@ public: fIdleThread(kNullThread), fMainThread(pthread_self()), fProcThread(kNullThread), -#ifdef CARLA_OS_MAC - fMacBundleRef(nullptr), - fMacBundleRefNum(0), -#endif + #ifdef CARLA_OS_MAC + fBundleLoader(), + #endif fFirstActive(true), fBufferSize(engine->getBufferSize()), fAudioOutBuffers(nullptr), @@ -156,21 +155,6 @@ public: } clearBuffers(); - -#ifdef CARLA_OS_MAC - if (fMacBundleRef != nullptr) - { - CFBundleCloseBundleResourceMap(fMacBundleRef, fMacBundleRefNum); - - if (CFGetRetainCount(fMacBundleRef) == 1) - CFBundleUnloadExecutable(fMacBundleRef); - - if (CFGetRetainCount(fMacBundleRef) > 0) - CFRelease(fMacBundleRef); - - fMacBundleRef = nullptr; - } -#endif } // ------------------------------------------------------------------- @@ -2498,37 +2482,22 @@ public: if (filenameCheck.endsWith(".vst") || filenameCheck.endsWith(".vst/")) { - // FIXME assert returns, set engine error - const CFURLRef urlRef = CFURLCreateFromFileSystemRepresentation(0, (const UInt8*)filename, (CFIndex)strlen(filename), true); - CARLA_SAFE_ASSERT_RETURN(urlRef != nullptr, false); - - fMacBundleRef = CFBundleCreate(kCFAllocatorDefault, urlRef); - CFRelease(urlRef); - CARLA_SAFE_ASSERT_RETURN(fMacBundleRef != nullptr, false); - - if (! CFBundleLoadExecutable(fMacBundleRef)) + if (! fBundleLoader.load(filename)) { - CFRelease(fMacBundleRef); - fMacBundleRef = nullptr; pData->engine->setLastError("Failed to load VST2 bundle executable"); return false; } - vstFn = (VST_Function)CFBundleGetFunctionPointerForName(fMacBundleRef, CFSTR("VSTPluginMain")); + vstFn = fBundleLoader.getSymbol(CFSTR("VSTPluginMain")); if (vstFn == nullptr) - vstFn = (VST_Function)CFBundleGetFunctionPointerForName(fMacBundleRef, CFSTR("main_macho")); + vstFn = fBundleLoader.getSymbol(CFSTR("main_macho")); if (vstFn == nullptr) { - CFBundleUnloadExecutable(fMacBundleRef); - CFRelease(fMacBundleRef); - fMacBundleRef = nullptr; pData->engine->setLastError("Not a VST2 plugin"); return false; } - - fMacBundleRefNum = CFBundleOpenBundleResourceMap(fMacBundleRef); } else #endif @@ -2764,10 +2733,9 @@ private: pthread_t fMainThread; pthread_t fProcThread; -#ifdef CARLA_OS_MAC - CFBundleRef fMacBundleRef; - CFBundleRefNum fMacBundleRefNum; -#endif + #ifdef CARLA_OS_MAC + BundleLoader fBundleLoader; + #endif bool fFirstActive; // first process() call after activate() uint32_t fBufferSize; diff --git a/source/backend/plugin/CarlaPluginVST3.cpp b/source/backend/plugin/CarlaPluginVST3.cpp index 926beccf5..7f45cc66f 100644 --- a/source/backend/plugin/CarlaPluginVST3.cpp +++ b/source/backend/plugin/CarlaPluginVST3.cpp @@ -2040,9 +2040,9 @@ public: return false; } - v3_entry = (V3_ENTRYFN)CFBundleGetFunctionPointerForName(fMacBundleLoader.getRef(), CFSTR(V3_ENTRYFNNAME)); - v3_exit = (V3_EXITFN)CFBundleGetFunctionPointerForName(fMacBundleLoader.getRef(), CFSTR(V3_EXITFNNAME)); - v3_get = (V3_GETFN)CFBundleGetFunctionPointerForName(fMacBundleLoader.getRef(), CFSTR(V3_GETFNNAME)); + v3_entry = fMacBundleLoader.getSymbol(CFSTR(V3_ENTRYFNNAME)); + v3_exit = fMacBundleLoader.getSymbol(CFSTR(V3_EXITFNNAME)); + v3_get = fMacBundleLoader.getSymbol(CFSTR(V3_GETFNNAME)); #else water::String binaryfilename = filename; diff --git a/source/discovery/carla-discovery.cpp b/source/discovery/carla-discovery.cpp index 0a45263cc..3aa75e445 100644 --- a/source/discovery/carla-discovery.cpp +++ b/source/discovery/carla-discovery.cpp @@ -1007,10 +1007,10 @@ static void do_vst2_check(lib_t& libHandle, const char* const filename, const bo return; } - vstFn = (VST_Function)CFBundleGetFunctionPointerForName(bundleLoader.getRef(), CFSTR("main_macho")); + vstFn = bundleLoader.getSymbol(CFSTR("main_macho")); if (vstFn == nullptr) - vstFn = (VST_Function)CFBundleGetFunctionPointerForName(bundleLoader.getRef(), CFSTR("VSTPluginMain")); + vstFn = bundleLoader.getSymbol(CFSTR("VSTPluginMain")); if (vstFn == nullptr) { @@ -1468,9 +1468,9 @@ static void do_vst3_check(lib_t& libHandle, const char* const filename, const bo return; } - v3_entry = (V3_ENTRYFN)CFBundleGetFunctionPointerForName(bundleLoader.getRef(), CFSTR(V3_ENTRYFNNAME)); - v3_exit = (V3_EXITFN)CFBundleGetFunctionPointerForName(bundleLoader.getRef(), CFSTR(V3_EXITFNNAME)); - v3_get = (V3_GETFN)CFBundleGetFunctionPointerForName(bundleLoader.getRef(), CFSTR(V3_GETFNNAME)); + v3_entry = bundleLoader.getSymbol(CFSTR(V3_ENTRYFNNAME)); + v3_exit = bundleLoader.getSymbol(CFSTR(V3_EXITFNNAME)); + v3_get = bundleLoader.getSymbol(CFSTR(V3_GETFNNAME)); #else water::String binaryfilename = filename; @@ -1869,7 +1869,27 @@ struct carla_clap_host : clap_host_t { static void do_clap_check(lib_t& libHandle, const char* const filename, const bool doInit) { - const clap_plugin_entry_t* const entry = lib_symbol(libHandle, "clap_entry"); + const clap_plugin_entry_t* entry = nullptr; + + #ifdef CARLA_OS_MAC + BundleLoader bundleLoader; + + // if passed filename is not a plugin binary directly, inspect bundle and find one + if (libHandle == nullptr) + { + if (! bundleLoader.load(filename)) + { + DISCOVERY_OUT("error", "Failed to load CLAP bundle executable"); + return; + } + + entry = bundleLoader.getSymbol(CFSTR("clap_entry")); + } + else + #endif + { + entry = lib_symbol(libHandle, "clap_entry"); + } // ensure entry points are available if (entry == nullptr || entry->init == nullptr || entry->deinit == nullptr || entry->get_factory == nullptr) @@ -1887,7 +1907,11 @@ static void do_clap_check(lib_t& libHandle, const char* const filename, const bo const water::String pluginPath(water::File(filename).getParentDirectory().getFullPathName()); - entry->init(pluginPath.toRawUTF8()); + if (!entry->init(pluginPath.toRawUTF8())) + { + DISCOVERY_OUT("error", "CLAP plugin failed to initialize"); + return; + } const clap_plugin_factory_t* const factory = static_cast( entry->get_factory(CLAP_PLUGIN_FACTORY_ID)); diff --git a/source/utils/CarlaMacUtils.cpp b/source/utils/CarlaMacUtils.cpp index e2cf10550..8d179a909 100644 --- a/source/utils/CarlaMacUtils.cpp +++ b/source/utils/CarlaMacUtils.cpp @@ -106,8 +106,12 @@ struct BundleLoader::PrivateData { return; CFBundleCloseBundleResourceMap(ref, refNum); - CFBundleUnloadExecutable(ref); - CFRelease(ref); + + if (CFGetRetainCount(ref) == 1) + CFBundleUnloadExecutable(ref); + + if (CFGetRetainCount(ref) > 0) + CFRelease(ref); } bool load(const char* const filename) diff --git a/source/utils/CarlaMacUtils.hpp b/source/utils/CarlaMacUtils.hpp index cf12508c1..369f8cfa9 100644 --- a/source/utils/CarlaMacUtils.hpp +++ b/source/utils/CarlaMacUtils.hpp @@ -68,6 +68,12 @@ struct BundleLoader { bool load(const char* const filename); CFBundleRef getRef() const noexcept; + inline template + Func getSymbol(const CFStringRef name) const + { + return reinterpret_cast(CFBundleGetFunctionPointerForName(getRef(), name)); + } + private: struct PrivateData; PrivateData* const pData;