diff --git a/INSTALL.md b/INSTALL.md index 6cbe3a34b..83121eb19 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -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 diff --git a/data/macos/common.env b/data/macos/common.env index b81d0d679..e1176c929 100644 --- a/data/macos/common.env +++ b/data/macos/common.env @@ -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 diff --git a/data/macos/env.sh b/data/macos/env.sh index 1f5544fa4..7c4e83b80 100644 --- a/data/macos/env.sh +++ b/data/macos/env.sh @@ -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" diff --git a/source/Makefile.mk b/source/Makefile.mk index 78210e62d..de35d45e3 100644 --- a/source/Makefile.mk +++ b/source/Makefile.mk @@ -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 ' | $(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 ' | $(CC) $(CFLAGS_WITHOUT_ARCH) -x c -w -c - -o /dev/null 2>/dev/null && echo true) endif endif diff --git a/source/backend/CarlaPlugin.hpp b/source/backend/CarlaPlugin.hpp index 6991950ba..f934e7188 100644 --- a/source/backend/CarlaPlugin.hpp +++ b/source/backend/CarlaPlugin.hpp @@ -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); diff --git a/source/backend/engine/CarlaEngine.cpp b/source/backend/engine/CarlaEngine.cpp index 5413dcd50..5f697951f 100644 --- a/source/backend/engine/CarlaEngine.cpp +++ b/source/backend/engine/CarlaEngine.cpp @@ -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 // 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 { diff --git a/source/backend/engine/CarlaEngineBridge.cpp b/source/backend/engine/CarlaEngineBridge.cpp index 888f4f266..a58e98a2a 100644 --- a/source/backend/engine/CarlaEngineBridge.cpp +++ b/source/backend/engine/CarlaEngineBridge.cpp @@ -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; } diff --git a/source/backend/engine/CarlaEngineDummy.cpp b/source/backend/engine/CarlaEngineDummy.cpp index c6e17dde4..4477776ef 100644 --- a/source/backend/engine/CarlaEngineDummy.cpp +++ b/source/backend/engine/CarlaEngineDummy.cpp @@ -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); } // ------------------------------------------------------------------- diff --git a/source/backend/plugin/CarlaPluginBridge.cpp b/source/backend/plugin/CarlaPluginBridge.cpp index 1a9ca49f2..2501d65d2 100644 --- a/source/backend/plugin/CarlaPluginBridge.cpp +++ b/source/backend/plugin/CarlaPluginBridge.cpp @@ -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 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; diff --git a/source/backend/plugin/CarlaPluginJuce.cpp b/source/backend/plugin/CarlaPluginJuce.cpp index 8df120e85..f9b1b914a 100644 --- a/source/backend/plugin/CarlaPluginJuce.cpp +++ b/source/backend/plugin/CarlaPluginJuce.cpp @@ -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 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) diff --git a/source/backend/plugin/CarlaPluginLV2.cpp b/source/backend/plugin/CarlaPluginLV2.cpp index ce1f79909..17ce8725c 100644 --- a/source/backend/plugin/CarlaPluginLV2.cpp +++ b/source/backend/plugin/CarlaPluginLV2.cpp @@ -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 #include @@ -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 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 diff --git a/source/backend/plugin/CarlaPluginVST2.cpp b/source/backend/plugin/CarlaPluginVST2.cpp index 7ac2ea5f8..d854191cf 100644 --- a/source/backend/plugin/CarlaPluginVST2.cpp +++ b/source/backend/plugin/CarlaPluginVST2.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 diff --git a/source/bridges-plugin/CarlaBridgePlugin.cpp b/source/bridges-plugin/CarlaBridgePlugin.cpp index 8e428aa44..2a8af4b93 100644 --- a/source/bridges-plugin/CarlaBridgePlugin.cpp +++ b/source/bridges-plugin/CarlaBridgePlugin.cpp @@ -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) diff --git a/source/bridges-ui/CarlaBridgeFormatLV2.cpp b/source/bridges-ui/CarlaBridgeFormatLV2.cpp index 72d263758..6eb279065 100644 --- a/source/bridges-ui/CarlaBridgeFormatLV2.cpp +++ b/source/bridges-ui/CarlaBridgeFormatLV2.cpp @@ -25,6 +25,10 @@ #include "water/files/File.h" +#ifdef CARLA_OS_MAC +# include "CarlaMacUtils.hpp" +#endif + #include #include @@ -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()); diff --git a/source/discovery/carla-discovery.cpp b/source/discovery/carla-discovery.cpp index d45e6a12a..766d8955c 100644 --- a/source/discovery/carla-discovery.cpp +++ b/source/discovery/carla-discovery.cpp @@ -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: diff --git a/source/frontend/widgets/racklistwidget.py b/source/frontend/widgets/racklistwidget.py index 641137c0f..3208dd4ca 100644 --- a/source/frontend/widgets/racklistwidget.py +++ b/source/frontend/widgets/racklistwidget.py @@ -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"), diff --git a/source/native-plugins/audio-base.hpp b/source/native-plugins/audio-base.hpp index 8dd76d229..535de52f8 100644 --- a/source/native-plugins/audio-base.hpp +++ b/source/native-plugins/audio-base.hpp @@ -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) }; diff --git a/source/native-plugins/audio-file.cpp b/source/native-plugins/audio-file.cpp index 8c687e925..f1a68d2d6 100644 --- a/source/native-plugins/audio-file.cpp +++ b/source/native-plugins/audio-file.cpp @@ -41,19 +41,17 @@ static const char* const audiofilesWildcard = // ----------------------------------------------------------------------- -class AudioFilePlugin : public NativePluginWithMidiPrograms, - public AbstractAudioPlayer +class AudioFilePlugin : public NativePluginWithMidiPrograms { public: AudioFilePlugin(const NativeHostDescriptor* const host) : NativePluginWithMidiPrograms(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; diff --git a/source/native-plugins/midi-file.cpp b/source/native-plugins/midi-file.cpp index b501da931..f4e84fb42 100644 --- a/source/native-plugins/midi-file.cpp +++ b/source/native-plugins/midi-file.cpp @@ -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(time), midiMessage.getRawData(), static_cast(dataSize)); diff --git a/source/utils/CarlaBinaryUtils.hpp b/source/utils/CarlaBinaryUtils.hpp index a29a53e59..1ce1131eb 100644 --- a/source/utils/CarlaBinaryUtils.hpp +++ b/source/utils/CarlaBinaryUtils.hpp @@ -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; } diff --git a/source/utils/CarlaMacUtils.cpp b/source/utils/CarlaMacUtils.cpp index abc79a35b..d5e2edc45 100644 --- a/source/utils/CarlaMacUtils.cpp +++ b/source/utils/CarlaMacUtils.cpp @@ -20,6 +20,8 @@ #include "CarlaMacUtils.hpp" #include "CarlaString.hpp" +#include + #import 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() diff --git a/source/utils/CarlaMacUtils.hpp b/source/utils/CarlaMacUtils.hpp index bb455879b..9753da824 100644 --- a/source/utils/CarlaMacUtils.hpp +++ b/source/utils/CarlaMacUtils.hpp @@ -33,6 +33,11 @@ CARLA_BACKEND_START_NAMESPACE */ const char* findBinaryInBundle(const char* const bundleDir); +/* + * ... + */ +bool removeFileFromQuarantine(const char* const filename); + /* * ... */ diff --git a/source/utils/CarlaMutex.hpp b/source/utils/CarlaMutex.hpp index ff91e4827..f2f4215ef 100644 --- a/source/utils/CarlaMutex.hpp +++ b/source/utils/CarlaMutex.hpp @@ -325,6 +325,11 @@ public: return !fLocked; } + bool tryAgain() const noexcept + { + return fMutex.tryLock(); + } + private: const Mutex& fMutex; const bool fLocked;