| @@ -1,5 +1,11 @@ | |||
| # INSTALL for Carla | |||
| NOTE: when using MSYS2 on Windows, an additional step is necessary in order | |||
| to solve an issue with symbolic links to some dependency folders before build: | |||
| ``` | |||
| $ make msys2fix | |||
| ``` | |||
| To install Carla, simply run as usual: | |||
| ``` | |||
| $ make | |||
| @@ -1,6 +1,6 @@ | |||
| #!/bin/bash | |||
| if [ "$(uname -m)" = "arm64" ]; then | |||
| if [ "$(uname -m)" = "arm64" ] || [ "$(uname -r)" = "20.1.0" ]; then | |||
| MACOS_UNIVERSAL=1 | |||
| else | |||
| MACOS_UNIVERSAL=0 | |||
| @@ -13,7 +13,7 @@ fi | |||
| export CC=clang | |||
| export CXX=clang++ | |||
| export CFLAGS="-I${TARGETDIR}/carla64/include -mmacosx-version-min=${MACOS_VERSION_MIN}" | |||
| export CFLAGS="-I${TARGETDIR}/carla64/include -mmacosx-version-min=${MACOS_VERSION_MIN} -DMAC_OS_X_VERSION_MAX_ALLOWED=MAC_OS_X_VERSION_10_12" | |||
| export CFLAGS="${CFLAGS} -mtune=generic -msse -msse2" | |||
| export LDFLAGS="-L${TARGETDIR}/carla64/lib -stdlib=libc++" | |||
| unset CPPFLAGS | |||
| @@ -21,9 +21,10 @@ unset CPPFLAGS | |||
| if [ "${MACOS_UNIVERSAL}" -eq 1 ]; then | |||
| export CFLAGS="${CFLAGS} -arch x86_64 -arch arm64" | |||
| export LDFLAGS="${LDFLAGS} -arch x86_64 -arch arm64" | |||
| export MACOS_UNIVERSAL="true" | |||
| else | |||
| export CFLAGS="${CFLAGS} -m${ARCH}" | |||
| export LDFLAGS="${LDFLAGS} -m${ARCH}" | |||
| export CFLAGS="${CFLAGS} -m64" | |||
| export LDFLAGS="${LDFLAGS} -m64" | |||
| fi | |||
| export CXXFLAGS="${CFLAGS} -stdlib=libc++ -Wno-unknown-pragmas -Wno-unused-private-field -Werror=auto-var-id" | |||
| @@ -359,7 +359,8 @@ ifeq ($(shell $(PKG_CONFIG) --exists libmagic && echo true),true) | |||
| HAVE_LIBMAGIC = true | |||
| else | |||
| # old libmagic versions don't have a pkg-config file, so we need to call the compiler to test it | |||
| HAVE_LIBMAGIC = $(shell echo '\#include <magic.h>' | $(CC) $(CFLAGS) -x c -w -c - -o /dev/null 2>/dev/null && echo true) | |||
| CFLAGS_WITHOUT_ARCH = $(subst -arch arm64,,$(CFLAGS)) | |||
| HAVE_LIBMAGIC = $(shell echo '\#include <magic.h>' | $(CC) $(CFLAGS_WITHOUT_ARCH) -x c -w -c - -o /dev/null 2>/dev/null && echo true) | |||
| endif | |||
| endif | |||
| @@ -964,7 +964,8 @@ public: | |||
| static CarlaPluginPtr newNative(const Initializer& init); | |||
| static CarlaPluginPtr newBridge(const Initializer& init, | |||
| BinaryType btype, PluginType ptype, const char* bridgeBinary); | |||
| BinaryType btype, PluginType ptype, | |||
| const char* binaryArchName, const char* bridgeBinary); | |||
| static CarlaPluginPtr newLADSPA(const Initializer& init, const LADSPA_RDF_Descriptor* rdfDescriptor); | |||
| static CarlaPluginPtr newDSSI(const Initializer& init); | |||
| @@ -44,6 +44,13 @@ | |||
| #include "water/xml/XmlDocument.h" | |||
| #include "water/xml/XmlElement.h" | |||
| #ifdef CARLA_OS_MAC | |||
| # include "CarlaMacUtils.hpp" | |||
| # if defined(CARLA_OS_64BIT) && defined(HAVE_LIBMAGIC) && ! defined(BUILD_BRIDGE_ALTERNATIVE_ARCH) | |||
| # define ADAPT_FOR_APPLE_SILLICON | |||
| # endif | |||
| #endif | |||
| #include <map> | |||
| // FIXME Remove on 2.1 release | |||
| @@ -560,12 +567,27 @@ bool CarlaEngine::addPlugin(const BinaryType btype, | |||
| bridgeBinary.clear(); | |||
| } | |||
| const bool canBeBridged = ptype != PLUGIN_INTERNAL | |||
| && ptype != PLUGIN_DLS | |||
| && ptype != PLUGIN_GIG | |||
| && ptype != PLUGIN_SF2 | |||
| && ptype != PLUGIN_SFZ | |||
| && ptype != PLUGIN_JACK; | |||
| // Prefer bridges for some specific plugins | |||
| const bool preferBridges = pData->options.preferPluginBridges; | |||
| bool preferBridges = pData->options.preferPluginBridges; | |||
| const char* needsArchBridge = nullptr; | |||
| #if 0 // ndef BUILD_BRIDGE | |||
| if (! preferBridges) | |||
| #ifdef CARLA_OS_MAC | |||
| // Plugin might be in quarentine due to Apple stupid notarization rules, let's remove that if possible | |||
| if (canBeBridged && ptype != PLUGIN_LV2 && ptype != PLUGIN_AU) | |||
| removeFileFromQuarantine(filename); | |||
| #endif | |||
| #ifndef BUILD_BRIDGE | |||
| if (canBeBridged && ! preferBridges) | |||
| { | |||
| # if 0 | |||
| if (ptype == PLUGIN_LV2 && label != nullptr) | |||
| { | |||
| if (std::strncmp(label, "http://calf.sourceforge.net/plugins/", 36) == 0 || | |||
| @@ -575,19 +597,39 @@ bool CarlaEngine::addPlugin(const BinaryType btype, | |||
| preferBridges = true; | |||
| } | |||
| } | |||
| # endif | |||
| # ifdef ADAPT_FOR_APPLE_SILLICON | |||
| // see if this binary needs bridging | |||
| if (ptype == PLUGIN_VST2) | |||
| { | |||
| if (const char* const vst2Binary = findBinaryInBundle(filename)) | |||
| { | |||
| const CarlaMagic magic; | |||
| if (const char* const output = magic.getFileDescription(vst2Binary)) | |||
| { | |||
| # ifdef __aarch64__ | |||
| if (std::strstr(output, "arm64") == nullptr && std::strstr(output, "x86_64") != nullptr) | |||
| needsArchBridge = "x86_64"; | |||
| # else | |||
| if (std::strstr(output, "x86_64") == nullptr && std::strstr(output, "arm64") != nullptr) | |||
| needsArchBridge = "arm64"; | |||
| # endif | |||
| } | |||
| } | |||
| } | |||
| else if (ptype == PLUGIN_VST3) | |||
| { | |||
| // TODO | |||
| } | |||
| # endif | |||
| } | |||
| #endif // ! BUILD_BRIDGE | |||
| const bool canBeBridged = ptype != PLUGIN_INTERNAL | |||
| && ptype != PLUGIN_SF2 | |||
| && ptype != PLUGIN_SFZ | |||
| && ptype != PLUGIN_JACK; | |||
| if (canBeBridged && (btype != BINARY_NATIVE || (preferBridges && bridgeBinary.isNotEmpty()))) | |||
| if (canBeBridged && (needsArchBridge || btype != BINARY_NATIVE || (preferBridges && bridgeBinary.isNotEmpty()))) | |||
| { | |||
| if (bridgeBinary.isNotEmpty()) | |||
| { | |||
| plugin = CarlaPlugin::newBridge(initializer, btype, ptype, bridgeBinary); | |||
| plugin = CarlaPlugin::newBridge(initializer, btype, ptype, needsArchBridge, bridgeBinary); | |||
| } | |||
| else | |||
| { | |||
| @@ -143,49 +143,56 @@ public: | |||
| if (! fShmAudioPool.attachClient(fBaseNameAudioPool)) | |||
| { | |||
| carla_stderr("Failed to attach to audio pool shared memory"); | |||
| pData->close(); | |||
| setLastError("Failed to attach to audio pool shared memory"); | |||
| return false; | |||
| } | |||
| if (! fShmRtClientControl.attachClient(fBaseNameRtClientControl)) | |||
| { | |||
| pData->close(); | |||
| clear(); | |||
| carla_stderr("Failed to attach to rt client control shared memory"); | |||
| setLastError("Failed to attach to rt client control shared memory"); | |||
| return false; | |||
| } | |||
| if (! fShmRtClientControl.mapData()) | |||
| { | |||
| pData->close(); | |||
| clear(); | |||
| carla_stderr("Failed to map rt client control shared memory"); | |||
| setLastError("Failed to map rt client control shared memory"); | |||
| return false; | |||
| } | |||
| if (! fShmNonRtClientControl.attachClient(fBaseNameNonRtClientControl)) | |||
| { | |||
| pData->close(); | |||
| clear(); | |||
| carla_stderr("Failed to attach to non-rt client control shared memory"); | |||
| setLastError("Failed to attach to non-rt client control shared memory"); | |||
| return false; | |||
| } | |||
| if (! fShmNonRtClientControl.mapData()) | |||
| { | |||
| pData->close(); | |||
| clear(); | |||
| carla_stderr("Failed to map non-rt control client shared memory"); | |||
| setLastError("Failed to map non-rt control client shared memory"); | |||
| return false; | |||
| } | |||
| if (! fShmNonRtServerControl.attachClient(fBaseNameNonRtServerControl)) | |||
| { | |||
| pData->close(); | |||
| clear(); | |||
| carla_stderr("Failed to attach to non-rt server control shared memory"); | |||
| setLastError("Failed to attach to non-rt server control shared memory"); | |||
| return false; | |||
| } | |||
| if (! fShmNonRtServerControl.mapData()) | |||
| { | |||
| pData->close(); | |||
| clear(); | |||
| carla_stderr("Failed to map non-rt control server shared memory"); | |||
| setLastError("Failed to map non-rt control server shared memory"); | |||
| return false; | |||
| } | |||
| @@ -210,7 +217,9 @@ public: | |||
| shmNonRtClientDataSize != sizeof(BridgeNonRtClientData) || | |||
| shmNonRtServerDataSize != sizeof(BridgeNonRtServerData)) | |||
| { | |||
| carla_stderr2("CarlaEngineBridge: data size mismatch"); | |||
| pData->close(); | |||
| clear(); | |||
| setLastError("Shared memory data size mismatch"); | |||
| return false; | |||
| } | |||
| @@ -222,7 +231,9 @@ public: | |||
| if (pData->bufferSize == 0 || carla_isZero(pData->sampleRate)) | |||
| { | |||
| carla_stderr2("CarlaEngineBridge: invalid empty state"); | |||
| pData->close(); | |||
| clear(); | |||
| setLastError("Shared memory has invalid data"); | |||
| return false; | |||
| } | |||
| @@ -232,9 +232,11 @@ protected: | |||
| carla_zeroFloats(audioIns[1], bufferSize); | |||
| carla_zeroStructs(pData->events.in, kMaxEngineEventInternalCount); | |||
| int64_t oldTime, newTime; | |||
| while (! shouldThreadExit()) | |||
| { | |||
| const int64_t oldTime = getTimeInMicroseconds(); | |||
| oldTime = getTimeInMicroseconds(); | |||
| const PendingRtEventsRunner prt(this, bufferSize, true); | |||
| @@ -244,7 +246,7 @@ protected: | |||
| pData->graph.process(pData, audioIns, audioOuts, bufferSize); | |||
| const int64_t newTime = getTimeInMicroseconds(); | |||
| newTime = getTimeInMicroseconds(); | |||
| CARLA_SAFE_ASSERT_CONTINUE(newTime >= oldTime); | |||
| const int64_t remainingTime = cycleTime - (newTime - oldTime); | |||
| @@ -267,7 +269,7 @@ protected: | |||
| std::free(audioOuts[0]); | |||
| std::free(audioOuts[1]); | |||
| carla_stdout("CarlaEngineDummy audio thread finished"); | |||
| carla_stdout("CarlaEngineDummy audio thread finished with %u Xruns", pData->xruns); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| @@ -91,7 +91,8 @@ public: | |||
| : CarlaThread("CarlaPluginBridgeThread"), | |||
| kEngine(engine), | |||
| kPlugin(plugin), | |||
| fBinary(), | |||
| fBinaryArchName(), | |||
| fBridgeBinary(), | |||
| fLabel(), | |||
| fShmIds(), | |||
| #ifndef CARLA_OS_WIN | |||
| @@ -103,18 +104,20 @@ public: | |||
| #ifndef CARLA_OS_WIN | |||
| const char* const winePrefix, | |||
| #endif | |||
| const char* const binary, | |||
| const char* const binaryArchName, | |||
| const char* const bridgeBinary, | |||
| const char* const label, | |||
| const char* const shmIds) noexcept | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(binary != nullptr && binary[0] != '\0',); | |||
| CARLA_SAFE_ASSERT_RETURN(bridgeBinary != nullptr && bridgeBinary[0] != '\0',); | |||
| CARLA_SAFE_ASSERT_RETURN(shmIds != nullptr && shmIds[0] != '\0',); | |||
| CARLA_SAFE_ASSERT(! isThreadRunning()); | |||
| #ifndef CARLA_OS_WIN | |||
| fWinePrefix = winePrefix; | |||
| #endif | |||
| fBinary = binary; | |||
| fBinaryArchName = binaryArchName; | |||
| fBridgeBinary = bridgeBinary; | |||
| fShmIds = shmIds; | |||
| if (label != nullptr) | |||
| @@ -160,7 +163,7 @@ protected: | |||
| #ifndef CARLA_OS_WIN | |||
| // start with "wine" if needed | |||
| if (fBinary.endsWithIgnoreCase(".exe")) | |||
| if (fBridgeBinary.endsWithIgnoreCase(".exe")) | |||
| { | |||
| String wineCMD; | |||
| @@ -168,7 +171,7 @@ protected: | |||
| { | |||
| wineCMD = options.wine.executable; | |||
| if (fBinary.endsWithIgnoreCase("64.exe") | |||
| if (fBridgeBinary.endsWithIgnoreCase("64.exe") | |||
| && options.wine.executable[0] == CARLA_OS_SEP | |||
| && File(wineCMD + "64").existsAsFile()) | |||
| wineCMD += "64"; | |||
| @@ -182,8 +185,18 @@ protected: | |||
| } | |||
| #endif | |||
| // binary | |||
| arguments.add(fBinary); | |||
| #ifdef CARLA_OS_MAC | |||
| // setup binary arch | |||
| if (fBinaryArchName.isNotEmpty()) | |||
| { | |||
| arguments.add("arch"); | |||
| arguments.add("-arch"); | |||
| arguments.add(fBinaryArchName); | |||
| } | |||
| #endif | |||
| // bridge binary | |||
| arguments.add(fBridgeBinary); | |||
| // plugin type | |||
| arguments.add(getPluginTypeAsString(kPlugin->getType())); | |||
| @@ -309,7 +322,7 @@ protected: | |||
| #endif | |||
| carla_stdout("Starting plugin bridge, command is:\n%s \"%s\" \"%s\" \"%s\" " P_INT64, | |||
| fBinary.toRawUTF8(), getPluginTypeAsString(kPlugin->getType()), filename.toRawUTF8(), fLabel.toRawUTF8(), kPlugin->getUniqueId()); | |||
| fBridgeBinary.toRawUTF8(), getPluginTypeAsString(kPlugin->getType()), filename.toRawUTF8(), fLabel.toRawUTF8(), kPlugin->getUniqueId()); | |||
| started = fProcess->start(arguments); | |||
| } | |||
| @@ -361,7 +374,8 @@ private: | |||
| CarlaEngine* const kEngine; | |||
| CarlaPlugin* const kPlugin; | |||
| String fBinary; | |||
| String fBinaryArchName; | |||
| String fBridgeBinary; | |||
| String fLabel; | |||
| String fShmIds; | |||
| #ifndef CARLA_OS_WIN | |||
| @@ -2543,6 +2557,7 @@ public: | |||
| const char* const label, | |||
| const int64_t uniqueId, | |||
| const uint options, | |||
| const char* const binaryArchName, | |||
| const char* const bridgeBinary) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr, false); | |||
| @@ -2652,7 +2667,7 @@ public: | |||
| #ifndef CARLA_OS_WIN | |||
| fWinePrefix.toRawUTF8(), | |||
| #endif | |||
| bridgeBinary, label, shmIdsStr); | |||
| binaryArchName, bridgeBinary, label, shmIdsStr); | |||
| } | |||
| if (! restartBridgeThread()) | |||
| @@ -3136,11 +3151,14 @@ CARLA_BACKEND_END_NAMESPACE | |||
| CARLA_BACKEND_START_NAMESPACE | |||
| CarlaPluginPtr CarlaPlugin::newBridge(const Initializer& init, | |||
| BinaryType btype, PluginType ptype, const char* bridgeBinary) | |||
| const BinaryType btype, | |||
| const PluginType ptype, | |||
| const char* const binaryArchName, | |||
| const char* bridgeBinary) | |||
| { | |||
| carla_debug("CarlaPlugin::newBridge({%p, \"%s\", \"%s\", \"%s\"}, %s, %s, \"%s\")", | |||
| carla_debug("CarlaPlugin::newBridge({%p, \"%s\", \"%s\", \"%s\"}, %s, %s, \"%s\", \"%s\")", | |||
| init.engine, init.filename, init.name, init.label, | |||
| BinaryType2Str(btype), PluginType2Str(ptype), bridgeBinary); | |||
| BinaryType2Str(btype), PluginType2Str(ptype), binaryArchName, bridgeBinary); | |||
| if (bridgeBinary == nullptr || bridgeBinary[0] == '\0') | |||
| { | |||
| @@ -3156,7 +3174,7 @@ CarlaPluginPtr CarlaPlugin::newBridge(const Initializer& init, | |||
| std::shared_ptr<CarlaPluginBridge> plugin(new CarlaPluginBridge(init.engine, init.id, btype, ptype)); | |||
| if (! plugin->init(plugin, init.filename, init.name, init.label, init.uniqueId, init.options, bridgeBinary)) | |||
| if (! plugin->init(plugin, init.filename, init.name, init.label, init.uniqueId, init.options, binaryArchName, bridgeBinary)) | |||
| return nullptr; | |||
| return plugin; | |||
| @@ -1378,7 +1378,28 @@ public: | |||
| fDesc.name = label; | |||
| } | |||
| fFormatManager.addDefaultFormats(); | |||
| /**/ if (std::strcmp(format, "AU") == 0) | |||
| { | |||
| #if JUCE_PLUGINHOST_AU | |||
| fFormatManager.addFormat(new juce::AudioUnitPluginFormat()); | |||
| #endif | |||
| } | |||
| else if (std::strcmp(format, "VST2") == 0) | |||
| { | |||
| #if JUCE_PLUGINHOST_VST | |||
| fFormatManager.addFormat(new juce::VSTPluginFormat()); | |||
| #endif | |||
| } | |||
| else if (std::strcmp(format, "VST3") == 0) | |||
| { | |||
| #if JUCE_PLUGINHOST_VST3 | |||
| fFormatManager.addFormat(new juce::VST3PluginFormat()); | |||
| #endif | |||
| } | |||
| else | |||
| { | |||
| fFormatManager.addDefaultFormats(); | |||
| } | |||
| { | |||
| juce::OwnedArray<juce::PluginDescription> pluginDescriptions; | |||
| @@ -1389,12 +1410,18 @@ public: | |||
| for (int i = 0; i < fFormatManager.getNumFormats(); ++i) | |||
| { | |||
| juce::AudioPluginFormat* const apformat = fFormatManager.getFormat(i); | |||
| CARLA_SAFE_ASSERT_CONTINUE(apformat != nullptr); | |||
| carla_debug("Trying to load '%s' plugin with format '%s'", fileOrIdentifier.toRawUTF8(), apformat->getName().toRawUTF8()); | |||
| try { | |||
| plist.scanAndAddFile(fileOrIdentifier, true, pluginDescriptions, *fFormatManager.getFormat(i)); | |||
| plist.scanAndAddFile(fileOrIdentifier, true, pluginDescriptions, *apformat); | |||
| } CARLA_SAFE_EXCEPTION_CONTINUE("scanAndAddFile") | |||
| if (sac.wasTriggered()) | |||
| { | |||
| carla_stderr("WARNING: Caught exception while scanning file, will not load this plugin"); | |||
| pluginDescriptions.clearQuick(false); | |||
| break; | |||
| } | |||
| @@ -1426,7 +1453,10 @@ public: | |||
| } CARLA_SAFE_EXCEPTION("createPluginInstance") | |||
| if (sac.wasTriggered()) | |||
| { | |||
| fInstance = nullptr; | |||
| carla_stderr("WARNING: Caught exception while instantiating, will not load this plugin"); | |||
| } | |||
| } | |||
| if (fInstance == nullptr) | |||
| @@ -41,6 +41,14 @@ extern "C" { | |||
| #include "water/files/File.h" | |||
| #include "water/misc/Time.h" | |||
| #ifdef CARLA_OS_MAC | |||
| # include "CarlaMacUtils.hpp" | |||
| # if defined(CARLA_OS_64BIT) && defined(HAVE_LIBMAGIC) && ! defined(BUILD_BRIDGE_ALTERNATIVE_ARCH) | |||
| # define ADAPT_FOR_APPLE_SILLICON | |||
| # include "CarlaBinaryUtils.hpp" | |||
| # endif | |||
| #endif | |||
| #include <string> | |||
| #include <vector> | |||
| @@ -6249,7 +6257,8 @@ public: | |||
| public: | |||
| bool init(const CarlaPluginPtr plugin, | |||
| const char* const name, const char* const uri, const uint options) | |||
| const char* const name, const char* const uri, const uint options, | |||
| const char*& needsArchBridge) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr, false); | |||
| @@ -6293,9 +6302,36 @@ public: | |||
| return false; | |||
| } | |||
| #ifdef ADAPT_FOR_APPLE_SILLICON | |||
| // --------------------------------------------------------------- | |||
| // check if we can open this binary, might need a bridge | |||
| { | |||
| const CarlaMagic magic; | |||
| if (const char* const output = magic.getFileDescription(fRdfDescriptor->Binary)) | |||
| { | |||
| # ifdef __aarch64__ | |||
| if (std::strstr(output, "arm64") == nullptr && std::strstr(output, "x86_64") != nullptr) | |||
| needsArchBridge = "x86_64"; | |||
| # else | |||
| if (std::strstr(output, "x86_64") == nullptr && std::strstr(output, "arm64") != nullptr) | |||
| needsArchBridge = "arm64"; | |||
| # endif | |||
| if (needsArchBridge != nullptr) | |||
| return false; | |||
| } | |||
| } | |||
| #endif // ADAPT_FOR_APPLE_SILLICON | |||
| // --------------------------------------------------------------- | |||
| // open DLL | |||
| #ifdef CARLA_OS_MAC | |||
| // Binary might be in quarentine due to Apple stupid notarization rules, let's remove that if possible | |||
| removeFileFromQuarantine(fRdfDescriptor->Binary); | |||
| #endif | |||
| if (! pData->libOpen(fRdfDescriptor->Binary)) | |||
| { | |||
| pData->engine->setLastError(pData->libError(fRdfDescriptor->Binary)); | |||
| @@ -6712,6 +6748,9 @@ public: | |||
| initUi(); | |||
| return true; | |||
| // might be unused | |||
| (void)needsArchBridge; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| @@ -6961,6 +7000,11 @@ public: | |||
| // --------------------------------------------------------------- | |||
| // open UI DLL | |||
| #ifdef CARLA_OS_MAC | |||
| // Binary might be in quarentine due to Apple stupid notarization rules, let's remove that if possible | |||
| removeFileFromQuarantine(fUI.rdfDescriptor->Binary); | |||
| #endif | |||
| if (! pData->uiLibOpen(fUI.rdfDescriptor->Binary, canDelete)) | |||
| { | |||
| carla_stderr2("Could not load UI library, error was:\n%s", pData->libError(fUI.rdfDescriptor->Binary)); | |||
| @@ -8103,10 +8147,19 @@ CarlaPluginPtr CarlaPlugin::newLV2(const Initializer& init) | |||
| std::shared_ptr<CarlaPluginLV2> plugin(new CarlaPluginLV2(init.engine, init.id)); | |||
| if (! plugin->init(plugin, init.name, init.label, init.options)) | |||
| return nullptr; | |||
| const char* needsArchBridge = nullptr; | |||
| if (plugin->init(plugin, init.name, init.label, init.options, needsArchBridge)) | |||
| return plugin; | |||
| if (needsArchBridge != nullptr) | |||
| { | |||
| CarlaString bridgeBinary(init.engine->getOptions().binaryDir); | |||
| bridgeBinary += CARLA_OS_SEP_STR "carla-bridge-native"; | |||
| return CarlaPlugin::newBridge(init, BINARY_NATIVE, PLUGIN_LV2, needsArchBridge, bridgeBinary); | |||
| } | |||
| return plugin; | |||
| return nullptr; | |||
| } | |||
| // used in CarlaStandalone.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| #include "CarlaPluginInternal.hpp" | |||
| #include "CarlaEngine.hpp" | |||
| #include "AppConfig.h" | |||
| #if defined(USING_JUCE) && JUCE_PLUGINHOST_VST | |||
| # define USE_JUCE_FOR_VST2 | |||
| @@ -68,6 +68,7 @@ | |||
| #include "jackbridge/JackBridge.hpp" | |||
| #include "water/files/File.h" | |||
| #include "water/misc/Time.h" | |||
| using CarlaBackend::CarlaEngine; | |||
| using CarlaBackend::EngineCallbackOpcode; | |||
| @@ -238,7 +239,7 @@ public: | |||
| { | |||
| carla_debug("CarlaBridgePlugin::~CarlaBridgePlugin()"); | |||
| if (! fUsingExec) | |||
| if (fEngine != nullptr && ! fUsingExec) | |||
| carla_engine_close(gHostHandle); | |||
| } | |||
| @@ -281,6 +282,15 @@ public: | |||
| gIsInitiated = true; | |||
| const bool testing = std::getenv("CARLA_BRIDGE_TESTING") != nullptr; | |||
| int64_t timeToEnd = 0; | |||
| if (testing) | |||
| { | |||
| timeToEnd = water::Time::currentTimeMillis() + 10 * 1000; | |||
| fEngine->transportPlay(); | |||
| } | |||
| #if defined(USING_JUCE) && (defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)) | |||
| # ifndef CARLA_OS_WIN | |||
| static const int argc = 0; | |||
| @@ -298,6 +308,8 @@ public: | |||
| # else | |||
| carla_msleep(5); | |||
| # endif | |||
| if (testing && timeToEnd - water::Time::currentTimeMillis() < 0) | |||
| break; | |||
| } | |||
| #endif | |||
| @@ -332,7 +344,7 @@ protected: | |||
| } | |||
| private: | |||
| const CarlaEngine* fEngine; | |||
| CarlaEngine* fEngine; | |||
| #ifdef USING_JUCE | |||
| const juce::ScopedJuceInitialiser_GUI fJuceInitialiser; | |||
| @@ -649,7 +661,14 @@ int main(int argc, char* argv[]) | |||
| if (const CarlaPluginInfo* const pluginInfo = carla_get_plugin_info(gHostHandle, 0)) | |||
| { | |||
| if (pluginInfo->hints & CarlaBackend::PLUGIN_HAS_CUSTOM_UI) | |||
| if (itype == CarlaBackend::PLUGIN_INTERNAL && (std::strcmp(label, "audiofile") == 0 || std::strcmp(label, "midifile") == 0)) | |||
| { | |||
| if (file.exists()) | |||
| carla_set_custom_data(gHostHandle, 0, | |||
| CarlaBackend::CUSTOM_DATA_TYPE_STRING, | |||
| "file", file.getFullPathName().toRawUTF8()); | |||
| } | |||
| else if (pluginInfo->hints & CarlaBackend::PLUGIN_HAS_CUSTOM_UI) | |||
| { | |||
| #ifdef HAVE_X11 | |||
| if (std::getenv("DISPLAY") != nullptr) | |||
| @@ -25,6 +25,10 @@ | |||
| #include "water/files/File.h" | |||
| #ifdef CARLA_OS_MAC | |||
| # include "CarlaMacUtils.hpp" | |||
| #endif | |||
| #include <string> | |||
| #include <vector> | |||
| @@ -512,6 +516,11 @@ public: | |||
| // ------------------------------------------------------------------------------------------------------------ | |||
| // open DLL | |||
| #ifdef CARLA_OS_MAC | |||
| // Binary might be in quarentine due to Apple stupid notarization rules, let's remove that if possible | |||
| CarlaBackend::removeFileFromQuarantine(fRdfUiDescriptor->Binary); | |||
| #endif | |||
| if (! libOpen(fRdfUiDescriptor->Binary)) | |||
| { | |||
| carla_stderr("Failed to load UI binary, error was:\n%s", libError()); | |||
| @@ -63,6 +63,7 @@ | |||
| # undef Component | |||
| # undef MemoryBlock | |||
| # undef Point | |||
| # include "CarlaMacUtils.cpp" | |||
| #endif | |||
| #ifdef CARLA_OS_WIN | |||
| @@ -1753,6 +1754,21 @@ int main(int argc, char* argv[]) | |||
| } | |||
| #endif | |||
| #ifdef CARLA_OS_MAC | |||
| // Plugin might be in quarentine due to Apple stupid notarization rules, let's remove that if possible | |||
| switch (type) | |||
| { | |||
| case PLUGIN_LADSPA: | |||
| case PLUGIN_DSSI: | |||
| case PLUGIN_VST2: | |||
| case PLUGIN_VST3: | |||
| removeFileFromQuarantine(filename); | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| #endif | |||
| switch (type) | |||
| { | |||
| case PLUGIN_LADSPA: | |||
| @@ -252,6 +252,12 @@ class RackListWidget(QListWidget): | |||
| self.clearFocus() | |||
| def isDragUrlValid(self, filename): | |||
| if not filename: | |||
| return False | |||
| if filename[-1] == '/': | |||
| filename = filename[:-1] | |||
| lfilename = filename.lower() | |||
| if os.path.isdir(filename): | |||
| @@ -333,6 +339,12 @@ class RackListWidget(QListWidget): | |||
| filename = url.toLocalFile() | |||
| if not filename: | |||
| continue | |||
| if filename[-1] == '/': | |||
| filename = filename[:-1] | |||
| if not self.host.load_file(filename): | |||
| CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), | |||
| self.tr("Failed to load file"), | |||
| @@ -97,21 +97,14 @@ struct AudioFilePool { | |||
| CARLA_DECLARE_NON_COPY_STRUCT(AudioFilePool) | |||
| }; | |||
| class AbstractAudioPlayer | |||
| { | |||
| public: | |||
| virtual ~AbstractAudioPlayer() {} | |||
| virtual uint64_t getLastFrame() const = 0; | |||
| }; | |||
| class AudioFileThread : public CarlaThread | |||
| { | |||
| public: | |||
| AudioFileThread(AbstractAudioPlayer* const player) | |||
| AudioFileThread() | |||
| : CarlaThread("AudioFileThread"), | |||
| kPlayer(player), | |||
| fEntireFileLoaded(false), | |||
| fLoopingMode(true), | |||
| fNeedsFrame(0), | |||
| fNeedsRead(false), | |||
| fQuitNow(true), | |||
| fFilePtr(nullptr), | |||
| @@ -120,10 +113,9 @@ public: | |||
| fPollTempData(nullptr), | |||
| fPollTempSize(0), | |||
| fPool(), | |||
| fMutex() | |||
| fMutex(), | |||
| fSignal() | |||
| { | |||
| CARLA_ASSERT(kPlayer != nullptr); | |||
| static bool adInitiated = false; | |||
| if (! adInitiated) | |||
| @@ -168,16 +160,19 @@ public: | |||
| if (fPollTempData == nullptr) | |||
| return; | |||
| fNeedsRead = true; | |||
| fNeedsFrame = 0; | |||
| fNeedsRead = false; | |||
| fQuitNow = false; | |||
| startThread(); | |||
| } | |||
| void stopNow() | |||
| { | |||
| fNeedsFrame = 0; | |||
| fNeedsRead = false; | |||
| fQuitNow = true; | |||
| fSignal.signal(); | |||
| stopThread(1000); | |||
| const CarlaMutexLocker cml(fMutex); | |||
| @@ -209,9 +204,14 @@ public: | |||
| fLoopingMode = on; | |||
| } | |||
| void setNeedsRead() noexcept | |||
| void setNeedsRead(const uint64_t frame) noexcept | |||
| { | |||
| if (fEntireFileLoaded) | |||
| return; | |||
| fNeedsFrame = frame; | |||
| fNeedsRead = true; | |||
| fSignal.signal(); | |||
| } | |||
| bool loadFilename(const char* const filename, const uint32_t sampleRate) | |||
| @@ -295,24 +295,73 @@ public: | |||
| carla_copyFloats(pool.buffer[1], fPool.buffer[1], fPool.numFrames); | |||
| } | |||
| bool tryPutData(AudioFilePool& pool, const uint64_t framePos, const uint32_t frames) | |||
| bool tryPutData(float* const out1, float* const out2, uint64_t framePos, const uint32_t frames) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(pool.numFrames == fPool.numFrames, false); | |||
| CARLA_SAFE_ASSERT_RETURN(fPool.numFrames != 0, false); | |||
| if (framePos >= fPool.numFrames) | |||
| return false; | |||
| if (framePos >= fNumFileFrames) | |||
| { | |||
| if (fLoopingMode) | |||
| framePos %= fNumFileFrames; | |||
| else | |||
| return false; | |||
| } | |||
| uint64_t frameDiff; | |||
| const uint64_t numFramesNearEnd = fPool.numFrames*3/4; | |||
| #if 1 | |||
| const CarlaMutexLocker cml(fMutex); | |||
| /* | |||
| #else | |||
| const CarlaMutexTryLocker cmtl(fMutex); | |||
| if (! cmtl.wasLocked()) | |||
| return false; | |||
| */ | |||
| { | |||
| for (int i=0; i<5; ++i) | |||
| { | |||
| pthread_yield(); | |||
| if (cmtl.tryAgain()) | |||
| break; | |||
| if (i == 4) | |||
| return false; | |||
| } | |||
| } | |||
| #endif | |||
| pool.startFrame = fPool.startFrame; | |||
| if (framePos < fPool.startFrame) | |||
| { | |||
| if (fPool.startFrame + fPool.numFrames <= fNumFileFrames) | |||
| { | |||
| setNeedsRead(framePos); | |||
| return false; | |||
| } | |||
| carla_copyFloats(pool.buffer[0] + framePos, fPool.buffer[0] + framePos, frames); | |||
| carla_copyFloats(pool.buffer[1] + framePos, fPool.buffer[1] + framePos, frames); | |||
| frameDiff = framePos + (fNumFileFrames - fPool.startFrame); | |||
| if (frameDiff + frames >= fPool.numFrames) | |||
| { | |||
| setNeedsRead(framePos); | |||
| return false; | |||
| } | |||
| carla_copyFloats(out1, fPool.buffer[0] + frameDiff, frames); | |||
| carla_copyFloats(out2, fPool.buffer[1] + frameDiff, frames); | |||
| } | |||
| else | |||
| { | |||
| frameDiff = framePos - fPool.startFrame; | |||
| if (frameDiff + frames >= fPool.numFrames) | |||
| { | |||
| setNeedsRead(framePos); | |||
| return false; | |||
| } | |||
| carla_copyFloats(out1, fPool.buffer[0] + frameDiff, frames); | |||
| carla_copyFloats(out2, fPool.buffer[1] + frameDiff, frames); | |||
| } | |||
| if (frameDiff > numFramesNearEnd) | |||
| setNeedsRead(framePos + frames); | |||
| return true; | |||
| } | |||
| @@ -372,17 +421,19 @@ public: | |||
| if (fNumFileFrames == 0 || fFileNfo.channels == 0 || fFilePtr == nullptr) | |||
| { | |||
| carla_debug("R: no song loaded"); | |||
| fNeedsFrame = 0; | |||
| fNeedsRead = false; | |||
| return; | |||
| } | |||
| if (fPollTempData == nullptr) | |||
| { | |||
| carla_debug("R: nothing to poll"); | |||
| fNeedsFrame = 0; | |||
| fNeedsRead = false; | |||
| return; | |||
| } | |||
| uint64_t lastFrame = kPlayer->getLastFrame(); | |||
| uint64_t lastFrame = fNeedsFrame; | |||
| int64_t readFrameCheck; | |||
| if (lastFrame >= fNumFileFrames) | |||
| @@ -398,6 +449,7 @@ public: | |||
| else | |||
| { | |||
| carla_debug("R: transport out of bounds"); | |||
| fNeedsFrame = 0; | |||
| fNeedsRead = false; | |||
| return; | |||
| } | |||
| @@ -428,6 +480,7 @@ public: | |||
| if (rv < 0) | |||
| { | |||
| carla_stderr("R: ad_read failed"); | |||
| fNeedsFrame = 0; | |||
| fNeedsRead = false; | |||
| return; | |||
| } | |||
| @@ -442,36 +495,46 @@ public: | |||
| rv += ad_read(fFilePtr, fPollTempData+urv, fPollTempSize-urv); | |||
| } | |||
| carla_debug("R: reading %li frames at frame %lu", rv, readFrameCheck); | |||
| // local copy | |||
| const uint32_t poolNumFrame = fPool.numFrames; | |||
| const int64_t fileFrames = fFileNfo.frames; | |||
| const bool isMonoFile = fFileNfo.channels == 1; | |||
| float* const pbuffer0 = fPool.buffer[0]; | |||
| float* const pbuffer1 = fPool.buffer[1]; | |||
| const float* const tmpbuf = fPollTempData; | |||
| // lock, and put data asap | |||
| const CarlaMutexLocker cml(fMutex); | |||
| do { | |||
| for (; i < fPool.numFrames && j < rv; ++j) | |||
| for (; i < poolNumFrame && j < rv; ++j) | |||
| { | |||
| if (fFileNfo.channels == 1) | |||
| if (isMonoFile) | |||
| { | |||
| fPool.buffer[0][i] = fPollTempData[j]; | |||
| fPool.buffer[1][i] = fPollTempData[j]; | |||
| pbuffer0[i] = pbuffer1[i] = tmpbuf[j]; | |||
| i++; | |||
| } | |||
| else | |||
| { | |||
| if (j % 2 == 0) | |||
| { | |||
| fPool.buffer[0][i] = fPollTempData[j]; | |||
| pbuffer0[i] = tmpbuf[j]; | |||
| } | |||
| else | |||
| { | |||
| fPool.buffer[1][i] = fPollTempData[j]; | |||
| i++; | |||
| pbuffer1[i] = tmpbuf[j]; | |||
| ++i; | |||
| } | |||
| } | |||
| } | |||
| if (i >= fPool.numFrames) | |||
| if (i >= poolNumFrame) { | |||
| break; | |||
| } | |||
| if (rv == fFileNfo.frames) | |||
| if (rv == fileFrames) | |||
| { | |||
| // full file read | |||
| j = 0; | |||
| @@ -481,14 +544,14 @@ public: | |||
| { | |||
| carla_debug("read break, not enough space"); | |||
| carla_zeroFloats(fPool.buffer[0] + i, fPool.numFrames - i); | |||
| carla_zeroFloats(fPool.buffer[1] + i, fPool.numFrames - i); | |||
| carla_zeroFloats(pbuffer0, poolNumFrame - i); | |||
| carla_zeroFloats(pbuffer1, poolNumFrame - i); | |||
| break; | |||
| } | |||
| } while (i < fPool.numFrames); | |||
| } while (i < poolNumFrame); | |||
| fPool.startFrame = lastFrame; | |||
| fPool.startFrame = readFrame; | |||
| } | |||
| fNeedsRead = false; | |||
| @@ -497,25 +560,22 @@ public: | |||
| protected: | |||
| void run() override | |||
| { | |||
| const uint64_t numFramesNearEnd = fPool.numFrames*3/4; | |||
| uint64_t lastFrame; | |||
| while (! fQuitNow) | |||
| { | |||
| lastFrame = kPlayer->getLastFrame(); | |||
| if (fNeedsRead || lastFrame < fPool.startFrame || lastFrame - fPool.startFrame >= numFramesNearEnd) | |||
| if (fNeedsRead) | |||
| readPoll(); | |||
| carla_msleep(50); | |||
| if (fQuitNow) | |||
| break; | |||
| fSignal.wait(); | |||
| } | |||
| } | |||
| private: | |||
| AbstractAudioPlayer* const kPlayer; | |||
| bool fEntireFileLoaded; | |||
| bool fLoopingMode; | |||
| volatile uint64_t fNeedsFrame; | |||
| volatile bool fNeedsRead; | |||
| volatile bool fQuitNow; | |||
| @@ -529,6 +589,7 @@ private: | |||
| AudioFilePool fPool; | |||
| CarlaMutex fMutex; | |||
| CarlaSignal fSignal; | |||
| CARLA_DECLARE_NON_COPY_STRUCT(AudioFileThread) | |||
| }; | |||
| @@ -41,19 +41,17 @@ static const char* const audiofilesWildcard = | |||
| // ----------------------------------------------------------------------- | |||
| class AudioFilePlugin : public NativePluginWithMidiPrograms<FileAudio>, | |||
| public AbstractAudioPlayer | |||
| class AudioFilePlugin : public NativePluginWithMidiPrograms<FileAudio> | |||
| { | |||
| public: | |||
| AudioFilePlugin(const NativeHostDescriptor* const host) | |||
| : NativePluginWithMidiPrograms<FileAudio>(host, fPrograms, 2), | |||
| AbstractAudioPlayer(), | |||
| fLoopMode(true), | |||
| fDoProcess(false), | |||
| fLastFrame(0), | |||
| fWasPlayingBefore(false), | |||
| fMaxFrame(0), | |||
| fPool(), | |||
| fThread(this), | |||
| fThread(), | |||
| fPrograms(hostGetFilePath("audio"), audiofilesWildcard) | |||
| #ifndef __MOD_DEVICES__ | |||
| , fInlineDisplay() | |||
| @@ -67,11 +65,6 @@ public: | |||
| fPool.destroy(); | |||
| } | |||
| uint64_t getLastFrame() const override | |||
| { | |||
| return fLastFrame; | |||
| } | |||
| protected: | |||
| // ------------------------------------------------------------------- | |||
| // Plugin parameter calls | |||
| @@ -126,7 +119,6 @@ protected: | |||
| fLoopMode = b; | |||
| fThread.setLoopingMode(b); | |||
| fThread.setNeedsRead(); | |||
| } | |||
| void setCustomData(const char* const key, const char* const value) override | |||
| @@ -151,8 +143,7 @@ protected: | |||
| if (! fDoProcess) | |||
| { | |||
| //carla_stderr("P: no process"); | |||
| fLastFrame = timePos->frame; | |||
| // carla_stderr("P: no process"); | |||
| carla_zeroFloats(out1, frames); | |||
| carla_zeroFloats(out2, frames); | |||
| return; | |||
| @@ -161,23 +152,26 @@ protected: | |||
| // not playing | |||
| if (! timePos->playing) | |||
| { | |||
| //carla_stderr("P: not playing"); | |||
| if (timePos->frame == 0 && fLastFrame > 0) | |||
| fThread.setNeedsRead(); | |||
| // carla_stderr("P: not playing"); | |||
| if (timePos->frame == 0 && fWasPlayingBefore) | |||
| fThread.setNeedsRead(timePos->frame); | |||
| fLastFrame = timePos->frame; | |||
| carla_zeroFloats(out1, frames); | |||
| carla_zeroFloats(out2, frames); | |||
| fWasPlayingBefore = false; | |||
| return; | |||
| } | |||
| else | |||
| { | |||
| fWasPlayingBefore = true; | |||
| } | |||
| // out of reach | |||
| if ((timePos->frame < fPool.startFrame || timePos->frame >= fMaxFrame) && !fLoopMode) | |||
| { | |||
| if (timePos->frame < fPool.startFrame) | |||
| fThread.setNeedsRead(); | |||
| fThread.setNeedsRead(timePos->frame); | |||
| fLastFrame = timePos->frame; | |||
| carla_zeroFloats(out1, frames); | |||
| carla_zeroFloats(out2, frames); | |||
| @@ -236,15 +230,7 @@ protected: | |||
| } | |||
| else | |||
| { | |||
| // NOTE: timePos->frame is always >= fPool.startFrame | |||
| const uint64_t poolStartFrame = timePos->frame - fThread.getPoolStartFrame(); | |||
| if (fThread.tryPutData(fPool, poolStartFrame, frames)) | |||
| { | |||
| carla_copyFloats(out1, fPool.buffer[0]+poolStartFrame, frames); | |||
| carla_copyFloats(out2, fPool.buffer[1]+poolStartFrame, frames); | |||
| } | |||
| else | |||
| if (! fThread.tryPutData(out1, out2, timePos->frame, frames)) | |||
| { | |||
| carla_zeroFloats(out1, frames); | |||
| carla_zeroFloats(out2, frames); | |||
| @@ -262,11 +248,10 @@ protected: | |||
| if (! fInlineDisplay.pending) | |||
| { | |||
| fInlineDisplay.pending = true; | |||
| // FIXME this is not supposed to be here, but in some idle callback | |||
| hostQueueDrawInlineDisplay(); | |||
| } | |||
| #endif | |||
| fLastFrame = timePos->frame; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| @@ -411,8 +396,8 @@ protected: | |||
| private: | |||
| bool fLoopMode; | |||
| bool fDoProcess; | |||
| bool fWasPlayingBefore; | |||
| volatile uint64_t fLastFrame; | |||
| uint32_t fMaxFrame; | |||
| AudioFilePool fPool; | |||
| @@ -185,35 +185,17 @@ private: | |||
| CARLA_SAFE_ASSERT_CONTINUE(midiEventHolder != nullptr); | |||
| const MidiMessage& midiMessage(midiEventHolder->message); | |||
| //const double time(track->getEventTime(i)*sampleRate); | |||
| const int dataSize(midiMessage.getRawDataSize()); | |||
| const int dataSize = midiMessage.getRawDataSize(); | |||
| if (dataSize <= 0 || dataSize > MAX_EVENT_DATA_SIZE) | |||
| continue; | |||
| if (midiMessage.isActiveSense()) | |||
| continue; | |||
| if (midiMessage.isMetaEvent()) | |||
| continue; | |||
| if (midiMessage.isMidiStart()) | |||
| continue; | |||
| if (midiMessage.isMidiContinue()) | |||
| continue; | |||
| if (midiMessage.isMidiStop()) | |||
| continue; | |||
| if (midiMessage.isMidiClock()) | |||
| continue; | |||
| if (midiMessage.isSongPositionPointer()) | |||
| continue; | |||
| if (midiMessage.isQuarterFrame()) | |||
| continue; | |||
| if (midiMessage.isFullFrame()) | |||
| continue; | |||
| if (midiMessage.isMidiMachineControlMessage()) | |||
| continue; | |||
| if (midiMessage.isSysEx()) | |||
| const uint8_t* const data = midiMessage.getRawData(); | |||
| if (! MIDI_IS_CHANNEL_MESSAGE(data[0])) | |||
| continue; | |||
| const double time(midiMessage.getTimeStamp()*sampleRate); | |||
| const double time = midiMessage.getTimeStamp() * sampleRate; | |||
| // const double time = track->getEventTime(i) * sampleRate; | |||
| CARLA_SAFE_ASSERT_CONTINUE(time >= 0.0); | |||
| fMidiOut.addRaw(static_cast<uint64_t>(time), midiMessage.getRawData(), static_cast<uint8_t>(dataSize)); | |||
| @@ -112,9 +112,11 @@ BinaryType getBinaryTypeFromFile(const char* const filename) | |||
| // We just assume what architectures are more important, and check for them first | |||
| if (std::strstr(output, "x86_64") != nullptr) | |||
| return BINARY_POSIX64; | |||
| if (std::strstr(output, "i386")) | |||
| if (std::strstr(output, "arm64") != nullptr) | |||
| return BINARY_POSIX64; | |||
| if (std::strstr(output, "i386") != nullptr) | |||
| return BINARY_POSIX32; | |||
| if (std::strstr(output, "ppc")) | |||
| if (std::strstr(output, "ppc") != nullptr) | |||
| return BINARY_OTHER; | |||
| } | |||
| @@ -20,6 +20,8 @@ | |||
| #include "CarlaMacUtils.hpp" | |||
| #include "CarlaString.hpp" | |||
| #include <sys/xattr.h> | |||
| #import <Foundation/Foundation.h> | |||
| CARLA_BACKEND_START_NAMESPACE | |||
| @@ -54,6 +56,11 @@ const char* findBinaryInBundle(const char* const bundleDir) | |||
| return ret.buffer(); | |||
| } | |||
| bool removeFileFromQuarantine(const char* const filename) | |||
| { | |||
| return removexattr(filename, "com.apple.quarantine", 0) == 0; | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| AutoNSAutoreleasePool::AutoNSAutoreleasePool() | |||
| @@ -33,6 +33,11 @@ CARLA_BACKEND_START_NAMESPACE | |||
| */ | |||
| const char* findBinaryInBundle(const char* const bundleDir); | |||
| /* | |||
| * ... | |||
| */ | |||
| bool removeFileFromQuarantine(const char* const filename); | |||
| /* | |||
| * ... | |||
| */ | |||
| @@ -325,6 +325,11 @@ public: | |||
| return !fLocked; | |||
| } | |||
| bool tryAgain() const noexcept | |||
| { | |||
| return fMutex.tryLock(); | |||
| } | |||
| private: | |||
| const Mutex& fMutex; | |||
| const bool fLocked; | |||