| @@ -1,5 +1,11 @@ | |||||
| # INSTALL for Carla | # 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: | To install Carla, simply run as usual: | ||||
| ``` | ``` | ||||
| $ make | $ make | ||||
| @@ -1,6 +1,6 @@ | |||||
| #!/bin/bash | #!/bin/bash | ||||
| if [ "$(uname -m)" = "arm64" ]; then | |||||
| if [ "$(uname -m)" = "arm64" ] || [ "$(uname -r)" = "20.1.0" ]; then | |||||
| MACOS_UNIVERSAL=1 | MACOS_UNIVERSAL=1 | ||||
| else | else | ||||
| MACOS_UNIVERSAL=0 | MACOS_UNIVERSAL=0 | ||||
| @@ -13,7 +13,7 @@ fi | |||||
| export CC=clang | export CC=clang | ||||
| export CXX=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 CFLAGS="${CFLAGS} -mtune=generic -msse -msse2" | ||||
| export LDFLAGS="-L${TARGETDIR}/carla64/lib -stdlib=libc++" | export LDFLAGS="-L${TARGETDIR}/carla64/lib -stdlib=libc++" | ||||
| unset CPPFLAGS | unset CPPFLAGS | ||||
| @@ -21,9 +21,10 @@ unset CPPFLAGS | |||||
| if [ "${MACOS_UNIVERSAL}" -eq 1 ]; then | if [ "${MACOS_UNIVERSAL}" -eq 1 ]; then | ||||
| export CFLAGS="${CFLAGS} -arch x86_64 -arch arm64" | export CFLAGS="${CFLAGS} -arch x86_64 -arch arm64" | ||||
| export LDFLAGS="${LDFLAGS} -arch x86_64 -arch arm64" | export LDFLAGS="${LDFLAGS} -arch x86_64 -arch arm64" | ||||
| export MACOS_UNIVERSAL="true" | |||||
| else | else | ||||
| export CFLAGS="${CFLAGS} -m${ARCH}" | |||||
| export LDFLAGS="${LDFLAGS} -m${ARCH}" | |||||
| export CFLAGS="${CFLAGS} -m64" | |||||
| export LDFLAGS="${LDFLAGS} -m64" | |||||
| fi | fi | ||||
| export CXXFLAGS="${CFLAGS} -stdlib=libc++ -Wno-unknown-pragmas -Wno-unused-private-field -Werror=auto-var-id" | 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 | HAVE_LIBMAGIC = true | ||||
| else | else | ||||
| # old libmagic versions don't have a pkg-config file, so we need to call the compiler to test it | # 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 | ||||
| endif | endif | ||||
| @@ -964,7 +964,8 @@ public: | |||||
| static CarlaPluginPtr newNative(const Initializer& init); | static CarlaPluginPtr newNative(const Initializer& init); | ||||
| static CarlaPluginPtr newBridge(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 newLADSPA(const Initializer& init, const LADSPA_RDF_Descriptor* rdfDescriptor); | ||||
| static CarlaPluginPtr newDSSI(const Initializer& init); | static CarlaPluginPtr newDSSI(const Initializer& init); | ||||
| @@ -44,6 +44,13 @@ | |||||
| #include "water/xml/XmlDocument.h" | #include "water/xml/XmlDocument.h" | ||||
| #include "water/xml/XmlElement.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> | #include <map> | ||||
| // FIXME Remove on 2.1 release | // FIXME Remove on 2.1 release | ||||
| @@ -560,12 +567,27 @@ bool CarlaEngine::addPlugin(const BinaryType btype, | |||||
| bridgeBinary.clear(); | 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 | // 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 (ptype == PLUGIN_LV2 && label != nullptr) | ||||
| { | { | ||||
| if (std::strncmp(label, "http://calf.sourceforge.net/plugins/", 36) == 0 || | if (std::strncmp(label, "http://calf.sourceforge.net/plugins/", 36) == 0 || | ||||
| @@ -575,19 +597,39 @@ bool CarlaEngine::addPlugin(const BinaryType btype, | |||||
| preferBridges = true; | 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 | #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()) | if (bridgeBinary.isNotEmpty()) | ||||
| { | { | ||||
| plugin = CarlaPlugin::newBridge(initializer, btype, ptype, bridgeBinary); | |||||
| plugin = CarlaPlugin::newBridge(initializer, btype, ptype, needsArchBridge, bridgeBinary); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -143,49 +143,56 @@ public: | |||||
| if (! fShmAudioPool.attachClient(fBaseNameAudioPool)) | 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; | return false; | ||||
| } | } | ||||
| if (! fShmRtClientControl.attachClient(fBaseNameRtClientControl)) | if (! fShmRtClientControl.attachClient(fBaseNameRtClientControl)) | ||||
| { | { | ||||
| pData->close(); | |||||
| clear(); | clear(); | ||||
| carla_stderr("Failed to attach to rt client control shared memory"); | |||||
| setLastError("Failed to attach to rt client control shared memory"); | |||||
| return false; | return false; | ||||
| } | } | ||||
| if (! fShmRtClientControl.mapData()) | if (! fShmRtClientControl.mapData()) | ||||
| { | { | ||||
| pData->close(); | |||||
| clear(); | clear(); | ||||
| carla_stderr("Failed to map rt client control shared memory"); | |||||
| setLastError("Failed to map rt client control shared memory"); | |||||
| return false; | return false; | ||||
| } | } | ||||
| if (! fShmNonRtClientControl.attachClient(fBaseNameNonRtClientControl)) | if (! fShmNonRtClientControl.attachClient(fBaseNameNonRtClientControl)) | ||||
| { | { | ||||
| pData->close(); | |||||
| clear(); | 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; | return false; | ||||
| } | } | ||||
| if (! fShmNonRtClientControl.mapData()) | if (! fShmNonRtClientControl.mapData()) | ||||
| { | { | ||||
| pData->close(); | |||||
| clear(); | clear(); | ||||
| carla_stderr("Failed to map non-rt control client shared memory"); | |||||
| setLastError("Failed to map non-rt control client shared memory"); | |||||
| return false; | return false; | ||||
| } | } | ||||
| if (! fShmNonRtServerControl.attachClient(fBaseNameNonRtServerControl)) | if (! fShmNonRtServerControl.attachClient(fBaseNameNonRtServerControl)) | ||||
| { | { | ||||
| pData->close(); | |||||
| clear(); | 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; | return false; | ||||
| } | } | ||||
| if (! fShmNonRtServerControl.mapData()) | if (! fShmNonRtServerControl.mapData()) | ||||
| { | { | ||||
| pData->close(); | |||||
| clear(); | clear(); | ||||
| carla_stderr("Failed to map non-rt control server shared memory"); | |||||
| setLastError("Failed to map non-rt control server shared memory"); | |||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -210,7 +217,9 @@ public: | |||||
| shmNonRtClientDataSize != sizeof(BridgeNonRtClientData) || | shmNonRtClientDataSize != sizeof(BridgeNonRtClientData) || | ||||
| shmNonRtServerDataSize != sizeof(BridgeNonRtServerData)) | shmNonRtServerDataSize != sizeof(BridgeNonRtServerData)) | ||||
| { | { | ||||
| carla_stderr2("CarlaEngineBridge: data size mismatch"); | |||||
| pData->close(); | |||||
| clear(); | |||||
| setLastError("Shared memory data size mismatch"); | |||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -222,7 +231,9 @@ public: | |||||
| if (pData->bufferSize == 0 || carla_isZero(pData->sampleRate)) | 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; | return false; | ||||
| } | } | ||||
| @@ -232,9 +232,11 @@ protected: | |||||
| carla_zeroFloats(audioIns[1], bufferSize); | carla_zeroFloats(audioIns[1], bufferSize); | ||||
| carla_zeroStructs(pData->events.in, kMaxEngineEventInternalCount); | carla_zeroStructs(pData->events.in, kMaxEngineEventInternalCount); | ||||
| int64_t oldTime, newTime; | |||||
| while (! shouldThreadExit()) | while (! shouldThreadExit()) | ||||
| { | { | ||||
| const int64_t oldTime = getTimeInMicroseconds(); | |||||
| oldTime = getTimeInMicroseconds(); | |||||
| const PendingRtEventsRunner prt(this, bufferSize, true); | const PendingRtEventsRunner prt(this, bufferSize, true); | ||||
| @@ -244,7 +246,7 @@ protected: | |||||
| pData->graph.process(pData, audioIns, audioOuts, bufferSize); | pData->graph.process(pData, audioIns, audioOuts, bufferSize); | ||||
| const int64_t newTime = getTimeInMicroseconds(); | |||||
| newTime = getTimeInMicroseconds(); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(newTime >= oldTime); | CARLA_SAFE_ASSERT_CONTINUE(newTime >= oldTime); | ||||
| const int64_t remainingTime = cycleTime - (newTime - oldTime); | const int64_t remainingTime = cycleTime - (newTime - oldTime); | ||||
| @@ -267,7 +269,7 @@ protected: | |||||
| std::free(audioOuts[0]); | std::free(audioOuts[0]); | ||||
| std::free(audioOuts[1]); | 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"), | : CarlaThread("CarlaPluginBridgeThread"), | ||||
| kEngine(engine), | kEngine(engine), | ||||
| kPlugin(plugin), | kPlugin(plugin), | ||||
| fBinary(), | |||||
| fBinaryArchName(), | |||||
| fBridgeBinary(), | |||||
| fLabel(), | fLabel(), | ||||
| fShmIds(), | fShmIds(), | ||||
| #ifndef CARLA_OS_WIN | #ifndef CARLA_OS_WIN | ||||
| @@ -103,18 +104,20 @@ public: | |||||
| #ifndef CARLA_OS_WIN | #ifndef CARLA_OS_WIN | ||||
| const char* const winePrefix, | const char* const winePrefix, | ||||
| #endif | #endif | ||||
| const char* const binary, | |||||
| const char* const binaryArchName, | |||||
| const char* const bridgeBinary, | |||||
| const char* const label, | const char* const label, | ||||
| const char* const shmIds) noexcept | 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_RETURN(shmIds != nullptr && shmIds[0] != '\0',); | ||||
| CARLA_SAFE_ASSERT(! isThreadRunning()); | CARLA_SAFE_ASSERT(! isThreadRunning()); | ||||
| #ifndef CARLA_OS_WIN | #ifndef CARLA_OS_WIN | ||||
| fWinePrefix = winePrefix; | fWinePrefix = winePrefix; | ||||
| #endif | #endif | ||||
| fBinary = binary; | |||||
| fBinaryArchName = binaryArchName; | |||||
| fBridgeBinary = bridgeBinary; | |||||
| fShmIds = shmIds; | fShmIds = shmIds; | ||||
| if (label != nullptr) | if (label != nullptr) | ||||
| @@ -160,7 +163,7 @@ protected: | |||||
| #ifndef CARLA_OS_WIN | #ifndef CARLA_OS_WIN | ||||
| // start with "wine" if needed | // start with "wine" if needed | ||||
| if (fBinary.endsWithIgnoreCase(".exe")) | |||||
| if (fBridgeBinary.endsWithIgnoreCase(".exe")) | |||||
| { | { | ||||
| String wineCMD; | String wineCMD; | ||||
| @@ -168,7 +171,7 @@ protected: | |||||
| { | { | ||||
| wineCMD = options.wine.executable; | wineCMD = options.wine.executable; | ||||
| if (fBinary.endsWithIgnoreCase("64.exe") | |||||
| if (fBridgeBinary.endsWithIgnoreCase("64.exe") | |||||
| && options.wine.executable[0] == CARLA_OS_SEP | && options.wine.executable[0] == CARLA_OS_SEP | ||||
| && File(wineCMD + "64").existsAsFile()) | && File(wineCMD + "64").existsAsFile()) | ||||
| wineCMD += "64"; | wineCMD += "64"; | ||||
| @@ -182,8 +185,18 @@ protected: | |||||
| } | } | ||||
| #endif | #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 | // plugin type | ||||
| arguments.add(getPluginTypeAsString(kPlugin->getType())); | arguments.add(getPluginTypeAsString(kPlugin->getType())); | ||||
| @@ -309,7 +322,7 @@ protected: | |||||
| #endif | #endif | ||||
| carla_stdout("Starting plugin bridge, command is:\n%s \"%s\" \"%s\" \"%s\" " P_INT64, | 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); | started = fProcess->start(arguments); | ||||
| } | } | ||||
| @@ -361,7 +374,8 @@ private: | |||||
| CarlaEngine* const kEngine; | CarlaEngine* const kEngine; | ||||
| CarlaPlugin* const kPlugin; | CarlaPlugin* const kPlugin; | ||||
| String fBinary; | |||||
| String fBinaryArchName; | |||||
| String fBridgeBinary; | |||||
| String fLabel; | String fLabel; | ||||
| String fShmIds; | String fShmIds; | ||||
| #ifndef CARLA_OS_WIN | #ifndef CARLA_OS_WIN | ||||
| @@ -2543,6 +2557,7 @@ public: | |||||
| const char* const label, | const char* const label, | ||||
| const int64_t uniqueId, | const int64_t uniqueId, | ||||
| const uint options, | const uint options, | ||||
| const char* const binaryArchName, | |||||
| const char* const bridgeBinary) | const char* const bridgeBinary) | ||||
| { | { | ||||
| CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr, false); | CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr, false); | ||||
| @@ -2652,7 +2667,7 @@ public: | |||||
| #ifndef CARLA_OS_WIN | #ifndef CARLA_OS_WIN | ||||
| fWinePrefix.toRawUTF8(), | fWinePrefix.toRawUTF8(), | ||||
| #endif | #endif | ||||
| bridgeBinary, label, shmIdsStr); | |||||
| binaryArchName, bridgeBinary, label, shmIdsStr); | |||||
| } | } | ||||
| if (! restartBridgeThread()) | if (! restartBridgeThread()) | ||||
| @@ -3136,11 +3151,14 @@ CARLA_BACKEND_END_NAMESPACE | |||||
| CARLA_BACKEND_START_NAMESPACE | CARLA_BACKEND_START_NAMESPACE | ||||
| CarlaPluginPtr CarlaPlugin::newBridge(const Initializer& init, | 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, | 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') | 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)); | 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 nullptr; | ||||
| return plugin; | return plugin; | ||||
| @@ -1378,7 +1378,28 @@ public: | |||||
| fDesc.name = label; | 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; | juce::OwnedArray<juce::PluginDescription> pluginDescriptions; | ||||
| @@ -1389,12 +1410,18 @@ public: | |||||
| for (int i = 0; i < fFormatManager.getNumFormats(); ++i) | 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 { | try { | ||||
| plist.scanAndAddFile(fileOrIdentifier, true, pluginDescriptions, *fFormatManager.getFormat(i)); | |||||
| plist.scanAndAddFile(fileOrIdentifier, true, pluginDescriptions, *apformat); | |||||
| } CARLA_SAFE_EXCEPTION_CONTINUE("scanAndAddFile") | } CARLA_SAFE_EXCEPTION_CONTINUE("scanAndAddFile") | ||||
| if (sac.wasTriggered()) | if (sac.wasTriggered()) | ||||
| { | { | ||||
| carla_stderr("WARNING: Caught exception while scanning file, will not load this plugin"); | |||||
| pluginDescriptions.clearQuick(false); | pluginDescriptions.clearQuick(false); | ||||
| break; | break; | ||||
| } | } | ||||
| @@ -1426,7 +1453,10 @@ public: | |||||
| } CARLA_SAFE_EXCEPTION("createPluginInstance") | } CARLA_SAFE_EXCEPTION("createPluginInstance") | ||||
| if (sac.wasTriggered()) | if (sac.wasTriggered()) | ||||
| { | |||||
| fInstance = nullptr; | fInstance = nullptr; | ||||
| carla_stderr("WARNING: Caught exception while instantiating, will not load this plugin"); | |||||
| } | |||||
| } | } | ||||
| if (fInstance == nullptr) | if (fInstance == nullptr) | ||||
| @@ -41,6 +41,14 @@ extern "C" { | |||||
| #include "water/files/File.h" | #include "water/files/File.h" | ||||
| #include "water/misc/Time.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 <string> | ||||
| #include <vector> | #include <vector> | ||||
| @@ -6249,7 +6257,8 @@ public: | |||||
| public: | public: | ||||
| bool init(const CarlaPluginPtr plugin, | 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); | CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr, false); | ||||
| @@ -6293,9 +6302,36 @@ public: | |||||
| return false; | 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 | // 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)) | if (! pData->libOpen(fRdfDescriptor->Binary)) | ||||
| { | { | ||||
| pData->engine->setLastError(pData->libError(fRdfDescriptor->Binary)); | pData->engine->setLastError(pData->libError(fRdfDescriptor->Binary)); | ||||
| @@ -6712,6 +6748,9 @@ public: | |||||
| initUi(); | initUi(); | ||||
| return true; | return true; | ||||
| // might be unused | |||||
| (void)needsArchBridge; | |||||
| } | } | ||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| @@ -6961,6 +7000,11 @@ public: | |||||
| // --------------------------------------------------------------- | // --------------------------------------------------------------- | ||||
| // open UI DLL | // 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)) | if (! pData->uiLibOpen(fUI.rdfDescriptor->Binary, canDelete)) | ||||
| { | { | ||||
| carla_stderr2("Could not load UI library, error was:\n%s", pData->libError(fUI.rdfDescriptor->Binary)); | 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)); | 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 | // used in CarlaStandalone.cpp | ||||
| @@ -17,6 +17,7 @@ | |||||
| #include "CarlaPluginInternal.hpp" | #include "CarlaPluginInternal.hpp" | ||||
| #include "CarlaEngine.hpp" | #include "CarlaEngine.hpp" | ||||
| #include "AppConfig.h" | |||||
| #if defined(USING_JUCE) && JUCE_PLUGINHOST_VST | #if defined(USING_JUCE) && JUCE_PLUGINHOST_VST | ||||
| # define USE_JUCE_FOR_VST2 | # define USE_JUCE_FOR_VST2 | ||||
| @@ -68,6 +68,7 @@ | |||||
| #include "jackbridge/JackBridge.hpp" | #include "jackbridge/JackBridge.hpp" | ||||
| #include "water/files/File.h" | #include "water/files/File.h" | ||||
| #include "water/misc/Time.h" | |||||
| using CarlaBackend::CarlaEngine; | using CarlaBackend::CarlaEngine; | ||||
| using CarlaBackend::EngineCallbackOpcode; | using CarlaBackend::EngineCallbackOpcode; | ||||
| @@ -238,7 +239,7 @@ public: | |||||
| { | { | ||||
| carla_debug("CarlaBridgePlugin::~CarlaBridgePlugin()"); | carla_debug("CarlaBridgePlugin::~CarlaBridgePlugin()"); | ||||
| if (! fUsingExec) | |||||
| if (fEngine != nullptr && ! fUsingExec) | |||||
| carla_engine_close(gHostHandle); | carla_engine_close(gHostHandle); | ||||
| } | } | ||||
| @@ -281,6 +282,15 @@ public: | |||||
| gIsInitiated = true; | 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)) | #if defined(USING_JUCE) && (defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)) | ||||
| # ifndef CARLA_OS_WIN | # ifndef CARLA_OS_WIN | ||||
| static const int argc = 0; | static const int argc = 0; | ||||
| @@ -298,6 +308,8 @@ public: | |||||
| # else | # else | ||||
| carla_msleep(5); | carla_msleep(5); | ||||
| # endif | # endif | ||||
| if (testing && timeToEnd - water::Time::currentTimeMillis() < 0) | |||||
| break; | |||||
| } | } | ||||
| #endif | #endif | ||||
| @@ -332,7 +344,7 @@ protected: | |||||
| } | } | ||||
| private: | private: | ||||
| const CarlaEngine* fEngine; | |||||
| CarlaEngine* fEngine; | |||||
| #ifdef USING_JUCE | #ifdef USING_JUCE | ||||
| const juce::ScopedJuceInitialiser_GUI fJuceInitialiser; | 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 (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 | #ifdef HAVE_X11 | ||||
| if (std::getenv("DISPLAY") != nullptr) | if (std::getenv("DISPLAY") != nullptr) | ||||
| @@ -25,6 +25,10 @@ | |||||
| #include "water/files/File.h" | #include "water/files/File.h" | ||||
| #ifdef CARLA_OS_MAC | |||||
| # include "CarlaMacUtils.hpp" | |||||
| #endif | |||||
| #include <string> | #include <string> | ||||
| #include <vector> | #include <vector> | ||||
| @@ -512,6 +516,11 @@ public: | |||||
| // ------------------------------------------------------------------------------------------------------------ | // ------------------------------------------------------------------------------------------------------------ | ||||
| // open DLL | // 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)) | if (! libOpen(fRdfUiDescriptor->Binary)) | ||||
| { | { | ||||
| carla_stderr("Failed to load UI binary, error was:\n%s", libError()); | carla_stderr("Failed to load UI binary, error was:\n%s", libError()); | ||||
| @@ -63,6 +63,7 @@ | |||||
| # undef Component | # undef Component | ||||
| # undef MemoryBlock | # undef MemoryBlock | ||||
| # undef Point | # undef Point | ||||
| # include "CarlaMacUtils.cpp" | |||||
| #endif | #endif | ||||
| #ifdef CARLA_OS_WIN | #ifdef CARLA_OS_WIN | ||||
| @@ -1753,6 +1754,21 @@ int main(int argc, char* argv[]) | |||||
| } | } | ||||
| #endif | #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) | switch (type) | ||||
| { | { | ||||
| case PLUGIN_LADSPA: | case PLUGIN_LADSPA: | ||||
| @@ -252,6 +252,12 @@ class RackListWidget(QListWidget): | |||||
| self.clearFocus() | self.clearFocus() | ||||
| def isDragUrlValid(self, filename): | def isDragUrlValid(self, filename): | ||||
| if not filename: | |||||
| return False | |||||
| if filename[-1] == '/': | |||||
| filename = filename[:-1] | |||||
| lfilename = filename.lower() | lfilename = filename.lower() | ||||
| if os.path.isdir(filename): | if os.path.isdir(filename): | ||||
| @@ -333,6 +339,12 @@ class RackListWidget(QListWidget): | |||||
| filename = url.toLocalFile() | filename = url.toLocalFile() | ||||
| if not filename: | |||||
| continue | |||||
| if filename[-1] == '/': | |||||
| filename = filename[:-1] | |||||
| if not self.host.load_file(filename): | if not self.host.load_file(filename): | ||||
| CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), | CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), | ||||
| self.tr("Failed to load file"), | self.tr("Failed to load file"), | ||||
| @@ -97,21 +97,14 @@ struct AudioFilePool { | |||||
| CARLA_DECLARE_NON_COPY_STRUCT(AudioFilePool) | CARLA_DECLARE_NON_COPY_STRUCT(AudioFilePool) | ||||
| }; | }; | ||||
| class AbstractAudioPlayer | |||||
| { | |||||
| public: | |||||
| virtual ~AbstractAudioPlayer() {} | |||||
| virtual uint64_t getLastFrame() const = 0; | |||||
| }; | |||||
| class AudioFileThread : public CarlaThread | class AudioFileThread : public CarlaThread | ||||
| { | { | ||||
| public: | public: | ||||
| AudioFileThread(AbstractAudioPlayer* const player) | |||||
| AudioFileThread() | |||||
| : CarlaThread("AudioFileThread"), | : CarlaThread("AudioFileThread"), | ||||
| kPlayer(player), | |||||
| fEntireFileLoaded(false), | fEntireFileLoaded(false), | ||||
| fLoopingMode(true), | fLoopingMode(true), | ||||
| fNeedsFrame(0), | |||||
| fNeedsRead(false), | fNeedsRead(false), | ||||
| fQuitNow(true), | fQuitNow(true), | ||||
| fFilePtr(nullptr), | fFilePtr(nullptr), | ||||
| @@ -120,10 +113,9 @@ public: | |||||
| fPollTempData(nullptr), | fPollTempData(nullptr), | ||||
| fPollTempSize(0), | fPollTempSize(0), | ||||
| fPool(), | fPool(), | ||||
| fMutex() | |||||
| fMutex(), | |||||
| fSignal() | |||||
| { | { | ||||
| CARLA_ASSERT(kPlayer != nullptr); | |||||
| static bool adInitiated = false; | static bool adInitiated = false; | ||||
| if (! adInitiated) | if (! adInitiated) | ||||
| @@ -168,16 +160,19 @@ public: | |||||
| if (fPollTempData == nullptr) | if (fPollTempData == nullptr) | ||||
| return; | return; | ||||
| fNeedsRead = true; | |||||
| fNeedsFrame = 0; | |||||
| fNeedsRead = false; | |||||
| fQuitNow = false; | fQuitNow = false; | ||||
| startThread(); | startThread(); | ||||
| } | } | ||||
| void stopNow() | void stopNow() | ||||
| { | { | ||||
| fNeedsFrame = 0; | |||||
| fNeedsRead = false; | fNeedsRead = false; | ||||
| fQuitNow = true; | fQuitNow = true; | ||||
| fSignal.signal(); | |||||
| stopThread(1000); | stopThread(1000); | ||||
| const CarlaMutexLocker cml(fMutex); | const CarlaMutexLocker cml(fMutex); | ||||
| @@ -209,9 +204,14 @@ public: | |||||
| fLoopingMode = on; | fLoopingMode = on; | ||||
| } | } | ||||
| void setNeedsRead() noexcept | |||||
| void setNeedsRead(const uint64_t frame) noexcept | |||||
| { | { | ||||
| if (fEntireFileLoaded) | |||||
| return; | |||||
| fNeedsFrame = frame; | |||||
| fNeedsRead = true; | fNeedsRead = true; | ||||
| fSignal.signal(); | |||||
| } | } | ||||
| bool loadFilename(const char* const filename, const uint32_t sampleRate) | 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); | 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); | const CarlaMutexLocker cml(fMutex); | ||||
| /* | |||||
| #else | |||||
| const CarlaMutexTryLocker cmtl(fMutex); | const CarlaMutexTryLocker cmtl(fMutex); | ||||
| if (! cmtl.wasLocked()) | 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; | return true; | ||||
| } | } | ||||
| @@ -372,17 +421,19 @@ public: | |||||
| if (fNumFileFrames == 0 || fFileNfo.channels == 0 || fFilePtr == nullptr) | if (fNumFileFrames == 0 || fFileNfo.channels == 0 || fFilePtr == nullptr) | ||||
| { | { | ||||
| carla_debug("R: no song loaded"); | carla_debug("R: no song loaded"); | ||||
| fNeedsFrame = 0; | |||||
| fNeedsRead = false; | fNeedsRead = false; | ||||
| return; | return; | ||||
| } | } | ||||
| if (fPollTempData == nullptr) | if (fPollTempData == nullptr) | ||||
| { | { | ||||
| carla_debug("R: nothing to poll"); | carla_debug("R: nothing to poll"); | ||||
| fNeedsFrame = 0; | |||||
| fNeedsRead = false; | fNeedsRead = false; | ||||
| return; | return; | ||||
| } | } | ||||
| uint64_t lastFrame = kPlayer->getLastFrame(); | |||||
| uint64_t lastFrame = fNeedsFrame; | |||||
| int64_t readFrameCheck; | int64_t readFrameCheck; | ||||
| if (lastFrame >= fNumFileFrames) | if (lastFrame >= fNumFileFrames) | ||||
| @@ -398,6 +449,7 @@ public: | |||||
| else | else | ||||
| { | { | ||||
| carla_debug("R: transport out of bounds"); | carla_debug("R: transport out of bounds"); | ||||
| fNeedsFrame = 0; | |||||
| fNeedsRead = false; | fNeedsRead = false; | ||||
| return; | return; | ||||
| } | } | ||||
| @@ -428,6 +480,7 @@ public: | |||||
| if (rv < 0) | if (rv < 0) | ||||
| { | { | ||||
| carla_stderr("R: ad_read failed"); | carla_stderr("R: ad_read failed"); | ||||
| fNeedsFrame = 0; | |||||
| fNeedsRead = false; | fNeedsRead = false; | ||||
| return; | return; | ||||
| } | } | ||||
| @@ -442,36 +495,46 @@ public: | |||||
| rv += ad_read(fFilePtr, fPollTempData+urv, fPollTempSize-urv); | 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 | // lock, and put data asap | ||||
| const CarlaMutexLocker cml(fMutex); | const CarlaMutexLocker cml(fMutex); | ||||
| do { | 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++; | i++; | ||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| if (j % 2 == 0) | if (j % 2 == 0) | ||||
| { | { | ||||
| fPool.buffer[0][i] = fPollTempData[j]; | |||||
| pbuffer0[i] = tmpbuf[j]; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| fPool.buffer[1][i] = fPollTempData[j]; | |||||
| i++; | |||||
| pbuffer1[i] = tmpbuf[j]; | |||||
| ++i; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| if (i >= fPool.numFrames) | |||||
| if (i >= poolNumFrame) { | |||||
| break; | break; | ||||
| } | |||||
| if (rv == fFileNfo.frames) | |||||
| if (rv == fileFrames) | |||||
| { | { | ||||
| // full file read | // full file read | ||||
| j = 0; | j = 0; | ||||
| @@ -481,14 +544,14 @@ public: | |||||
| { | { | ||||
| carla_debug("read break, not enough space"); | 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; | break; | ||||
| } | } | ||||
| } while (i < fPool.numFrames); | |||||
| } while (i < poolNumFrame); | |||||
| fPool.startFrame = lastFrame; | |||||
| fPool.startFrame = readFrame; | |||||
| } | } | ||||
| fNeedsRead = false; | fNeedsRead = false; | ||||
| @@ -497,25 +560,22 @@ public: | |||||
| protected: | protected: | ||||
| void run() override | void run() override | ||||
| { | { | ||||
| const uint64_t numFramesNearEnd = fPool.numFrames*3/4; | |||||
| uint64_t lastFrame; | |||||
| while (! fQuitNow) | while (! fQuitNow) | ||||
| { | { | ||||
| lastFrame = kPlayer->getLastFrame(); | |||||
| if (fNeedsRead || lastFrame < fPool.startFrame || lastFrame - fPool.startFrame >= numFramesNearEnd) | |||||
| if (fNeedsRead) | |||||
| readPoll(); | readPoll(); | ||||
| carla_msleep(50); | |||||
| if (fQuitNow) | |||||
| break; | |||||
| fSignal.wait(); | |||||
| } | } | ||||
| } | } | ||||
| private: | private: | ||||
| AbstractAudioPlayer* const kPlayer; | |||||
| bool fEntireFileLoaded; | bool fEntireFileLoaded; | ||||
| bool fLoopingMode; | bool fLoopingMode; | ||||
| volatile uint64_t fNeedsFrame; | |||||
| volatile bool fNeedsRead; | volatile bool fNeedsRead; | ||||
| volatile bool fQuitNow; | volatile bool fQuitNow; | ||||
| @@ -529,6 +589,7 @@ private: | |||||
| AudioFilePool fPool; | AudioFilePool fPool; | ||||
| CarlaMutex fMutex; | CarlaMutex fMutex; | ||||
| CarlaSignal fSignal; | |||||
| CARLA_DECLARE_NON_COPY_STRUCT(AudioFileThread) | 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: | public: | ||||
| AudioFilePlugin(const NativeHostDescriptor* const host) | AudioFilePlugin(const NativeHostDescriptor* const host) | ||||
| : NativePluginWithMidiPrograms<FileAudio>(host, fPrograms, 2), | : NativePluginWithMidiPrograms<FileAudio>(host, fPrograms, 2), | ||||
| AbstractAudioPlayer(), | |||||
| fLoopMode(true), | fLoopMode(true), | ||||
| fDoProcess(false), | fDoProcess(false), | ||||
| fLastFrame(0), | |||||
| fWasPlayingBefore(false), | |||||
| fMaxFrame(0), | fMaxFrame(0), | ||||
| fPool(), | fPool(), | ||||
| fThread(this), | |||||
| fThread(), | |||||
| fPrograms(hostGetFilePath("audio"), audiofilesWildcard) | fPrograms(hostGetFilePath("audio"), audiofilesWildcard) | ||||
| #ifndef __MOD_DEVICES__ | #ifndef __MOD_DEVICES__ | ||||
| , fInlineDisplay() | , fInlineDisplay() | ||||
| @@ -67,11 +65,6 @@ public: | |||||
| fPool.destroy(); | fPool.destroy(); | ||||
| } | } | ||||
| uint64_t getLastFrame() const override | |||||
| { | |||||
| return fLastFrame; | |||||
| } | |||||
| protected: | protected: | ||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| // Plugin parameter calls | // Plugin parameter calls | ||||
| @@ -126,7 +119,6 @@ protected: | |||||
| fLoopMode = b; | fLoopMode = b; | ||||
| fThread.setLoopingMode(b); | fThread.setLoopingMode(b); | ||||
| fThread.setNeedsRead(); | |||||
| } | } | ||||
| void setCustomData(const char* const key, const char* const value) override | void setCustomData(const char* const key, const char* const value) override | ||||
| @@ -151,8 +143,7 @@ protected: | |||||
| if (! fDoProcess) | if (! fDoProcess) | ||||
| { | { | ||||
| //carla_stderr("P: no process"); | |||||
| fLastFrame = timePos->frame; | |||||
| // carla_stderr("P: no process"); | |||||
| carla_zeroFloats(out1, frames); | carla_zeroFloats(out1, frames); | ||||
| carla_zeroFloats(out2, frames); | carla_zeroFloats(out2, frames); | ||||
| return; | return; | ||||
| @@ -161,23 +152,26 @@ protected: | |||||
| // not playing | // not playing | ||||
| if (! timePos->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(out1, frames); | ||||
| carla_zeroFloats(out2, frames); | carla_zeroFloats(out2, frames); | ||||
| fWasPlayingBefore = false; | |||||
| return; | return; | ||||
| } | } | ||||
| else | |||||
| { | |||||
| fWasPlayingBefore = true; | |||||
| } | |||||
| // out of reach | // out of reach | ||||
| if ((timePos->frame < fPool.startFrame || timePos->frame >= fMaxFrame) && !fLoopMode) | if ((timePos->frame < fPool.startFrame || timePos->frame >= fMaxFrame) && !fLoopMode) | ||||
| { | { | ||||
| if (timePos->frame < fPool.startFrame) | if (timePos->frame < fPool.startFrame) | ||||
| fThread.setNeedsRead(); | |||||
| fThread.setNeedsRead(timePos->frame); | |||||
| fLastFrame = timePos->frame; | |||||
| carla_zeroFloats(out1, frames); | carla_zeroFloats(out1, frames); | ||||
| carla_zeroFloats(out2, frames); | carla_zeroFloats(out2, frames); | ||||
| @@ -236,15 +230,7 @@ protected: | |||||
| } | } | ||||
| else | 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(out1, frames); | ||||
| carla_zeroFloats(out2, frames); | carla_zeroFloats(out2, frames); | ||||
| @@ -262,11 +248,10 @@ protected: | |||||
| if (! fInlineDisplay.pending) | if (! fInlineDisplay.pending) | ||||
| { | { | ||||
| fInlineDisplay.pending = true; | fInlineDisplay.pending = true; | ||||
| // FIXME this is not supposed to be here, but in some idle callback | |||||
| hostQueueDrawInlineDisplay(); | hostQueueDrawInlineDisplay(); | ||||
| } | } | ||||
| #endif | #endif | ||||
| fLastFrame = timePos->frame; | |||||
| } | } | ||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| @@ -411,8 +396,8 @@ protected: | |||||
| private: | private: | ||||
| bool fLoopMode; | bool fLoopMode; | ||||
| bool fDoProcess; | bool fDoProcess; | ||||
| bool fWasPlayingBefore; | |||||
| volatile uint64_t fLastFrame; | |||||
| uint32_t fMaxFrame; | uint32_t fMaxFrame; | ||||
| AudioFilePool fPool; | AudioFilePool fPool; | ||||
| @@ -185,35 +185,17 @@ private: | |||||
| CARLA_SAFE_ASSERT_CONTINUE(midiEventHolder != nullptr); | CARLA_SAFE_ASSERT_CONTINUE(midiEventHolder != nullptr); | ||||
| const MidiMessage& midiMessage(midiEventHolder->message); | 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) | if (dataSize <= 0 || dataSize > MAX_EVENT_DATA_SIZE) | ||||
| continue; | 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; | 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); | CARLA_SAFE_ASSERT_CONTINUE(time >= 0.0); | ||||
| fMidiOut.addRaw(static_cast<uint64_t>(time), midiMessage.getRawData(), static_cast<uint8_t>(dataSize)); | 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 | // We just assume what architectures are more important, and check for them first | ||||
| if (std::strstr(output, "x86_64") != nullptr) | if (std::strstr(output, "x86_64") != nullptr) | ||||
| return BINARY_POSIX64; | 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; | return BINARY_POSIX32; | ||||
| if (std::strstr(output, "ppc")) | |||||
| if (std::strstr(output, "ppc") != nullptr) | |||||
| return BINARY_OTHER; | return BINARY_OTHER; | ||||
| } | } | ||||
| @@ -20,6 +20,8 @@ | |||||
| #include "CarlaMacUtils.hpp" | #include "CarlaMacUtils.hpp" | ||||
| #include "CarlaString.hpp" | #include "CarlaString.hpp" | ||||
| #include <sys/xattr.h> | |||||
| #import <Foundation/Foundation.h> | #import <Foundation/Foundation.h> | ||||
| CARLA_BACKEND_START_NAMESPACE | CARLA_BACKEND_START_NAMESPACE | ||||
| @@ -54,6 +56,11 @@ const char* findBinaryInBundle(const char* const bundleDir) | |||||
| return ret.buffer(); | return ret.buffer(); | ||||
| } | } | ||||
| bool removeFileFromQuarantine(const char* const filename) | |||||
| { | |||||
| return removexattr(filename, "com.apple.quarantine", 0) == 0; | |||||
| } | |||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| AutoNSAutoreleasePool::AutoNSAutoreleasePool() | AutoNSAutoreleasePool::AutoNSAutoreleasePool() | ||||
| @@ -33,6 +33,11 @@ CARLA_BACKEND_START_NAMESPACE | |||||
| */ | */ | ||||
| const char* findBinaryInBundle(const char* const bundleDir); | const char* findBinaryInBundle(const char* const bundleDir); | ||||
| /* | |||||
| * ... | |||||
| */ | |||||
| bool removeFileFromQuarantine(const char* const filename); | |||||
| /* | /* | ||||
| * ... | * ... | ||||
| */ | */ | ||||
| @@ -325,6 +325,11 @@ public: | |||||
| return !fLocked; | return !fLocked; | ||||
| } | } | ||||
| bool tryAgain() const noexcept | |||||
| { | |||||
| return fMutex.tryLock(); | |||||
| } | |||||
| private: | private: | ||||
| const Mutex& fMutex; | const Mutex& fMutex; | ||||
| const bool fLocked; | const bool fLocked; | ||||