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