@@ -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; | |||